ndr_dev_support 2.1.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +23 -4
  3. data/README.md +24 -31
  4. data/code_safety.yml +99 -19
  5. data/lib/ndr_dev_support/daemon/ci_server.rb +94 -0
  6. data/lib/ndr_dev_support/daemon/stoppable.rb +111 -0
  7. data/lib/ndr_dev_support/integration_testing.rb +25 -3
  8. data/lib/ndr_dev_support/integration_testing/drivers/chrome.rb +9 -0
  9. data/lib/ndr_dev_support/integration_testing/drivers/chrome_headless.rb +17 -0
  10. data/lib/ndr_dev_support/integration_testing/drivers/firefox.rb +9 -0
  11. data/lib/ndr_dev_support/integration_testing/drivers/poltergeist.rb +15 -0
  12. data/lib/ndr_dev_support/integration_testing/drivers/switchable.rb +21 -0
  13. data/lib/ndr_dev_support/integration_testing/dsl.rb +62 -0
  14. data/lib/ndr_dev_support/rake_ci/brakeman_helper.rb +48 -0
  15. data/lib/ndr_dev_support/rake_ci/concerns/commit_metadata_persistable.rb +49 -0
  16. data/lib/ndr_dev_support/rake_ci/simple_cov_helper.rb +26 -0
  17. data/lib/ndr_dev_support/slack_message_publisher.rb +48 -0
  18. data/lib/ndr_dev_support/tasks.rb +12 -0
  19. data/lib/ndr_dev_support/version.rb +1 -1
  20. data/lib/tasks/audit_code.rake +94 -92
  21. data/lib/tasks/ci/brakeman.rake +54 -0
  22. data/lib/tasks/ci/bundle_audit.rake +45 -0
  23. data/lib/tasks/ci/bundle_install.rake +21 -0
  24. data/lib/tasks/ci/housekeep.rake +8 -0
  25. data/lib/tasks/ci/linguist.rake +42 -0
  26. data/lib/tasks/ci/notes.rake +30 -0
  27. data/lib/tasks/ci/prometheus.rake +50 -0
  28. data/lib/tasks/ci/rugged.rake +39 -0
  29. data/lib/tasks/ci/server.rake +12 -0
  30. data/lib/tasks/ci/simplecov.rake +37 -0
  31. data/lib/tasks/ci/slack.rake +30 -0
  32. data/lib/tasks/ci/stats.rake +24 -0
  33. data/ndr_dev_support.gemspec +15 -3
  34. metadata +164 -18
  35. data/lib/ndr_dev_support/integration_testing/capybara.rb +0 -3
  36. data/lib/ndr_dev_support/integration_testing/connection_sharing.rb +0 -47
  37. data/lib/ndr_dev_support/integration_testing/poltergeist.rb +0 -39
  38. data/lib/ndr_dev_support/integration_testing/screenshot.rb +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b462909165b10c8d687b0748b757ee8a797766bc
4
- data.tar.gz: bfaa3539a90a7b010f81e12d31c2852a25f1a6e4
2
+ SHA256:
3
+ metadata.gz: dbcfdf282fc498259213e7c7961b199abf2fab0b20ffd2b3e7ef38d8222722d6
4
+ data.tar.gz: 3403335c3c26478a99ff0b2de557d0d4c7899d8325073073d12a84d78e36211d
5
5
  SHA512:
6
- metadata.gz: 202df7fbda561139125efae9eeb3ba6e1753c0252c8e3097a6febb0668a81e866bb2daf1ba8f6480dafb6ecd37cabd1123d7f1a798869ef825a21ea7336f428f
7
- data.tar.gz: abf58184c149a5053dc394ef41184e54b733a4fe5aaa01be61b3455f11765c5d1aabf91af8a927efdff74d83a3dbcfcbafeac72dc1ceb9ee00fb8cc7216fb26e
6
+ metadata.gz: '02974ba29c5f09941a52fb3fcc7af3aeb01a9edb9d80608ac500959dd19fb39290b35e5b33c55e55f833f552f0313a86c0ca370d333531b6e182f5db4ec8a2a3'
7
+ data.tar.gz: ec95966fbb3dcf4826e54e1eb1c14b981541243d49daf1c83662e2b109f4b36a9973a5320f9b6615e6fe13de53c8d817966d596bc834914a66af10c270032aca
data/.rubocop.yml CHANGED
@@ -13,10 +13,23 @@ AllCops:
13
13
  - 'tmp/**/*'
14
14
  - 'vendor/**/*'
15
15
 
16
+ # Once supported by RuboCop, this will prevent cop-specific Excludes from
17
+ # overwriting the AllCops defaults above:
18
+ #
19
+ # inherit_mode:
20
+ # merge:
21
+ # - Exclude
22
+
16
23
  # Run the Rails cops by default (-R/--rails not required):
17
24
  Rails:
18
25
  Enabled: true
19
26
 
27
+ ##################### Layout #################################
28
+
29
+ Layout/DotPosition:
30
+ # Multi-line method chaining should be done with trailing dots.
31
+ EnforcedStyle: trailing
32
+
20
33
  ##################### Style ##################################
21
34
 
22
35
  # We make use of block comments, e.g. for validation documentation.
@@ -27,10 +40,6 @@ Style/Documentation:
27
40
  Exclude:
28
41
  - 'test/**/*.rb'
29
42
 
30
- Style/DotPosition:
31
- # Multi-line method chaining should be done with trailing dots.
32
- EnforcedStyle: trailing
33
-
34
43
  Style/FrozenStringLiteralComment:
35
44
  # We're not confident enough to make this recommendation everywhere
36
45
  Enabled: false
@@ -46,6 +55,10 @@ Style/NumericLiterals:
46
55
  Exclude:
47
56
  - 'test/**/*.rb'
48
57
 
58
+ Style/YodaCondition:
59
+ # Disagree; literals as first argument can guard against accidental assignment.
60
+ Enabled: false
61
+
49
62
  Style/SingleLineBlockParams:
50
63
  # Prefer readability of contextually-named variables.
51
64
  Enabled: false
@@ -101,3 +114,9 @@ Rails/ActionFilter:
101
114
  # projects will want to override this configuration to use 'filter' instead.
102
115
  EnforcedStyle: action
103
116
 
117
+ Rails/SkipsModelValidations:
118
+ # Methods like 'update_column' exist for a reason, and it is the developer's
119
+ # responsibilty to understand the behaviour of the code they write; blanket
120
+ # avoiding them is not helpful/practical.
121
+ Enabled: false
122
+
data/README.md CHANGED
@@ -85,54 +85,47 @@ $ find . -iregex .*\.rake$ | xargs rake rubocop:diff:file
85
85
 
86
86
  ### Integration test environment
87
87
 
88
- ndr_dev_support bundles a configured Rails integration testing environment. It uses `capybara` and `poltergeist` to drive a PhantomJS headless browser, and includes some sensible configuration.
88
+ ndr_dev_support bundles a configured Rails integration testing environment.
89
89
 
90
- If an integration test errors or fails, `capybara-screenshot` is used to automatically retrieve a full-height screenshot from PhantomJS, which is then stored in `tmp/`.
90
+ By default, it uses `capybara` and `poltergeist` to drive a PhantomJS headless browser, and includes some sensible configuration.
91
91
 
92
- Beyond standard Capybara testing DSL, ndr_dev_support bundles some additional functionality:
93
-
94
- * `clear_headless_session!` - causes PhantomJS to reset, simulating a browser restart.
95
- * `delete_all_cookies!` - causes PhantomJS to delete all cookies. Helpful for testing AJAX logouts.
96
- * `within_screenshot_compatible_window` – similar to `within_window`, but allows failure screenshots to be taken of the failing child window, rather than the spawning parent.
97
-
98
- To use, ensure `phantomjs` is installed, and add the following to your application's `test_helper.rb`
92
+ To use, simply add the following to your application's `test_helper.rb`
99
93
 
100
94
  ```ruby
101
95
  require 'ndr_dev_support/integration_testing'
102
96
  ```
103
97
 
104
- When using `capybara` with PhantomJS, the test database must be consistent between the test runner and the application being tested. With transactional tests in operation, this means that both must share a connection. Doing so is error-prone, and can introduce race conditions. However, some projects have had success with the approach, so it is available within `ndr_dev_support` with the following additional require statement:
98
+ #### Other drivers
99
+
100
+ Other drivers are also supported; `chrome` / `chrome_headless` / `firefox` are all powered by selenium, and can either be explicitly used with:
105
101
 
106
102
  ```ruby
107
- # WARNING: can result in race conditions within the test suite
108
- require 'ndr_dev_support/integration_testing/connection_sharing'
103
+ Capybara.default_driver = :chrome_headless
104
+ Capybara.javascript_driver = :chrome_headless
109
105
  ```
110
106
 
111
- The slower, more reliable, alternative is to use the `database_cleaner` gem. `ndr_dev_support` provides no built-in support for this approach, as configuration can be quite project-specific. However, as a starting point:
112
-
113
- Add to the `Gemfile`:
107
+ ...or, assuming no driver has been explicitly set, can be selected at runtime:
114
108
 
115
- ```ruby
116
- group :test do
117
- gem 'database_cleaner'
118
- end
109
+ ```
110
+ $ INTEGRATION_DRIVER=chrome_headless bin/rake test
119
111
  ```
120
112
 
121
- Add to `test_helper.rb`:
113
+ #### Screenshots
122
114
 
123
- ```ruby
124
- require 'database_cleaner'
125
- DatabaseCleaner.strategy = :deletion # anecdotally, faster than :truncation for our projects
115
+ If an integration test errors or fails, `capybara-screenshot` is used to automatically retrieve a full-height screenshot from the headless browser, which is then stored in `tmp/`.
126
116
 
127
- class ActionDispatch::IntegrationTest
128
- # Don't wrap each test case in a transaction:
129
- self.use_transactional_tests = false
117
+ #### DSL extensions
130
118
 
131
- # Instead, insert fixtures afresh between each test:
132
- setup { DatabaseCleaner.start }
133
- teardown { DatabaseCleaner.clean }
134
- end
135
- ```
119
+ Beyond standard Capybara testing DSL, ndr_dev_support bundles some additional functionality:
120
+
121
+ * `clear_headless_session!` - causes the headless browser to reset, simulating a browser restart.
122
+ * `delete_all_cookies!` - causes the headless browser to delete all cookies. Helpful for testing AJAX logouts.
123
+ * `within_screenshot_compatible_window` – similar to `within_window`, but allows failure screenshots to be taken of the failing child window, rather than the spawning parent.
124
+ * `within_modal` - scope capybara to only interact within a modal, and (by default) expect the modal to disappear when done.
125
+
126
+ #### Database synchronisation
127
+
128
+ When using a headless browser for integration tests, the test database must be consistent between the test runner and the application being tested. With transactional tests in operation, this means that both must share a connection. It is up to the individual project to provide this facility; as of Rails 5.1, it is built in to the framework directly.
136
129
 
137
130
  ## Development
138
131
 
data/code_safety.yml CHANGED
@@ -11,7 +11,7 @@ file safety:
11
11
  ".rubocop.yml":
12
12
  comments:
13
13
  reviewed_by: timgentry
14
- safe_revision: ac89be1deb3d47e9fb00ac2de424116593012074
14
+ safe_revision: 3a4fc58f43e28c32e4fea25fa56a1eaf4a887d84
15
15
  ".travis.yml":
16
16
  comments:
17
17
  reviewed_by: josh.pencheon
@@ -30,8 +30,8 @@ file safety:
30
30
  safe_revision: c59a45986f8b6d087c8c21b1e889f31f7346da17
31
31
  README.md:
32
32
  comments:
33
- reviewed_by: timgentry
34
- safe_revision: 04a0d70cc467dad5a380744a6c678693429e0213
33
+ reviewed_by: josh.pencheon
34
+ safe_revision: e0b3871607c649565330d6e62740ffd860930338
35
35
  Rakefile:
36
36
  comments:
37
37
  reviewed_by: josh.pencheon
@@ -48,26 +48,54 @@ file safety:
48
48
  comments:
49
49
  reviewed_by: timgentry
50
50
  safe_revision: c59a45986f8b6d087c8c21b1e889f31f7346da17
51
+ lib/ndr_dev_support/daemon/ci_server.rb:
52
+ comments:
53
+ reviewed_by: josh.pencheon
54
+ safe_revision: 3d81c21c0085acb44aedadeaa66313ac280eb326
55
+ lib/ndr_dev_support/daemon/stoppable.rb:
56
+ comments:
57
+ reviewed_by: josh.pencheon
58
+ safe_revision: 1bdb008829c0c19d7167f9d6aea9b97db15a5403
51
59
  lib/ndr_dev_support/integration_testing.rb:
52
60
  comments:
53
61
  reviewed_by: josh.pencheon
54
- safe_revision: 5b238d73cfb0e506ced8b4e474cc2b3392838925
55
- lib/ndr_dev_support/integration_testing/capybara.rb:
62
+ safe_revision: e0b3871607c649565330d6e62740ffd860930338
63
+ lib/ndr_dev_support/integration_testing/drivers/chrome.rb:
64
+ comments:
65
+ reviewed_by: josh.pencheon
66
+ safe_revision: e0b3871607c649565330d6e62740ffd860930338
67
+ lib/ndr_dev_support/integration_testing/drivers/chrome_headless.rb:
68
+ comments:
69
+ reviewed_by: josh.pencheon
70
+ safe_revision: e0b3871607c649565330d6e62740ffd860930338
71
+ lib/ndr_dev_support/integration_testing/drivers/firefox.rb:
72
+ comments:
73
+ reviewed_by: josh.pencheon
74
+ safe_revision: e0b3871607c649565330d6e62740ffd860930338
75
+ lib/ndr_dev_support/integration_testing/drivers/poltergeist.rb:
56
76
  comments:
57
77
  reviewed_by: josh.pencheon
58
- safe_revision: 88ac9269eb8f0b44c5920a946708e4715421bcad
59
- lib/ndr_dev_support/integration_testing/connection_sharing.rb:
78
+ safe_revision: e0b3871607c649565330d6e62740ffd860930338
79
+ lib/ndr_dev_support/integration_testing/drivers/switchable.rb:
60
80
  comments:
61
81
  reviewed_by: josh.pencheon
62
- safe_revision: 6cf4a9cab6f8f028d45e05e256b63d9fe9d1b7fc
63
- lib/ndr_dev_support/integration_testing/poltergeist.rb:
82
+ safe_revision: e0b3871607c649565330d6e62740ffd860930338
83
+ lib/ndr_dev_support/integration_testing/dsl.rb:
64
84
  comments:
65
85
  reviewed_by: josh.pencheon
66
- safe_revision: 88ac9269eb8f0b44c5920a946708e4715421bcad
67
- lib/ndr_dev_support/integration_testing/screenshot.rb:
86
+ safe_revision: 12d9c51ec2bc946ee02eee63700e905b8c0c01c1
87
+ lib/ndr_dev_support/rake_ci/brakeman_helper.rb:
68
88
  comments:
69
89
  reviewed_by: josh.pencheon
70
- safe_revision: 88ac9269eb8f0b44c5920a946708e4715421bcad
90
+ safe_revision: 49caa4ef8ad9926843354d36f4610d40603ec632
91
+ lib/ndr_dev_support/rake_ci/concerns/commit_metadata_persistable.rb:
92
+ comments:
93
+ reviewed_by: josh.pencheon
94
+ safe_revision: 49caa4ef8ad9926843354d36f4610d40603ec632
95
+ lib/ndr_dev_support/rake_ci/simple_cov_helper.rb:
96
+ comments:
97
+ reviewed_by: josh.pencheon
98
+ safe_revision: 6240a84dc859af0623328f8667b93299c8eae257
71
99
  lib/ndr_dev_support/rubocop/executor.rb:
72
100
  comments:
73
101
  reviewed_by: josh.pencheon
@@ -84,27 +112,79 @@ file safety:
84
112
  comments:
85
113
  reviewed_by: josh.pencheon
86
114
  safe_revision: 41cf1558f567928faaa1e670a36d941c03c78044
115
+ lib/ndr_dev_support/slack_message_publisher.rb:
116
+ comments:
117
+ reviewed_by: josh.pencheon
118
+ safe_revision: ad38e92c6e56b9d81fdab10681d8f2924eeadf5a
87
119
  lib/ndr_dev_support/tasks.rb:
88
120
  comments:
89
- reviewed_by: joshpencheon
90
- safe_revision: a55c971cb770a8e5dc4be7ed66663dd5d695fcd1
121
+ reviewed_by: josh.pencheon
122
+ safe_revision: 6240a84dc859af0623328f8667b93299c8eae257
91
123
  lib/ndr_dev_support/version.rb:
92
124
  comments:
93
- reviewed_by: timgentry
94
- safe_revision: fde5bc348b5393ab95b8f0e8d5cae9df29a85f34
125
+ reviewed_by: josh.pencheon
126
+ safe_revision: b725560e101395a6dd1a9ece106462edb6ebcc74
95
127
  lib/tasks/audit_code.rake:
96
128
  comments: Identical to the version reviewed by josh.pencheon when contained within
97
129
  ndr_support
130
+ reviewed_by: josh.pencheon
131
+ safe_revision: 73030d56a06d05ae2d8716713b01647d76b7d904
132
+ lib/tasks/ci/brakeman.rake:
133
+ comments:
134
+ reviewed_by: josh.pencheon
135
+ safe_revision: 49caa4ef8ad9926843354d36f4610d40603ec632
136
+ lib/tasks/ci/bundle_audit.rake:
137
+ comments:
138
+ reviewed_by: josh.pencheon
139
+ safe_revision: ddcf7de0bf1f5fb17cc28e2460009e162ff8917c
140
+ lib/tasks/ci/bundle_install.rake:
141
+ comments:
142
+ reviewed_by: timgentry
143
+ safe_revision: cf22c7ab8b2eb67b501b5ebd1309c832db4fdf6c
144
+ lib/tasks/ci/housekeep.rake:
145
+ comments:
146
+ reviewed_by: timgentry
147
+ safe_revision: 3dfcdb6c76fbccc44a331986673d63282f87f2fa
148
+ lib/tasks/ci/linguist.rake:
149
+ comments:
150
+ reviewed_by: timgentry
151
+ safe_revision: 00cd6c2f71612a467ee88ad53c21e08f073871d5
152
+ lib/tasks/ci/notes.rake:
153
+ comments:
154
+ reviewed_by: timgentry
155
+ safe_revision: f828113894a16581d0aa181504c799e661f8401d
156
+ lib/tasks/ci/prometheus.rake:
157
+ comments:
158
+ reviewed_by: josh.pencheon
159
+ safe_revision: a10b6f3f5c9261135fae534e5785e2342430dde2
160
+ lib/tasks/ci/rugged.rake:
161
+ comments:
162
+ reviewed_by: timgentry
163
+ safe_revision: 7ab7061b257f916eb43bc8d184aa425f1f08b739
164
+ lib/tasks/ci/server.rake:
165
+ comments:
166
+ reviewed_by: timgentry
167
+ safe_revision: e581239a77666500a3108bcb3d480e370a82adb8
168
+ lib/tasks/ci/simplecov.rake:
169
+ comments:
170
+ reviewed_by: josh.pencheon
171
+ safe_revision: 6240a84dc859af0623328f8667b93299c8eae257
172
+ lib/tasks/ci/slack.rake:
173
+ comments:
98
174
  reviewed_by: timgentry
99
- safe_revision: 4813e92fc951a3c033c08c848a9e07e8d5867133
175
+ safe_revision: ad38e92c6e56b9d81fdab10681d8f2924eeadf5a
176
+ lib/tasks/ci/stats.rake:
177
+ comments:
178
+ reviewed_by: josh.pencheon
179
+ safe_revision: 137be2205f283d444286c47502387487bb1b7015
100
180
  lib/tasks/rubocop.rake:
101
181
  comments:
102
182
  reviewed_by: josh.pencheon
103
183
  safe_revision: 41cf1558f567928faaa1e670a36d941c03c78044
104
184
  ndr_dev_support.gemspec:
105
185
  comments:
106
- reviewed_by: timgentry
107
- safe_revision: ac89be1deb3d47e9fb00ac2de424116593012074
186
+ reviewed_by: josh.pencheon
187
+ safe_revision: e0b3871607c649565330d6e62740ffd860930338
108
188
  test/ndr_dev_support_test.rb:
109
189
  comments:
110
190
  reviewed_by: timgentry
@@ -0,0 +1,94 @@
1
+ require_relative 'stoppable'
2
+ require 'rugged'
3
+
4
+ module NdrDevSupport
5
+ module Daemon
6
+ # Wrapper around Rake based CI testing loop
7
+ class CIServer
8
+ include Stoppable
9
+
10
+ GIT_SVN_REMOTE_BRANCH_NAME = 'git-svn'.freeze
11
+ MASTER_BRANCH_NAME = 'master'.freeze
12
+ ORIGIN_MASTER_BRANCH_NAME = 'origin/master'.freeze
13
+
14
+ attr_reader :repo
15
+
16
+ def self.from_args(env)
17
+ name = env['WORKER_NAME'].to_s
18
+
19
+ new(name: name)
20
+ end
21
+
22
+ def initialize(name:)
23
+ super
24
+
25
+ # Worker name can be used for clear logging:
26
+ @name = name
27
+ raise ArgumentError, 'No WORKER_NAME specified!' if name.blank?
28
+
29
+ @repo = Rugged::Repository.new('.')
30
+ end
31
+
32
+ def self.friendly_revision_name(commit)
33
+ if (matchdata = commit.message.match(/\bgit-svn-id: [^@]+@(\d+)\s/))
34
+ matchdata[1]
35
+ else
36
+ commit.oid[0, 7]
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def run_once
43
+ git_fetch
44
+ git_checkout(MASTER_BRANCH_NAME)
45
+
46
+ objectids_between_master_and_remote.each do |oid|
47
+ `git rebase #{oid}`
48
+
49
+ Rake::Task['ci:all'].invoke
50
+ end
51
+ end
52
+
53
+ def git_fetch
54
+ svn_remote? ? `git svn fetch` : `git fetch`
55
+ end
56
+
57
+ def git_checkout(oid)
58
+ `git checkout #{oid}`
59
+ end
60
+
61
+ def svn_remote?
62
+ GIT_SVN_REMOTE_BRANCH_NAME == remote_branch
63
+ end
64
+
65
+ def remote_branch
66
+ return @remote_branch if @remote_branch
67
+
68
+ remote_branches = repo.branches.each_name(:remote)
69
+ if remote_branches.count == 1
70
+ @remote_branch = remote_branches.first
71
+ elsif remote_branches.include?(ORIGIN_MASTER_BRANCH_NAME)
72
+ @remote_branch = ORIGIN_MASTER_BRANCH_NAME
73
+ else
74
+ raise "One remote branch expected (#{remote_branches.to_a.inspect})"
75
+ end
76
+ end
77
+
78
+ def objectids_between_master_and_remote
79
+ walker = Rugged::Walker.new(@repo)
80
+ walker.push(repo.branches[remote_branch].target_id)
81
+ current_target_id = repo.branches[MASTER_BRANCH_NAME].target_id
82
+
83
+ revisions = []
84
+ # walk backwards from the most recent commit, breaking at the current one
85
+ walker.each do |commit|
86
+ break if commit.oid == current_target_id
87
+ revisions << commit.oid
88
+ end
89
+
90
+ revisions.reverse
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,111 @@
1
+ require 'active_support/all'
2
+ require 'active_support/core_ext/numeric/bytes'
3
+
4
+ module NdrDevSupport
5
+ module Daemon
6
+ # Behaviour that allows daemons to be restarted, and stopped by god.
7
+ # To use, you need to call `super` in the initialize method, if defined.
8
+ module Stoppable
9
+ extend ActiveSupport::Concern
10
+
11
+ # touch this file to trigger graceful exit
12
+ # TODO: Consider supporting Rails
13
+ # RESTART_FILENAME = Rails.root.join('tmp', 'restart.txt')
14
+ RESTART_FILENAME = 'restart.txt'
15
+
16
+ MAX_MEMORY = 3.gigabytes # restart between jobs if memory consumption exceeds this
17
+ MAX_UPTIME = 2.hours # restart between jobs if have been up this long
18
+
19
+ # how long the daemon waits when it runs out of things to do:
20
+ # BIG_SLEEP = Rails.env.development? ? 10.seconds : 1.minute
21
+ BIG_SLEEP = 1.minute
22
+
23
+ # when idle, how long the daemon between making restart checks?
24
+ LITTLE_SLEEP = 5.seconds
25
+
26
+ included do
27
+ attr_reader :name, :start_time
28
+ end
29
+
30
+ def initialize(*)
31
+ setup_signals
32
+
33
+ @start_time = Time.current
34
+ end
35
+
36
+ def stop
37
+ @should_stop = true
38
+ end
39
+
40
+ def should_stop?
41
+ @should_stop ||= restart_file_touched? || excessive_memory? || been_up_a_while?
42
+ end
43
+
44
+ def run(exit_when_done: false)
45
+ loop do
46
+ run_once
47
+
48
+ # we've done all we can for the time being; either exit now, or
49
+ # have a sleep and loop round for another go:
50
+ break if exit_when_done
51
+ snooze(BIG_SLEEP)
52
+ # Our snooze may have come to an abrupt end:
53
+ break if should_stop?
54
+ end
55
+
56
+ if should_stop?
57
+ # An instruction to stop has been received:
58
+ log('Stopping')
59
+ return :stopped
60
+ else
61
+ # Processing has come to a natural end:
62
+ log('Done, exiting')
63
+ return :exiting
64
+ end
65
+ end
66
+
67
+ def log(message, level = :info)
68
+ tags = "[#{Time.current.to_s(:db)}] [#{level.upcase}] [daemon: #{name} (#{Process.pid})]"
69
+ message = "#{tags} #{message}"
70
+
71
+ $stdout.puts(message) unless Rails.env.test? || !$stdout.isatty
72
+ Rails.logger.send(level, message)
73
+ end
74
+
75
+ private
76
+
77
+ def setup_signals
78
+ Signal.trap('TERM') { stop }
79
+ end
80
+
81
+ def restart_file_touched?
82
+ File.exist?(RESTART_FILENAME) && File.mtime(RESTART_FILENAME) > start_time
83
+ end
84
+
85
+ def excessive_memory?
86
+ (`ps -o rss= -p #{$$}`.to_i.kilobytes) > MAX_MEMORY
87
+ rescue
88
+ false
89
+ end
90
+
91
+ def been_up_a_while?
92
+ start_time < MAX_UPTIME.ago
93
+ end
94
+
95
+ # sleeps for `duration`, but wakes up periodically to
96
+ # see if the daemon has been asked to restart. If so,
97
+ # returns immediately.
98
+ def snooze(duration)
99
+ number_of_mini_sleeps = duration / LITTLE_SLEEP
100
+ initial_sleep_length = duration % LITTLE_SLEEP
101
+
102
+ sleep(initial_sleep_length)
103
+
104
+ number_of_mini_sleeps.times do
105
+ return if should_stop?
106
+ sleep(LITTLE_SLEEP)
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end