whenever 0.9.2 → 0.10.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +17 -7
  4. data/CHANGELOG.md +63 -0
  5. data/Gemfile +0 -5
  6. data/LICENSE +1 -1
  7. data/README.md +101 -18
  8. data/Rakefile +3 -10
  9. data/bin/whenever +3 -0
  10. data/bin/wheneverize +8 -5
  11. data/gemfiles/activesupport4.1.gemfile +5 -0
  12. data/gemfiles/activesupport4.2.gemfile +5 -0
  13. data/lib/whenever/capistrano/v2/recipes.rb +4 -3
  14. data/lib/whenever/capistrano/v3/tasks/whenever.rake +20 -7
  15. data/lib/whenever/capistrano.rb +1 -1
  16. data/lib/whenever/command_line.rb +49 -28
  17. data/lib/whenever/cron.rb +43 -21
  18. data/lib/whenever/job.rb +4 -3
  19. data/lib/whenever/job_list.rb +54 -24
  20. data/lib/whenever/numeric.rb +13 -0
  21. data/lib/whenever/numeric_seconds.rb +48 -0
  22. data/lib/whenever/os.rb +7 -0
  23. data/lib/whenever/setup.rb +5 -1
  24. data/lib/whenever/version.rb +1 -1
  25. data/lib/whenever.rb +15 -14
  26. data/test/functional/command_line_test.rb +379 -255
  27. data/test/functional/output_at_test.rb +227 -249
  28. data/test/functional/output_default_defined_jobs_test.rb +239 -288
  29. data/test/functional/output_defined_job_test.rb +65 -91
  30. data/test/functional/output_env_test.rb +22 -26
  31. data/test/functional/output_jobs_for_roles_test.rb +46 -66
  32. data/test/functional/output_jobs_with_mailto_test.rb +168 -0
  33. data/test/functional/output_redirection_test.rb +231 -309
  34. data/test/test_case.rb +32 -0
  35. data/test/test_helper.rb +44 -15
  36. data/test/unit/capistrano_support_test.rb +126 -148
  37. data/test/unit/cron_test.rb +327 -210
  38. data/test/unit/executable_test.rb +142 -0
  39. data/test/unit/job_test.rb +111 -121
  40. data/whenever.gemspec +5 -4
  41. metadata +37 -28
  42. data/lib/whenever/tasks/whenever.rake +0 -43
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9b7b1f246b22c910d7f50418153572714548f3a7
4
- data.tar.gz: cc253c6228c7e36d7f07032d5272e442d2eb6b08
3
+ metadata.gz: 9c8fe56b21ffa57026a2b944ae208bea62a9d7e9
4
+ data.tar.gz: 543d704698ab03d944679daa1e85bee1b3936286
5
5
  SHA512:
6
- metadata.gz: bccd024f09f270d95b331f2c7177dad4914865eaacb5f83392f12a30fd1272cdfb3179dfaa731d84d63796a1f12a00d38b612b220fd46373a06ed7d0eeb0c90b
7
- data.tar.gz: 8cfcfb3ae75cf378878631528a397618f369743639e34e51a63a53b6ee0c59c919969cbb7152e59775a46478fb0b3232ec228a69c1b968668cb77a360a734106
6
+ metadata.gz: 3d0bd440a4beab5ecde93ccb91fad7cf5b2bf2ffe14a376ad24392fe5b4a3d53ba1d7ce15a53d2debc3b639a35f609436d21ab78de16b99a857fe1e16ed8bed1
7
+ data.tar.gz: 4bb8692d2f7130e45d80fc05a6a54609fd160155014eae330da8f0fc9ed97e9338d523282cd4f8ec197531231493bd04d2409a3d7a6bbc57761ff6ad0ff7f422
data/.gitignore CHANGED
@@ -3,3 +3,4 @@ pkg
3
3
  doc
4
4
  .*.sw[a-z]
5
5
  Gemfile.lock
6
+ gemfiles/*.lock
data/.travis.yml CHANGED
@@ -1,11 +1,21 @@
1
1
  language: ruby
2
+
3
+ before_install:
4
+ - gem install bundler
5
+
2
6
  rvm:
3
- - 1.8.7
4
- - 1.9.2
5
7
  - 1.9.3
6
8
  - 2.0.0
7
- - 2.1.0
8
- - ree
9
- - jruby
10
- - jruby-18mode
11
- - jruby-19mode
9
+ - 2.1.10
10
+ - 2.2.6
11
+ - 2.3.3
12
+ - 2.4.0
13
+ - jruby-9.1.9.0
14
+
15
+ gemfile:
16
+ - Gemfile
17
+ - gemfiles/activesupport4.1.gemfile
18
+ - gemfiles/activesupport4.2.gemfile
19
+ env:
20
+ global:
21
+ - JRUBY_OPTS=--debug
data/CHANGELOG.md CHANGED
@@ -1,3 +1,66 @@
1
+ ### develop
2
+
3
+ ### 0.10.0 / November 19, 2017
4
+
5
+ * Modify wheneverize to allow for the creating of 'config' directory when not present
6
+
7
+ * Add --crontab-command to whenever binary for overriding the crontab command. [Martin Grandrath]
8
+
9
+ * Allow setting the path within which Capistrano will execute whenever. [Samuel Johnson](https://github.com/javan/whenever/pull/619)
10
+
11
+ * Allow the use of string literals for month and day-of-week in raw cron syntax.. [Potamianos Gregory](https://github.com/javan/whenever/pull/711)
12
+
13
+ * Include Capistrano default environment variables when executing Whenever. [Karl Li](https://github.com/javan/whenever/pull/719)
14
+
15
+ * Allow configuring an alternative schedule file in Capistrano. [Shinichi Okamoto](https://github.com/javan/whenever/pull/666)
16
+
17
+ ### 0.9.7 / June 14, 2016
18
+
19
+ * Restore compatibility with Capistrano v3; it has a bug which we have to work around [Ben Langfeld, Chris Gunther, Shohei Yamasaki]
20
+
21
+ ### 0.9.6 / June 13, 2016
22
+
23
+ * Bypass symlinks when loading Capistrano v3 code, since these symlinks don't work in recent gem releases [Justin Ramos]
24
+
25
+ ### 0.9.5 / June 12, 2016
26
+
27
+ * Improve documentation [Ben Langfeld, Spencer Fry]
28
+
29
+ * Properly support Solaris / SmartOS [Steven Williamson]
30
+
31
+ * Drop support for Ruby < 1.9.3. Test newer Ruby versions. [Javan Makhmali, Bartłomiej Kozal]
32
+
33
+ * Suport Ruby 2.3.0 and Rails 4 [Vincent Boisard]
34
+
35
+ * Set `RAILS_ENV` correctly in schedule when writing crontab from Capistrano [Ben Langfeld, Lorenzo Manacorda]
36
+
37
+ * Minor refactoring, avoidance of Ruby warnings, etc [Ben Langfeld, DV Dasari]
38
+
39
+ * Correctly pass through date expressions (e.g. `1.day`) inside job definitions [Rafael Sales]
40
+
41
+ * Prevent writing invalid cron strings [Danny Fallon, Ben Langfeld]
42
+
43
+ * Execute runner with `bundle exec` to ensure presence of app dependencies [Judith Roth]
44
+
45
+
46
+ ### 0.9.4 / October 24, 2014
47
+
48
+ * Fix duplicated command line arguments when deploying to multiple servers with Cap 3. [betesh]
49
+
50
+ * Set `whenever_environment` to the current stage before defaulting to production in Cap 3 tasks. [Karthik T]
51
+
52
+
53
+ ### 0.9.3 / October 5, 2014
54
+
55
+ * Drop ActiveSupport dependency [James Healy, Javan Makhmali]
56
+
57
+ * Drop shoulda for tests
58
+
59
+ * Fix `whenever:clear_crontab` Cap 3 task [Javan Makhmali]
60
+
61
+ * Avoid using tempfiles [ahoward]
62
+
63
+
1
64
  ### 0.9.2 / March 4, 2014
2
65
 
3
66
  * Fix issues generating arguments for `execute` in Capistrano 3 tasks. [Javan Makhmali]
data/Gemfile CHANGED
@@ -2,8 +2,3 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in whenever.gemspec
4
4
  gemspec
5
-
6
- if RUBY_VERSION < "1.9.3"
7
- gem "activesupport", "< 4.0.0"
8
- gem "shoulda-matchers", "<= 2.0.0"
9
- end
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 Javan Makhmali
1
+ Copyright (c) 2017 Javan Makhmali
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person
4
4
  obtaining a copy of this software and associated documentation
data/README.md CHANGED
@@ -19,12 +19,36 @@ $ cd /apps/my-great-project
19
19
  $ wheneverize .
20
20
  ```
21
21
 
22
- This will create an initial `config/schedule.rb` file for you.
22
+ This will create an initial `config/schedule.rb` file for you (as long as the config folder is already present in your project).
23
+
24
+ ### The `whenever` command
25
+
26
+ ```sh
27
+ $ cd /apps/my-great-project
28
+ $ whenever
29
+ ```
30
+
31
+ This will simply show you your `schedule.rb` file converted to cron syntax. It does not read or write your crontab file; you'll need to do this in order for your jobs to execute:
32
+
33
+ ```sh
34
+ $ whenever --update-crontab
35
+ ```
36
+
37
+ Other commonly used options include:
38
+ ```sh
39
+ $ whenever --user app # set a user as which to install the crontab
40
+ $ whenever --load-file config/my_schedule.rb # set the schedule file
41
+ $ whenever --crontab-command 'sudo crontab` # override the crontab command
42
+ ```
43
+
44
+ You can list installed cron jobs using `crontab -l`.
45
+
46
+ Run `whenever --help` for a complete list of options for selecting the schedule to use, setting variables in the schedule, etc.
23
47
 
24
48
  ### Example schedule.rb file
25
49
 
26
50
  ```ruby
27
- every 3.hours do
51
+ every 3.hours do # 1.minute 1.day 1.week 1.month 1.year is also supported
28
52
  runner "MyModel.some_process"
29
53
  rake "my:rake:task"
30
54
  command "/usr/bin/my_great_command"
@@ -74,7 +98,7 @@ The default job types that ship with Whenever are defined like so:
74
98
  ```ruby
75
99
  job_type :command, ":task :output"
76
100
  job_type :rake, "cd :path && :environment_variable=:environment bundle exec rake :task --silent :output"
77
- job_type :runner, "cd :path && script/rails runner -e :environment ':task' :output"
101
+ job_type :runner, "cd :path && bin/rails runner -e :environment ':task' :output"
78
102
  job_type :script, "cd :path && :environment_variable=:environment bundle exec script/:task :output"
79
103
  ```
80
104
 
@@ -96,6 +120,57 @@ Or set the job_template to nil to have your jobs execute normally.
96
120
  set :job_template, nil
97
121
  ```
98
122
 
123
+ ### Parsing dates and times
124
+
125
+ Whenever uses the [Chronic](https://github.com/mojombo/chronic) gem to parse the specified dates and times.
126
+
127
+ You can set your custom Chronic configuration if the defaults don't fit you.
128
+
129
+ For example, to assume a 24 hour clock instead of the default 12 hour clock:
130
+
131
+ ```ruby
132
+ set :chronic_options, :hours24 => true
133
+
134
+ # By default this would run the job every day at 3am
135
+ every 1.day, :at => '3:00' do
136
+ runner "MyModel.nightly_archive_job"
137
+ end
138
+ ```
139
+
140
+ You can see a list of all available options here: <https://github.com/mojombo/chronic/blob/master/lib/chronic/parser.rb>
141
+
142
+ ### Customize email recipient with the `MAILTO` environment variable
143
+
144
+ Output from the jobs is sent to the email address configured in the `MAILTO` environment variable.
145
+
146
+ There are many ways to further configure the recipient.
147
+
148
+ Example: A global configuration, overriding the environment's value:
149
+
150
+ ```ruby
151
+ env 'MAILTO', 'output_of_cron@example.com'
152
+
153
+ every 3.hours do
154
+ command "/usr/bin/my_great_command"
155
+ end
156
+ ```
157
+
158
+ Example: A `MAILTO` configured for all the jobs in an interval block:
159
+
160
+ ```ruby
161
+ every 3.hours, mailto: 'my_super_command@example.com' do
162
+ command "/usr/bin/my_super_command"
163
+ end
164
+ ```
165
+
166
+ Example: A `MAILTO` configured for a single job:
167
+
168
+ ```ruby
169
+ every 3.hours do
170
+ command "/usr/bin/my_super_command", mailto: 'my_super_command_output@example.com'
171
+ end
172
+ ```
173
+
99
174
  ### Capistrano integration
100
175
 
101
176
  Use the built-in Capistrano recipe for easy crontab updates with deploys. For Capistrano V3, see the next section.
@@ -123,7 +198,7 @@ require "whenever/capistrano"
123
198
 
124
199
  The capistrano variable `:stage` should be the one holding your environment name. This will make the correct `:environment` available in your `schedule.rb`.
125
200
 
126
- If both your environments are on the same server you'll want to namespace them or they'll overwrite each other when you deploy:
201
+ If both your environments are on the same server you'll want to namespace them, or they'll overwrite each other when you deploy:
127
202
 
128
203
  ```ruby
129
204
  set :whenever_environment, defer { stage }
@@ -131,6 +206,13 @@ set :whenever_identifier, defer { "#{application}_#{stage}" }
131
206
  require "whenever/capistrano"
132
207
  ```
133
208
 
209
+ If you use a schedule at an alternative path, you may configure it like so:
210
+
211
+ ```ruby
212
+ set :whenever_load_file, defer { "#{release_path}/somewhere/else/schedule.rb" }
213
+ require "whenever/capistrano"
214
+ ```
215
+
134
216
  ### Capistrano V3 Integration
135
217
 
136
218
  In your "Capfile" file:
@@ -139,12 +221,14 @@ In your "Capfile" file:
139
221
  require "whenever/capistrano"
140
222
  ```
141
223
 
142
- Take a look at the load:defaults (bottom of file) task for options you can set. <http://github.com/javan/whenever/blob/master/lib/whenever/tasks/whenever.rake>. For example, to namespace the crontab entries by application and stage do this.
224
+ Take a look at the [load:defaults task](https://github.com/javan/whenever/blob/master/lib/whenever/capistrano/v3/tasks/whenever.rake) (bottom of file) for options you can set. For example, to namespace the crontab entries by application and stage do this in your "config/deploy.rb" file:
143
225
 
144
226
  ```ruby
145
227
  set :whenever_identifier, ->{ "#{fetch(:application)}_#{fetch(:stage)}" }
146
228
  ```
147
229
 
230
+ The Capistrano integration by default expects the `:application` variable to be set in order to scope jobs in the crontab.
231
+
148
232
  ### Capistrano roles
149
233
 
150
234
  The first thing to know about the new roles support is that it is entirely
@@ -153,10 +237,10 @@ different servers in your capistrano deployment, then you can safely stop readin
153
237
  now and everything should just work the same way it always has.
154
238
 
155
239
  When you define a job in your schedule.rb file, by default it will be deployed to
156
- all servers in the whenever_roles list (which defaults to [:db]).
240
+ all servers in the whenever_roles list (which defaults to `[:db]`).
157
241
 
158
242
  However, if you want to restrict certain jobs to only run on subset of servers,
159
- you can add a :roles => [...] argument to their definitions. **Make sure to add
243
+ you can add a `:roles => [...]` argument to their definitions. **Make sure to add
160
244
  that role to the whenever_roles list in your deploy.rb.**
161
245
 
162
246
  When you run `cap deploy`, jobs with a :roles list specified will only be added to
@@ -165,8 +249,8 @@ the crontabs on servers with one or more of the roles in that list.
165
249
  Jobs with no :roles argument will be deployed to all servers in the whenever_roles
166
250
  list. This is to maintain backward compatibility with previous releases of whenever.
167
251
 
168
- So, for example, with the default whenever_roles of [:db], a job like this would be
169
- deployed to all servers with the :db role:
252
+ So, for example, with the default whenever_roles of `[:db]`, a job like this would be
253
+ deployed to all servers with the `:db` role:
170
254
 
171
255
  ```ruby
172
256
  every :day, :at => '12:20am' do
@@ -174,7 +258,7 @@ every :day, :at => '12:20am' do
174
258
  end
175
259
  ```
176
260
 
177
- If we set whenever_roles to [:db, :app] in deploy.rb, and have the following
261
+ If we set whenever_roles to `[:db, :app]` in deploy.rb, and have the following
178
262
  jobs in schedule.rb:
179
263
 
180
264
  ```ruby
@@ -209,14 +293,13 @@ If your production environment uses RVM (Ruby Version Manager) you will run into
209
293
 
210
294
  This tells rvm to trust all rvmrc files.
211
295
 
212
- ### The `whenever` command
296
+ ### Heroku?
213
297
 
214
- ```sh
215
- $ cd /apps/my-great-project
216
- $ whenever
217
- ```
298
+ No. Heroku does not support cron, instead providing [Heroku Scheduler](https://devcenter.heroku.com/articles/scheduler). If you deploy to Heroku, you should use that rather than Whenever.
299
+
300
+ ### Testing
218
301
 
219
- This will simply show you your `schedule.rb` file converted to cron syntax. It does not read or write your crontab file. Run `whenever --help` for a complete list of options.
302
+ [whenever-test](https://github.com/heartbits/whenever-test) is an extension to Whenever for testing a Whenever schedule.
220
303
 
221
304
  ### Credit
222
305
 
@@ -235,8 +318,8 @@ It's a little bit dated now, but remains a good introduction.
235
318
 
236
319
  ----
237
320
 
238
- Compatible with Ruby 1.8.7-2.1.0, JRuby, and Rubinius. [![Build Status](https://secure.travis-ci.org/javan/whenever.png)](http://travis-ci.org/javan/whenever)
321
+ [![Build Status](https://secure.travis-ci.org/javan/whenever.png)](http://travis-ci.org/javan/whenever)
239
322
 
240
323
  ----
241
324
 
242
- Copyright &copy; 2014 Javan Makhmali
325
+ Copyright &copy; 2017 Javan Makhmali
data/Rakefile CHANGED
@@ -1,17 +1,10 @@
1
- begin
2
- require 'bundler'
3
- rescue LoadError => e
4
- warn("warning: Could not load bundler: #{e}")
5
- warn(" Some rake tasks will not be defined")
6
- else
7
- Bundler::GemHelper.install_tasks
8
- end
9
-
1
+ require 'bundler/gem_tasks'
10
2
  require 'rake/testtask'
3
+
11
4
  Rake::TestTask.new(:test) do |test|
12
5
  test.libs << 'lib' << 'test'
13
6
  test.pattern = 'test/{functional,unit}/**/*_test.rb'
14
7
  test.verbose = true
15
8
  end
16
9
 
17
- task :default => :test
10
+ task :default => :test
data/bin/whenever CHANGED
@@ -35,6 +35,9 @@ OptionParser.new do |opts|
35
35
  opts.on('-r', '--roles [role1,role2]', 'Comma-separated list of server roles to generate cron jobs for') do |roles|
36
36
  options[:roles] = roles.split(',').map(&:to_sym) if roles
37
37
  end
38
+ opts.on('-x', '--crontab-command [command]', 'Default: crontab') do |crontab_command|
39
+ options[:crontab_command] = crontab_command if crontab_command
40
+ end
38
41
  opts.on('-v', '--version') { puts "Whenever v#{Whenever::VERSION}"; exit(0) }
39
42
  end.parse!
40
43
 
data/bin/wheneverize CHANGED
@@ -18,7 +18,7 @@ OptionParser.new do |opts|
18
18
  end
19
19
 
20
20
  unless ARGV.empty?
21
- if !File.exists?(ARGV.first)
21
+ if !File.exist?(ARGV.first)
22
22
  abort "`#{ARGV.first}' does not exist."
23
23
  elsif !File.directory?(ARGV.first)
24
24
  abort "`#{ARGV.first}' is not a directory."
@@ -54,13 +54,16 @@ file = 'config/schedule.rb'
54
54
  base = ARGV.empty? ? '.' : ARGV.shift
55
55
 
56
56
  file = File.join(base, file)
57
- if File.exists?(file)
57
+ if File.exist?(file)
58
58
  warn "[skip] `#{file}' already exists"
59
- elsif File.exists?(file.downcase)
59
+ elsif File.exist?(file.downcase)
60
60
  warn "[skip] `#{file.downcase}' exists, which could conflict with `#{file}'"
61
- elsif !File.exists?(File.dirname(file))
62
- warn "[skip] directory `#{File.dirname(file)}' does not exist"
63
61
  else
62
+ dir = File.dirname(file)
63
+ if !File.exist?(dir)
64
+ warn "[add] creating `#{dir}'"
65
+ FileUtils.mkdir_p(dir)
66
+ end
64
67
  puts "[add] writing `#{file}'"
65
68
  File.open(file, "w") { |f| f.write(content) }
66
69
  end
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "activesupport", "~> 4.1.0"
4
+
5
+ gemspec path: "../"
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "activesupport", "~> 4.2.0"
4
+
5
+ gemspec path: "../"
@@ -7,10 +7,11 @@ Capistrano::Configuration.instance(:must_exist).load do
7
7
  _cset(:whenever_options) { {:roles => fetch(:whenever_roles)} }
8
8
  _cset(:whenever_command) { "whenever" }
9
9
  _cset(:whenever_identifier) { fetch :application }
10
- _cset(:whenever_environment) { fetch :rails_env, "production" }
10
+ _cset(:whenever_environment) { fetch :rails_env, fetch(:stage, "production") }
11
11
  _cset(:whenever_variables) { "environment=#{fetch :whenever_environment}" }
12
12
  _cset(:whenever_update_flags) { "--update-crontab #{fetch :whenever_identifier} --set #{fetch :whenever_variables}" }
13
13
  _cset(:whenever_clear_flags) { "--clear-crontab #{fetch :whenever_identifier}" }
14
+ _cset(:whenever_path) { fetch :latest_release }
14
15
 
15
16
  namespace :whenever do
16
17
  desc "Update application's crontab entries using Whenever"
@@ -18,7 +19,7 @@ Capistrano::Configuration.instance(:must_exist).load do
18
19
  args = {
19
20
  :command => fetch(:whenever_command),
20
21
  :flags => fetch(:whenever_update_flags),
21
- :path => fetch(:latest_release)
22
+ :path => fetch(:whenever_path)
22
23
  }
23
24
 
24
25
  if whenever_servers.any?
@@ -38,7 +39,7 @@ Capistrano::Configuration.instance(:must_exist).load do
38
39
  args = {
39
40
  :command => fetch(:whenever_command),
40
41
  :flags => fetch(:whenever_clear_flags),
41
- :path => fetch(:latest_release)
42
+ :path => fetch(:whenever_path)
42
43
  }
43
44
 
44
45
  whenever_run_commands(args)
@@ -3,26 +3,37 @@ namespace :whenever do
3
3
  args = Array(fetch(:whenever_command)) + args
4
4
 
5
5
  on roles fetch(:whenever_roles) do |host|
6
- host_args = Array(yield(host))
7
- within release_path do
6
+ args_for_host = block_given? ? args + Array(yield(host)) : args
7
+ within fetch(:whenever_path) do
8
8
  with fetch(:whenever_command_environment_variables) do
9
- execute *(args + host_args)
9
+ execute(*args_for_host)
10
10
  end
11
11
  end
12
12
  end
13
13
  end
14
14
 
15
+ def load_file
16
+ file = fetch(:whenever_load_file)
17
+ if file
18
+ "-f #{file}"
19
+ else
20
+ ''
21
+ end
22
+ end
23
+
15
24
  desc "Update application's crontab entries using Whenever"
16
25
  task :update_crontab do
17
26
  setup_whenever_task do |host|
18
27
  roles = host.roles_array.join(",")
19
- [fetch(:whenever_update_flags), "--roles=#{roles}"]
28
+ [fetch(:whenever_update_flags), "--roles=#{roles}", load_file]
20
29
  end
21
30
  end
22
31
 
23
32
  desc "Clear application's crontab entries using Whenever"
24
33
  task :clear_crontab do
25
- setup_whenever_task(fetch(:whenever_clear_flags))
34
+ setup_whenever_task do |host|
35
+ [fetch(:whenever_clear_flags), load_file]
36
+ end
26
37
  end
27
38
 
28
39
  after "deploy:updated", "whenever:update_crontab"
@@ -33,11 +44,13 @@ namespace :load do
33
44
  task :defaults do
34
45
  set :whenever_roles, ->{ :db }
35
46
  set :whenever_command, ->{ [:bundle, :exec, :whenever] }
36
- set :whenever_command_environment_variables, ->{ {} }
47
+ set :whenever_command_environment_variables, ->{ fetch(:default_env).merge!(rails_env: fetch(:whenever_environment)) }
37
48
  set :whenever_identifier, ->{ fetch :application }
38
- set :whenever_environment, ->{ fetch :rails_env, "production" }
49
+ set :whenever_environment, ->{ fetch :rails_env, fetch(:stage, "production") }
39
50
  set :whenever_variables, ->{ "environment=#{fetch :whenever_environment}" }
51
+ set :whenever_load_file, ->{ nil }
40
52
  set :whenever_update_flags, ->{ "--update-crontab #{fetch :whenever_identifier} --set #{fetch :whenever_variables}" }
41
53
  set :whenever_clear_flags, ->{ "--clear-crontab #{fetch :whenever_identifier}" }
54
+ set :whenever_path, ->{ fetch :release_path }
42
55
  end
43
56
  end
@@ -1,7 +1,7 @@
1
1
  require 'capistrano/version'
2
2
 
3
3
  if defined?(Capistrano::VERSION) && Gem::Version.new(Capistrano::VERSION).release >= Gem::Version.new('3.0.0')
4
- load File.expand_path("../tasks/whenever.rake", __FILE__)
4
+ load File.expand_path("../capistrano/v3/tasks/whenever.rake", __FILE__)
5
5
  else
6
6
  require 'whenever/capistrano/v2/hooks'
7
7
  end
@@ -1,5 +1,4 @@
1
1
  require 'fileutils'
2
- require 'tempfile'
3
2
 
4
3
  module Whenever
5
4
  class CommandLine
@@ -10,11 +9,12 @@ module Whenever
10
9
  def initialize(options={})
11
10
  @options = options
12
11
 
13
- @options[:file] ||= 'config/schedule.rb'
14
- @options[:cut] ||= 0
15
- @options[:identifier] ||= default_identifier
12
+ @options[:crontab_command] ||= 'crontab'
13
+ @options[:file] ||= 'config/schedule.rb'
14
+ @options[:cut] ||= 0
15
+ @options[:identifier] ||= default_identifier
16
16
 
17
- if !File.exists?(@options[:file]) && @options[:clear].nil?
17
+ if !File.exist?(@options[:file]) && @options[:clear].nil?
18
18
  warn("[fail] Can't find file: #{@options[:file]}")
19
19
  exit(1)
20
20
  end
@@ -29,6 +29,8 @@ module Whenever
29
29
  exit(1)
30
30
  end
31
31
  @options[:cut] = @options[:cut].to_i
32
+
33
+ @timestamp = Time.now.to_s
32
34
  end
33
35
 
34
36
  def run
@@ -56,9 +58,10 @@ module Whenever
56
58
  end
57
59
 
58
60
  def read_crontab
59
- return @current_crontab if @current_crontab
61
+ return @current_crontab if instance_variable_defined?(:@current_crontab)
60
62
 
61
- command = ['crontab -l']
63
+ command = [@options[:crontab_command]]
64
+ command << '-l'
62
65
  command << "-u #{@options[:user]}" if @options[:user]
63
66
 
64
67
  command_results = %x[#{command.join(' ')} 2> /dev/null]
@@ -66,42 +69,44 @@ module Whenever
66
69
  end
67
70
 
68
71
  def write_crontab(contents)
69
- tmp_cron_file = Tempfile.open('whenever_tmp_cron')
70
- tmp_cron_file << contents
71
- tmp_cron_file.fsync
72
-
73
- command = ['crontab']
72
+ command = [@options[:crontab_command]]
74
73
  command << "-u #{@options[:user]}" if @options[:user]
75
- command << tmp_cron_file.path
74
+ # Solaris/SmartOS cron does not support the - option to read from stdin.
75
+ command << "-" unless OS.solaris?
76
+
77
+ IO.popen(command.join(' '), 'r+') do |crontab|
78
+ crontab.write(contents)
79
+ crontab.close_write
80
+ end
76
81
 
77
- if system(command.join(' '))
82
+ success = $?.exitstatus.zero?
83
+
84
+ if success
78
85
  action = 'written' if @options[:write]
79
86
  action = 'updated' if @options[:update]
80
87
  puts "[write] crontab file #{action}"
81
- tmp_cron_file.close!
82
88
  exit(0)
83
89
  else
84
90
  warn "[fail] Couldn't write crontab; try running `whenever' with no options to ensure your schedule file is valid."
85
- tmp_cron_file.close!
86
91
  exit(1)
87
92
  end
88
93
  end
89
94
 
90
95
  def updated_crontab
91
96
  # Check for unopened or unclosed identifier blocks
92
- if read_crontab =~ Regexp.new("^#{comment_open}\s*$") && (read_crontab =~ Regexp.new("^#{comment_close}\s*$")).nil?
93
- warn "[fail] Unclosed indentifier; Your crontab file contains '#{comment_open}', but no '#{comment_close}'"
97
+ if read_crontab =~ Regexp.new("^#{comment_open_regex}\s*$") && (read_crontab =~ Regexp.new("^#{comment_close_regex}\s*$")).nil?
98
+ warn "[fail] Unclosed indentifier; Your crontab file contains '#{comment_open(false)}', but no '#{comment_close(false)}'"
94
99
  exit(1)
95
- elsif (read_crontab =~ Regexp.new("^#{comment_open}\s*$")).nil? && read_crontab =~ Regexp.new("^#{comment_close}\s*$")
96
- warn "[fail] Unopened indentifier; Your crontab file contains '#{comment_close}', but no '#{comment_open}'"
100
+ elsif (read_crontab =~ Regexp.new("^#{comment_open_regex}\s*$")).nil? && read_crontab =~ Regexp.new("^#{comment_close_regex}\s*$")
101
+ warn "[fail] Unopened indentifier; Your crontab file contains '#{comment_close(false)}', but no '#{comment_open(false)}'"
97
102
  exit(1)
98
103
  end
99
104
 
100
105
  # If an existing identier block is found, replace it with the new cron entries
101
- if read_crontab =~ Regexp.new("^#{comment_open}\s*$") && read_crontab =~ Regexp.new("^#{comment_close}\s*$")
106
+ if read_crontab =~ Regexp.new("^#{comment_open_regex}\s*$") && read_crontab =~ Regexp.new("^#{comment_close_regex}\s*$")
102
107
  # If the existing crontab file contains backslashes they get lost going through gsub.
103
108
  # .gsub('\\', '\\\\\\') preserves them. Go figure.
104
- read_crontab.gsub(Regexp.new("^#{comment_open}\s*$.+^#{comment_close}\s*$", Regexp::MULTILINE), whenever_cron.chomp.gsub('\\', '\\\\\\'))
109
+ read_crontab.gsub(Regexp.new("^#{comment_open_regex}\s*$.+^#{comment_close_regex}\s*$", Regexp::MULTILINE), whenever_cron.chomp.gsub('\\', '\\\\\\'))
105
110
  else # Otherwise, append the new cron entries after any existing ones
106
111
  [read_crontab, whenever_cron].join("\n\n")
107
112
  end.gsub(/\n{3,}/, "\n\n") # More than two newlines becomes just two.
@@ -119,16 +124,32 @@ module Whenever
119
124
  stripped_contents.gsub!(/\s+$/, $/)
120
125
  end
121
126
 
122
- def comment_base
123
- "Whenever generated tasks for: #{@options[:identifier]}"
127
+ def comment_base(include_timestamp = true)
128
+ if include_timestamp
129
+ "Whenever generated tasks for: #{@options[:identifier]} at: #{@timestamp}"
130
+ else
131
+ "Whenever generated tasks for: #{@options[:identifier]}"
132
+ end
133
+ end
134
+
135
+ def comment_open(include_timestamp = true)
136
+ "# Begin #{comment_base(include_timestamp)}"
137
+ end
138
+
139
+ def comment_close(include_timestamp = true)
140
+ "# End #{comment_base(include_timestamp)}"
141
+ end
142
+
143
+ def comment_open_regex
144
+ "#{comment_open(false)}(#{timestamp_regex}|)"
124
145
  end
125
146
 
126
- def comment_open
127
- "# Begin #{comment_base}"
147
+ def comment_close_regex
148
+ "#{comment_close(false)}(#{timestamp_regex}|)"
128
149
  end
129
150
 
130
- def comment_close
131
- "# End #{comment_base}"
151
+ def timestamp_regex
152
+ " at: \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} ([+-]\\d{4}|UTC)"
132
153
  end
133
154
  end
134
155
  end