ndr_dev_support 2.1.2 → 3.0.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.
- checksums.yaml +5 -5
- data/.rubocop.yml +23 -4
- data/README.md +24 -31
- data/code_safety.yml +99 -19
- data/lib/ndr_dev_support/daemon/ci_server.rb +94 -0
- data/lib/ndr_dev_support/daemon/stoppable.rb +111 -0
- data/lib/ndr_dev_support/integration_testing.rb +25 -3
- data/lib/ndr_dev_support/integration_testing/drivers/chrome.rb +9 -0
- data/lib/ndr_dev_support/integration_testing/drivers/chrome_headless.rb +17 -0
- data/lib/ndr_dev_support/integration_testing/drivers/firefox.rb +9 -0
- data/lib/ndr_dev_support/integration_testing/drivers/poltergeist.rb +15 -0
- data/lib/ndr_dev_support/integration_testing/drivers/switchable.rb +21 -0
- data/lib/ndr_dev_support/integration_testing/dsl.rb +62 -0
- data/lib/ndr_dev_support/rake_ci/brakeman_helper.rb +48 -0
- data/lib/ndr_dev_support/rake_ci/concerns/commit_metadata_persistable.rb +49 -0
- data/lib/ndr_dev_support/rake_ci/simple_cov_helper.rb +26 -0
- data/lib/ndr_dev_support/slack_message_publisher.rb +48 -0
- data/lib/ndr_dev_support/tasks.rb +12 -0
- data/lib/ndr_dev_support/version.rb +1 -1
- data/lib/tasks/audit_code.rake +94 -92
- data/lib/tasks/ci/brakeman.rake +54 -0
- data/lib/tasks/ci/bundle_audit.rake +45 -0
- data/lib/tasks/ci/bundle_install.rake +21 -0
- data/lib/tasks/ci/housekeep.rake +8 -0
- data/lib/tasks/ci/linguist.rake +42 -0
- data/lib/tasks/ci/notes.rake +30 -0
- data/lib/tasks/ci/prometheus.rake +50 -0
- data/lib/tasks/ci/rugged.rake +39 -0
- data/lib/tasks/ci/server.rake +12 -0
- data/lib/tasks/ci/simplecov.rake +37 -0
- data/lib/tasks/ci/slack.rake +30 -0
- data/lib/tasks/ci/stats.rake +24 -0
- data/ndr_dev_support.gemspec +15 -3
- metadata +164 -18
- data/lib/ndr_dev_support/integration_testing/capybara.rb +0 -3
- data/lib/ndr_dev_support/integration_testing/connection_sharing.rb +0 -47
- data/lib/ndr_dev_support/integration_testing/poltergeist.rb +0 -39
- data/lib/ndr_dev_support/integration_testing/screenshot.rb +0 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: dbcfdf282fc498259213e7c7961b199abf2fab0b20ffd2b3e7ef38d8222722d6
|
4
|
+
data.tar.gz: 3403335c3c26478a99ff0b2de557d0d4c7899d8325073073d12a84d78e36211d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
88
|
+
ndr_dev_support bundles a configured Rails integration testing environment.
|
89
89
|
|
90
|
-
|
90
|
+
By default, it uses `capybara` and `poltergeist` to drive a PhantomJS headless browser, and includes some sensible configuration.
|
91
91
|
|
92
|
-
|
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
|
-
|
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
|
-
|
108
|
-
|
103
|
+
Capybara.default_driver = :chrome_headless
|
104
|
+
Capybara.javascript_driver = :chrome_headless
|
109
105
|
```
|
110
106
|
|
111
|
-
|
112
|
-
|
113
|
-
Add to the `Gemfile`:
|
107
|
+
...or, assuming no driver has been explicitly set, can be selected at runtime:
|
114
108
|
|
115
|
-
```
|
116
|
-
|
117
|
-
gem 'database_cleaner'
|
118
|
-
end
|
109
|
+
```
|
110
|
+
$ INTEGRATION_DRIVER=chrome_headless bin/rake test
|
119
111
|
```
|
120
112
|
|
121
|
-
|
113
|
+
#### Screenshots
|
122
114
|
|
123
|
-
|
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
|
-
|
128
|
-
# Don't wrap each test case in a transaction:
|
129
|
-
self.use_transactional_tests = false
|
117
|
+
#### DSL extensions
|
130
118
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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:
|
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:
|
34
|
-
safe_revision:
|
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:
|
55
|
-
lib/ndr_dev_support/integration_testing/
|
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:
|
59
|
-
lib/ndr_dev_support/integration_testing/
|
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:
|
63
|
-
lib/ndr_dev_support/integration_testing/
|
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:
|
67
|
-
lib/ndr_dev_support/
|
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:
|
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:
|
90
|
-
safe_revision:
|
121
|
+
reviewed_by: josh.pencheon
|
122
|
+
safe_revision: 6240a84dc859af0623328f8667b93299c8eae257
|
91
123
|
lib/ndr_dev_support/version.rb:
|
92
124
|
comments:
|
93
|
-
reviewed_by:
|
94
|
-
safe_revision:
|
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:
|
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:
|
107
|
-
safe_revision:
|
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
|