ndr_dev_support 4.0.0 → 4.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a77c75ad60b45a91190083d470912db589015a7237713b510b9aa4b6cfc03284
4
- data.tar.gz: 9ddefdc49d88b6d445586537284590fb656fb8a97425c77e7881660bfb8f467d
3
+ metadata.gz: e2b84dc423c034b919fb74cd67458fc885d4f6565f4ad5798d2fbf201d8ddfc0
4
+ data.tar.gz: 26689488b7846c1edaf32f45f2a60976b4b7938a7af6d12a1626dce91d1c838a
5
5
  SHA512:
6
- metadata.gz: dd8c7362ccb13351c211cb068a61fe660a2f4fa36b7bc93d98b5f2c395a93cfa23e503ddb07578382d631e617a1c06593c32d53c3cbfa40fffc29103f92880ba
7
- data.tar.gz: 9534b04645eae6cae4d72902773f04809f8488c6a342e07e7fbf762da02372194c21f6ce0713a66856fb3034c04e341c861b939427c3205c5e37ae8d18f170a5
6
+ metadata.gz: 7f58716ac63abaeb4597084393f44528fa1f7426f3dce76f71fc771a3427a3dd5edcc5ee30df7e817ed35ba553107cca2997590aab1ff11811a850fe4941263e
7
+ data.tar.gz: a41b7019ba639d84bb3b28f01773c6aa3e69c217fadd8995655c90ec7c0392f7692288448dd23b029ffd92c86b97a6ca5a2fc6c2c4693830326b3a009dce9e62
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ rake_ci.*.yml
data/.rubocop.yml CHANGED
@@ -8,7 +8,7 @@ AllCops:
8
8
  # All cops should ignore files in the following locations:
9
9
  Exclude:
10
10
  - 'bin/*'
11
- - 'db/**/*'
11
+ - 'db/schema.rb'
12
12
  - 'lib/generators/**/templates/*'
13
13
  - 'tmp/**/*'
14
14
  - 'vendor/**/*'
@@ -44,15 +44,13 @@ Style/FrozenStringLiteralComment:
44
44
  # We're not confident enough to make this recommendation everywhere
45
45
  Enabled: false
46
46
 
47
- Metrics/LineLength:
48
- Max: 100
49
-
50
47
  Style/ModuleFunction:
51
48
  # `extend self` has fewer side effects than `module_function`.
52
49
  EnforcedStyle: extend_self
53
50
 
54
51
  Style/NumericLiterals:
55
52
  Exclude:
53
+ - 'db/migrate/*.rb'
56
54
  - 'test/**/*.rb'
57
55
 
58
56
  Style/YodaCondition:
@@ -75,6 +73,9 @@ Performance/Casecmp:
75
73
 
76
74
  Metrics/AbcSize:
77
75
  Max: 23
76
+ Exclude:
77
+ - 'db/migrate/*.rb'
78
+ - 'test/**/*.rb'
78
79
 
79
80
  Metrics/BlockLength:
80
81
  # We're already limiting method size, blocks outside of methods
@@ -84,6 +85,7 @@ Metrics/BlockLength:
84
85
  Metrics/ClassLength:
85
86
  Max: 150
86
87
  Exclude:
88
+ - 'db/migrate/*.rb'
87
89
  - 'test/**/*.rb'
88
90
 
89
91
  Metrics/ModuleLength:
@@ -97,11 +99,13 @@ Metrics/CyclomaticComplexity:
97
99
  Metrics/LineLength:
98
100
  Max: 100
99
101
  Exclude:
102
+ - 'db/migrate/*.rb'
100
103
  - 'test/**/*.rb'
101
104
 
102
105
  Metrics/MethodLength:
103
106
  Max: 15
104
107
  Exclude:
108
+ - 'db/migrate/*.rb'
105
109
  - 'test/**/*.rb'
106
110
 
107
111
  Metrics/PerceivedComplexity:
data/code_safety.yml CHANGED
@@ -2,8 +2,8 @@
2
2
  file safety:
3
3
  ".gitignore":
4
4
  comments:
5
- reviewed_by: timgentry
6
- safe_revision: c59a45986f8b6d087c8c21b1e889f31f7346da17
5
+ reviewed_by: josh.pencheon
6
+ safe_revision: 3e1fbd0a5931f7598a2dd3f929a7cd8366dc0e96
7
7
  ".hound.yml":
8
8
  comments:
9
9
  reviewed_by: timgentry
@@ -11,7 +11,7 @@ file safety:
11
11
  ".rubocop.yml":
12
12
  comments:
13
13
  reviewed_by: josh.pencheon
14
- safe_revision: 949a0ff086946426531a34d48f646b32c761c272
14
+ safe_revision: 33ecea8453c2b82c3e360819831bee5f1b8d3d4d
15
15
  ".travis.yml":
16
16
  comments:
17
17
  reviewed_by: josh.pencheon
@@ -44,6 +44,10 @@ file safety:
44
44
  comments:
45
45
  reviewed_by: timgentry
46
46
  safe_revision: c59a45986f8b6d087c8c21b1e889f31f7346da17
47
+ lib/minitest/rake_ci_reporter.rb:
48
+ comments:
49
+ reviewed_by: josh.pencheon
50
+ safe_revision: 3e1fbd0a5931f7598a2dd3f929a7cd8366dc0e96
47
51
  lib/ndr_dev_support.rb:
48
52
  comments:
49
53
  reviewed_by: timgentry
@@ -83,7 +87,7 @@ file safety:
83
87
  lib/ndr_dev_support/daemon/ci_server.rb:
84
88
  comments:
85
89
  reviewed_by: josh.pencheon
86
- safe_revision: ec3d1f3fa1d1f1f3d039587a2f31bda28062f731
90
+ safe_revision: 66ce36e2646d392e3a67e19fcb9322ed8c5a6c16
87
91
  lib/ndr_dev_support/daemon/stoppable.rb:
88
92
  comments:
89
93
  reviewed_by: josh.pencheon
@@ -120,10 +124,34 @@ file safety:
120
124
  comments:
121
125
  reviewed_by: josh.pencheon
122
126
  safe_revision: 49caa4ef8ad9926843354d36f4610d40603ec632
127
+ lib/ndr_dev_support/rake_ci/commit_cop.rb:
128
+ comments:
129
+ reviewed_by: josh.pencheon
130
+ safe_revision: a3f5b7d3709f7e340f2f7e81dab026a63b21e3ff
131
+ lib/ndr_dev_support/rake_ci/commit_cop/concerns/deputisable.rb:
132
+ comments:
133
+ reviewed_by: josh.pencheon
134
+ safe_revision: a3f5b7d3709f7e340f2f7e81dab026a63b21e3ff
135
+ lib/ndr_dev_support/rake_ci/commit_cop/migration_without_structure_dump.rb:
136
+ comments:
137
+ reviewed_by: josh.pencheon
138
+ safe_revision: a3f5b7d3709f7e340f2f7e81dab026a63b21e3ff
139
+ lib/ndr_dev_support/rake_ci/commit_cop/modified_migration.rb:
140
+ comments:
141
+ reviewed_by: josh.pencheon
142
+ safe_revision: a3f5b7d3709f7e340f2f7e81dab026a63b21e3ff
143
+ lib/ndr_dev_support/rake_ci/commit_cop/renamed_migration.rb:
144
+ comments:
145
+ reviewed_by: josh.pencheon
146
+ safe_revision: a3f5b7d3709f7e340f2f7e81dab026a63b21e3ff
123
147
  lib/ndr_dev_support/rake_ci/concerns/commit_metadata_persistable.rb:
124
148
  comments:
125
149
  reviewed_by: josh.pencheon
126
150
  safe_revision: 49caa4ef8ad9926843354d36f4610d40603ec632
151
+ lib/ndr_dev_support/rake_ci/redmine/ticket_resolver.rb:
152
+ comments:
153
+ reviewed_by: josh.pencheon
154
+ safe_revision: 2154aa7f32e731933ff6091b8f42b2b014028a6a
127
155
  lib/ndr_dev_support/rake_ci/simple_cov_helper.rb:
128
156
  comments:
129
157
  reviewed_by: josh.pencheon
@@ -151,11 +179,11 @@ file safety:
151
179
  lib/ndr_dev_support/tasks.rb:
152
180
  comments:
153
181
  reviewed_by: josh.pencheon
154
- safe_revision: ec3d1f3fa1d1f1f3d039587a2f31bda28062f731
182
+ safe_revision: 2154aa7f32e731933ff6091b8f42b2b014028a6a
155
183
  lib/ndr_dev_support/version.rb:
156
184
  comments:
157
185
  reviewed_by: josh.pencheon
158
- safe_revision: 3efca19afad2f7dedc1d470cd8dd51e95ec77545
186
+ safe_revision: e3f427496995516245e0de5c371b6867cf0a04df
159
187
  lib/tasks/audit_code.rake:
160
188
  comments: Identical to the version reviewed by josh.pencheon when contained within
161
189
  ndr_support
@@ -169,6 +197,10 @@ file safety:
169
197
  comments:
170
198
  reviewed_by: josh.pencheon
171
199
  safe_revision: ddcf7de0bf1f5fb17cc28e2460009e162ff8917c
200
+ lib/tasks/ci/commit_cop.rake:
201
+ comments:
202
+ reviewed_by: josh.pencheon
203
+ safe_revision: a3f5b7d3709f7e340f2f7e81dab026a63b21e3ff
172
204
  lib/tasks/ci/dependencies.rake:
173
205
  comments:
174
206
  reviewed_by: josh.pencheon
@@ -181,6 +213,10 @@ file safety:
181
213
  comments:
182
214
  reviewed_by: timgentry
183
215
  safe_revision: 00cd6c2f71612a467ee88ad53c21e08f073871d5
216
+ lib/tasks/ci/minitest.rake:
217
+ comments:
218
+ reviewed_by: josh.pencheon
219
+ safe_revision: 3e1fbd0a5931f7598a2dd3f929a7cd8366dc0e96
184
220
  lib/tasks/ci/notes.rake:
185
221
  comments:
186
222
  reviewed_by: timgentry
@@ -188,23 +224,27 @@ file safety:
188
224
  lib/tasks/ci/prometheus.rake:
189
225
  comments:
190
226
  reviewed_by: josh.pencheon
191
- safe_revision: a10b6f3f5c9261135fae534e5785e2342430dde2
227
+ safe_revision: 6cb22447594ebf450fc60deb90fd223c8067ff18
228
+ lib/tasks/ci/redmine.rake:
229
+ comments:
230
+ reviewed_by: josh.pencheon
231
+ safe_revision: 2154aa7f32e731933ff6091b8f42b2b014028a6a
192
232
  lib/tasks/ci/rugged.rake:
193
233
  comments:
194
234
  reviewed_by: timgentry
195
235
  safe_revision: 7ab7061b257f916eb43bc8d184aa425f1f08b739
196
236
  lib/tasks/ci/server.rake:
197
237
  comments:
198
- reviewed_by: timgentry
199
- safe_revision: e581239a77666500a3108bcb3d480e370a82adb8
238
+ reviewed_by: josh.pencheon
239
+ safe_revision: d8e52aab569d9efac3e27485b5721c9608a84b6c
200
240
  lib/tasks/ci/simplecov.rake:
201
241
  comments:
202
242
  reviewed_by: josh.pencheon
203
243
  safe_revision: 6240a84dc859af0623328f8667b93299c8eae257
204
244
  lib/tasks/ci/slack.rake:
205
245
  comments:
206
- reviewed_by: timgentry
207
- safe_revision: ad38e92c6e56b9d81fdab10681d8f2924eeadf5a
246
+ reviewed_by: josh.pencheon
247
+ safe_revision: 6cb22447594ebf450fc60deb90fd223c8067ff18
208
248
  lib/tasks/ci/stats.rake:
209
249
  comments:
210
250
  reviewed_by: josh.pencheon
@@ -216,7 +256,7 @@ file safety:
216
256
  ndr_dev_support.gemspec:
217
257
  comments:
218
258
  reviewed_by: josh.pencheon
219
- safe_revision: ec3d1f3fa1d1f1f3d039587a2f31bda28062f731
259
+ safe_revision: 3e1fbd0a5931f7598a2dd3f929a7cd8366dc0e96
220
260
  test/daemon/ci_server_test.rb:
221
261
  comments:
222
262
  reviewed_by: josh.pencheon
@@ -225,6 +265,22 @@ file safety:
225
265
  comments:
226
266
  reviewed_by: timgentry
227
267
  safe_revision: 26a8907a7a8a6b0d836aa12ca0335fa40be12240
268
+ test/rake_ci/commit_cop/migration_without_structure_dump_test.rb:
269
+ comments:
270
+ reviewed_by: josh.pencheon
271
+ safe_revision: a3f5b7d3709f7e340f2f7e81dab026a63b21e3ff
272
+ test/rake_ci/commit_cop/modified_migration_test.rb:
273
+ comments:
274
+ reviewed_by: josh.pencheon
275
+ safe_revision: a3f5b7d3709f7e340f2f7e81dab026a63b21e3ff
276
+ test/rake_ci/commit_cop/renamed_migration_test.rb:
277
+ comments:
278
+ reviewed_by: josh.pencheon
279
+ safe_revision: a3f5b7d3709f7e340f2f7e81dab026a63b21e3ff
280
+ test/rake_ci/redmine/ticket_resolver_test.rb:
281
+ comments:
282
+ reviewed_by: josh.pencheon
283
+ safe_revision: 2154aa7f32e731933ff6091b8f42b2b014028a6a
228
284
  test/test_helper.rb:
229
285
  comments:
230
286
  reviewed_by: josh.pencheon
@@ -0,0 +1,115 @@
1
+ require 'minitest'
2
+ require 'rugged'
3
+ require 'ndr_dev_support/rake_ci/concerns/commit_metadata_persistable'
4
+
5
+ # The plugin needs to extend Minitest
6
+ module Minitest
7
+ # RakeCI Minitest Reporter
8
+ class RakeCIReporter < StatisticsReporter
9
+ include CommitMetadataPersistable
10
+
11
+ def report
12
+ super
13
+
14
+ hash = {
15
+ statistics: current_statistics,
16
+ # results: results,
17
+ metrics: current_metrics,
18
+ attachments: current_attachments
19
+ }
20
+
21
+ save_current_commit_data(hash)
22
+ end
23
+
24
+ def commit
25
+ return @commit if @commit
26
+
27
+ repo = Rugged::Repository.new('.')
28
+ @commit = repo.lookup(repo.head.target_id)
29
+ end
30
+
31
+ def load_current_commit_hash
32
+ load_current_commit_data
33
+ end
34
+
35
+ private
36
+
37
+ def current_statistics
38
+ @current_statistics ||= {
39
+ total_time: total_time, runs: count, assertions: assertions, failures: failures,
40
+ errors: errors, skips: skips
41
+ }
42
+ end
43
+
44
+ def current_metrics
45
+ return @current_metrics if @current_metrics
46
+
47
+ @current_metrics = []
48
+ (current_statistics.keys - %i[total_time]).each do |name|
49
+ metric = {
50
+ name: 'ci_test_count',
51
+ type: :gauge,
52
+ label_set: { name: name },
53
+ value: current_statistics[name]
54
+ }
55
+ @current_metrics << metric
56
+ io.puts metric.inspect
57
+ end
58
+ @current_metrics
59
+ end
60
+
61
+ def current_attachments
62
+ return @current_attachments if @current_attachments
63
+
64
+ @current_attachments = []
65
+ @current_attachments << failures_attachment if failures.positive?
66
+ @current_attachments << errors_attachment if errors.positive?
67
+ @current_attachments << pass_attachment if newly_passing?
68
+ @current_attachments
69
+ end
70
+
71
+ def failures_attachment
72
+ {
73
+ color: 'danger',
74
+ text: ActionController::Base.helpers.pluralize(failures, 'test failure'),
75
+ footer: 'bundle exec rake ci:minitest'
76
+ }
77
+ end
78
+
79
+ def errors_attachment
80
+ {
81
+ color: 'warning',
82
+ text: ActionController::Base.helpers.pluralize(errors, 'test error'),
83
+ footer: 'bundle exec rake ci:minitest'
84
+ }
85
+ end
86
+
87
+ def pass_attachment
88
+ {
89
+ color: 'good',
90
+ text: 'Tests now pass',
91
+ footer: 'bundle exec rake ci:minitest'
92
+ }
93
+ end
94
+
95
+ def newly_passing?
96
+ return false if failures.positive? || errors.positive?
97
+
98
+ last_commit_hash = load_last_commit_data
99
+ return false if last_commit_hash.nil?
100
+
101
+ last_commit_hash[:statistics][:failures].positive? ||
102
+ last_commit_hash[:statistics][:errors].positive?
103
+ end
104
+
105
+ def name
106
+ 'minitest'
107
+ end
108
+ end
109
+
110
+ def self.plugin_rake_ci_init(_options)
111
+ reporter << RakeCIReporter.new
112
+ end
113
+ end
114
+
115
+ Minitest.extensions << 'rake_ci'
@@ -1,6 +1,8 @@
1
1
  require 'English'
2
2
  require_relative 'stoppable'
3
+ require 'open3'
3
4
  require 'rugged'
5
+ require 'shellwords'
4
6
  require 'with_clean_rbenv'
5
7
 
6
8
  module NdrDevSupport
@@ -46,7 +48,7 @@ module NdrDevSupport
46
48
  git_checkout(MASTER_BRANCH_NAME)
47
49
 
48
50
  objectids_between_master_and_remote.each do |oid|
49
- `git rebase #{oid}`
51
+ git_rebase(oid)
50
52
 
51
53
  WithCleanRbenv.with_clean_rbenv do
52
54
  # TODO: rbenv_install
@@ -62,7 +64,19 @@ module NdrDevSupport
62
64
  end
63
65
 
64
66
  def git_checkout(oid)
65
- `git checkout #{oid}`
67
+ Open3.popen3('git', 'checkout', oid) do |_stdin, _stdout, stderr, wait_thr|
68
+ msg = stderr.read.strip
69
+ # TODO: Once https://github.com/PublicHealthEngland/ndr_dev_support/issues/27
70
+ # has been resolved, use logging instead of puts
71
+ puts msg unless msg == "Already on '#{oid}'"
72
+
73
+ process_status = wait_thr.value
74
+ raise stderr.read unless process_status.exited?
75
+ end
76
+ end
77
+
78
+ def git_rebase(oid)
79
+ `git rebase #{Shellwords.escape(oid)}`
66
80
  end
67
81
 
68
82
  def git_discard_changes
@@ -0,0 +1,64 @@
1
+ require 'active_support/core_ext/module/attribute_accessors'
2
+ require_relative 'commit_cop/migration_without_structure_dump'
3
+ require_relative 'commit_cop/modified_migration'
4
+ require_relative 'commit_cop/renamed_migration'
5
+
6
+ module NdrDevSupport
7
+ module RakeCI
8
+ # This module encapsulates commit cop logic
9
+ module CommitCop
10
+ # This defines the regular expression that identifies the path to migration files.
11
+ mattr_accessor :migration_paths
12
+ self.migration_paths =
13
+ if defined?(Rails)
14
+ Rails.application.config.paths['db/migrate'].map(&:to_s)
15
+ else
16
+ []
17
+ end
18
+
19
+ # This defines the regular expression that identifies structure dump files.
20
+ mattr_accessor :structure_dump_pattern
21
+ self.structure_dump_pattern = %r{\Adb/(structure\.sql|schema\.rb)\z}
22
+
23
+ COMMIT_COPS = [
24
+ MigrationWithoutStructureDump,
25
+ ModifiedMigration,
26
+ RenamedMigration
27
+ ].freeze
28
+
29
+ # enumerates over each delta of the commmit
30
+ def self.each_delta(commit, &block)
31
+ diffs = commit.parents.first.diff(commit)
32
+ diffs.find_similar!
33
+ diffs.each_delta(&block)
34
+ end
35
+
36
+ # converts the deltas into a simpler changes hash of filename sets representation
37
+ def self.changes(commit)
38
+ changes = { added: Set.new, deleted: Set.new, modified: Set.new, renamed: Set.new }
39
+
40
+ each_delta(commit) do |delta|
41
+ if delta.status == :renamed
42
+ changes[delta.status].add([delta.old_file[:path], delta.new_file[:path]])
43
+ else
44
+ # old_file and new_file are the same
45
+ changes[delta.status].add(delta.old_file[:path])
46
+ end
47
+ end
48
+
49
+ changes
50
+ end
51
+
52
+ # Isolates migration/structure pattern changes by resetting them after yielding
53
+ def self.with_pattern
54
+ default_migration_paths = migration_paths
55
+ default_structure_dump_pattern = structure_dump_pattern
56
+
57
+ yield
58
+
59
+ self.migration_paths = default_migration_paths
60
+ self.structure_dump_pattern = default_structure_dump_pattern
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,42 @@
1
+ require 'active_support/concern'
2
+
3
+ module NdrDevSupport
4
+ module RakeCI
5
+ module CommitCop
6
+ # Deputisable cop concern
7
+ module Deputisable
8
+ extend ActiveSupport::Concern
9
+
10
+ private
11
+
12
+ def migration_file?
13
+ proc do |file|
14
+ if file.is_a?(Array)
15
+ file.any?(&migration_file?)
16
+ else
17
+ file.start_with?(*NdrDevSupport::RakeCI::CommitCop.migration_paths) &&
18
+ file =~ /\d{14}_.*\.rb\z/
19
+ end
20
+ end
21
+ end
22
+
23
+ def unscoped_migration_file?
24
+ proc do |file|
25
+ file.start_with?(*NdrDevSupport::RakeCI::CommitCop.migration_paths) &&
26
+ file =~ /\d{14}_[^\.]*\.rb\z/
27
+ end
28
+ end
29
+
30
+ def structure_dump_file?
31
+ proc { |file| file =~ NdrDevSupport::RakeCI::CommitCop.structure_dump_pattern }
32
+ end
33
+
34
+ def attachment(severity, title, text)
35
+ {
36
+ color: severity.to_s, title: title, text: text, mrkdwn_in: ['text']
37
+ }
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'concerns/deputisable'
2
+
3
+ module NdrDevSupport
4
+ module RakeCI
5
+ module CommitCop
6
+ # This cop checks for new migrations with no accompanying structure dump
7
+ class MigrationWithoutStructureDump
8
+ include Deputisable
9
+
10
+ def check(changes)
11
+ return unless changes[:added].any?(&unscoped_migration_file?) &&
12
+ changes[:modified].none?(&structure_dump_file?)
13
+
14
+ attachment(:danger,
15
+ 'No structure file committed',
16
+ 'Migration(s) were added with no accompanying structure file(s)')
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ require_relative 'concerns/deputisable'
2
+
3
+ module NdrDevSupport
4
+ module RakeCI
5
+ module CommitCop
6
+ # This cop checks for modified migrations
7
+ class ModifiedMigration
8
+ include Deputisable
9
+
10
+ def check(changes)
11
+ return if changes[:modified].none?(&migration_file?)
12
+
13
+ attachment(:danger,
14
+ 'Modified Migration',
15
+ 'Migrations should not be modified. Create another migration.')
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'concerns/deputisable'
2
+
3
+ module NdrDevSupport
4
+ module RakeCI
5
+ module CommitCop
6
+ # This cop checks for renamed migrations that change timestamp
7
+ class RenamedMigration
8
+ include Deputisable
9
+
10
+ def check(changes)
11
+ renamed_migrations = changes[:renamed].select(&migration_file?)
12
+ return if renamed_migrations.all? do |old_file, new_file|
13
+ File.basename(old_file)[0, 14] == File.basename(new_file)[0, 14]
14
+ end
15
+
16
+ attachment(:danger,
17
+ 'Renamed Migration',
18
+ 'Migrations should not change timestamp')
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,101 @@
1
+ require 'json'
2
+ require 'net/http'
3
+
4
+ module NdrDevSupport
5
+ module RakeCI
6
+ module Redmine
7
+ # This class encapsulates Redmine ticket logic
8
+ class TicketResolver
9
+ CLOSE_REGEX =
10
+ /
11
+ (
12
+ (?:close[sd]?) # close, closes, closed
13
+ | (?:resolve[sd]?) # resolve, resolves, resolved
14
+ | (?:fix(?:e[sd])?) # fix, fixes, fixed
15
+ )
16
+ /ix
17
+
18
+ MEGA_REGEX =
19
+ /
20
+ #{CLOSE_REGEX}
21
+ | (relate[sd]\sto) # relates to, related to
22
+ | (?:
23
+ \[? # optional square bracket
24
+ \#(\d+) # ticket number with preceding hash character
25
+ (?:\#note-\d+)? # optional note reference, ignored
26
+ \]? # optional square bracket
27
+ \b # word boundary
28
+ )
29
+ /ix
30
+
31
+ def initialize(api_key, hostname)
32
+ @headers = {
33
+ 'X-Redmine-API-Key' => api_key,
34
+ 'Content-Type' => 'application/json'
35
+ }
36
+ @hostname = hostname
37
+ end
38
+
39
+ def process_commit(user, revision, message)
40
+ resolved_tickets = []
41
+
42
+ each_ticket_from(message) do |ticket, resolved|
43
+ update_ticket(message, user, revision, ticket, resolved)
44
+
45
+ resolved_tickets << ticket if resolved
46
+ end
47
+
48
+ resolved_tickets
49
+ end
50
+
51
+ def each_ticket_from(message, &block)
52
+ return enum_for(:each_ticket_from, message) unless block
53
+
54
+ key_words = message.scan(MEGA_REGEX).flatten.compact
55
+ action_groups = key_words.slice_when { |_l, r| r.to_i.to_s != r }.to_a
56
+
57
+ action_groups.each do |group|
58
+ resolved = group.any? { |word| word =~ CLOSE_REGEX }
59
+ tickets = group.select { |word| word.to_i.to_s == word }.uniq
60
+
61
+ tickets.each { |ticket| yield(ticket, resolved) }
62
+ end
63
+ end
64
+
65
+ def update_payload(message, user, revision, ticket_closed, resolved)
66
+ payload = {
67
+ notes: "_#{resolved ? 'Resolved' : 'Referenced'} by #{user} in #{revision}_:" \
68
+ "#{resolved ? message.gsub(CLOSE_REGEX, '+\1+') : message}"
69
+ }
70
+
71
+ payload[:status_id] = 3 if resolved && !ticket_closed
72
+ payload
73
+ end
74
+
75
+ private
76
+
77
+ # Connect lazily
78
+ def http
79
+ return @http if @http
80
+ @http = Net::HTTP.new(@hostname, 443)
81
+ @http.use_ssl = true
82
+ @http
83
+ end
84
+
85
+ def update_ticket(message, user, revision, ticket, resolved)
86
+ payload = update_payload(message, user, revision, ticket_closed?(ticket), resolved)
87
+
88
+ http.send_request('PUT',
89
+ "/issues/#{ticket.to_i}.json",
90
+ JSON.dump(issue: payload),
91
+ @headers)
92
+ end
93
+
94
+ def ticket_closed?(ticket)
95
+ response = http.send_request('GET', "/issues/#{ticket.to_i}.json", nil, @headers)
96
+ JSON.parse(response.body)['issue']['status']['id'] == 5
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -1,11 +1,14 @@
1
1
  load 'tasks/audit_code.rake'
2
2
  load 'tasks/ci/brakeman.rake'
3
3
  load 'tasks/ci/bundle_audit.rake'
4
+ load 'tasks/ci/commit_cop.rake'
4
5
  load 'tasks/ci/dependencies.rake'
5
6
  load 'tasks/ci/housekeep.rake'
6
7
  load 'tasks/ci/linguist.rake'
8
+ load 'tasks/ci/minitest.rake'
7
9
  load 'tasks/ci/notes.rake'
8
10
  load 'tasks/ci/prometheus.rake'
11
+ load 'tasks/ci/redmine.rake'
9
12
  load 'tasks/ci/rugged.rake'
10
13
  load 'tasks/ci/server.rake'
11
14
  load 'tasks/ci/simplecov.rake'
@@ -2,5 +2,5 @@
2
2
  # This defines the NdrDevSupport version. If you change it, rebuild and commit the gem.
3
3
  # Use "rake build" to build the gem, see rake -T for all bundler rake tasks (and our own).
4
4
  module NdrDevSupport
5
- VERSION = '4.0.0'.freeze
5
+ VERSION = '4.1.0'.freeze
6
6
  end
@@ -0,0 +1,28 @@
1
+ namespace :ci do
2
+ desc 'commit_cop'
3
+ task commit_cop: 'ci:rugged:setup' do |t|
4
+ # Usage: bin/rake ci:commit_cop
5
+ require 'ndr_dev_support/rake_ci/commit_cop'
6
+
7
+ @attachments ||= []
8
+ changes = NdrDevSupport::RakeCI::CommitCop.changes(@commit)
9
+
10
+ NdrDevSupport::RakeCI::CommitCop::COMMIT_COPS.each do |klass|
11
+ attachment = klass.new.check(changes)
12
+ next if attachment.nil?
13
+
14
+ @attachments << attachment.merge(footer: "bundle exec rake #{t.name}")
15
+ puts attachment.to_yaml
16
+ end
17
+ end
18
+
19
+ desc 'changes'
20
+ task changes: 'ci:rugged:setup' do
21
+ # Usage: bin/rake ci:changes
22
+ require 'ndr_dev_support/rake_ci/commit_cop'
23
+
24
+ changes = NdrDevSupport::RakeCI::CommitCop.changes(@commit)
25
+
26
+ puts changes.inspect
27
+ end
28
+ end
@@ -0,0 +1,64 @@
1
+ namespace :ci do
2
+ desc 'Test the system'
3
+ task :minitest do
4
+ raise 'Your test command must be the rake default' unless Rake::Task.task_defined?('default')
5
+
6
+ # Run the tests
7
+ test_cmd = 'bundle exec rake ci:minitest:setup ci:simplecov:setup default'
8
+ test_cmd += ' 2>&1 >/dev/null' if ENV['RAKECI_HEADLESS']
9
+ system test_cmd
10
+
11
+ Rake::Task['ci:minitest:process'].invoke
12
+
13
+ next if ENV['RAKECI_HEADLESS']
14
+ puts @metrics.inspect
15
+ puts @attachments.inspect
16
+ end
17
+
18
+ namespace :minitest do
19
+ desc 'setup'
20
+ task :setup do
21
+ # Load the RakeCI reporter:
22
+ require 'minitest/rake_ci_reporter'
23
+
24
+ # Ensure spawned processes do the same:
25
+ ENV['RUBYOPT'] += ' -rminitest/rake_ci_reporter'
26
+ end
27
+
28
+ desc 'process'
29
+ task :process do
30
+ require 'minitest/rake_ci_reporter'
31
+
32
+ @attachments ||= []
33
+ @metrics ||= []
34
+
35
+ rake_ci_reporter = Minitest::RakeCIReporter.new
36
+ hash = rake_ci_reporter.load_current_commit_hash
37
+ if hash.nil?
38
+ # Tests didn't run properly
39
+ attachment = {
40
+ color: 'danger',
41
+ title: 'Testing Error',
42
+ text: "Minitest didn't run properly",
43
+ footer: 'bundle exec rake ci:minitest',
44
+ mrkdwn_in: ['text']
45
+ }
46
+ @attachments << attachment
47
+
48
+ next
49
+ end
50
+
51
+ # Test(s) ran
52
+ Rake::Task['ci:simplecov:process'].invoke
53
+
54
+ if hash[:statistics][:failures].zero? && hash[:statistics][:errors].zero? &&
55
+ Rake::Task.task_defined?('ci:redmine:update_tickets')
56
+ # Test(s) passing
57
+ Rake::Task['ci:redmine:update_tickets'].invoke
58
+ end
59
+
60
+ @attachments.concat(hash[:attachments])
61
+ @metrics.concat(hash[:metrics])
62
+ end
63
+ end
64
+ end
@@ -2,6 +2,7 @@ namespace :ci do
2
2
  namespace :prometheus do
3
3
  desc 'Set up Prometheus'
4
4
  task :setup do
5
+ require 'highline/import'
5
6
  @metrics = []
6
7
 
7
8
  ENV['PROMETHEUS_PUSHGATEWAY'] ||= ask('Prometheus pushgateway (host:port): ')
@@ -0,0 +1,41 @@
1
+ namespace :ci do
2
+ namespace :redmine do
3
+ desc 'Set up Redmine'
4
+ task :setup do
5
+ ENV['REDMINE_HOSTNAME'] ||= ask('Redmine URL: ')
6
+ ENV['REDMINE_HOSTNAME'] = nil if ENV['REDMINE_HOSTNAME'] == ''
7
+
8
+ ENV['REDMINE_API_KEY'] ||= ask('Redmine API Key: ') { |q| q.echo = '*' }
9
+ ENV['REDMINE_API_KEY'] = nil if ENV['REDMINE_API_KEY'] == ''
10
+ end
11
+
12
+ desc 'Update Redmine tickets'
13
+ task update_tickets: ['ci:rugged:setup', 'ci:redmine:setup'] do
14
+ api_key = ENV['REDMINE_API_KEY']
15
+ hostname = ENV['REDMINE_HOSTNAME']
16
+ next if api_key.nil? || hostname.nil?
17
+
18
+ require 'ndr_dev_support/rake_ci/redmine/ticket_resolver'
19
+
20
+ ticket_resolver = NdrDevSupport::RakeCI::Redmine::TicketResolver.new(api_key, hostname)
21
+ resolved_tickets = ticket_resolver.process_commit(@commit.author[:name],
22
+ @friendly_revision_name,
23
+ @commit.message)
24
+
25
+ next if resolved_tickets.empty?
26
+ resolved_tickets.map! { |ticket_id| "https://#{hostname}/issues/#{ticket_id}" }
27
+
28
+ issue_s = resolved_tickets.count == 1 ? 'issue' : 'issues'
29
+ attachment = {
30
+ color: 'good',
31
+ title: "#{issue_s.capitalize} Resolved",
32
+ text: "Tests pass, so #{issue_s} #{resolved_tickets.join(', ')}" \
33
+ " #{resolved_tickets.count == 1 ? 'has' : 'have'} been resolved",
34
+ footer: 'bundle exec rake ci:minitest',
35
+ mrkdwn_in: ['text']
36
+ }
37
+ @attachments ||= []
38
+ @attachments << attachment
39
+ end
40
+ end
41
+ end
@@ -2,6 +2,12 @@ namespace :ci do
2
2
  # Stub ci:all rake task
3
3
  task :all
4
4
 
5
+ desc 'publish'
6
+ task publish: [
7
+ 'ci:slack:publish',
8
+ 'ci:prometheus:publish'
9
+ ]
10
+
5
11
  desc 'Runs the Rake CI server'
6
12
  task :server do
7
13
  require 'ndr_dev_support/daemon/ci_server'
@@ -2,6 +2,7 @@ namespace :ci do
2
2
  namespace :slack do
3
3
  desc 'Set up Slack'
4
4
  task :setup do
5
+ require 'highline/import'
5
6
  @attachments = []
6
7
 
7
8
  ENV['SLACK_WEBHOOK_URL'] ||= ask('Slack Webhook URL: ')
@@ -45,6 +45,7 @@ Gem::Specification.new do |spec|
45
45
  spec.add_dependency 'github-linguist'
46
46
  spec.add_dependency 'prometheus-client'
47
47
  spec.add_dependency 'rugged'
48
+ spec.add_dependency 'simplecov'
48
49
  spec.add_dependency 'with_clean_rbenv'
49
50
 
50
51
  # Deployment dependencies:
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ndr_dev_support
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - NCRS Development Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-10-17 00:00:00.000000000 Z
11
+ date: 2018-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -234,6 +234,20 @@ dependencies:
234
234
  - - ">="
235
235
  - !ruby/object:Gem::Version
236
236
  version: '0'
237
+ - !ruby/object:Gem::Dependency
238
+ name: simplecov
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ type: :runtime
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ version: '0'
237
251
  - !ruby/object:Gem::Dependency
238
252
  name: with_clean_rbenv
239
253
  requirement: !ruby/object:Gem::Requirement
@@ -335,6 +349,7 @@ files:
335
349
  - bin/console
336
350
  - bin/setup
337
351
  - code_safety.yml
352
+ - lib/minitest/rake_ci_reporter.rb
338
353
  - lib/ndr_dev_support.rb
339
354
  - lib/ndr_dev_support/capistrano/assets.rb
340
355
  - lib/ndr_dev_support/capistrano/ndr_model.rb
@@ -354,7 +369,13 @@ files:
354
369
  - lib/ndr_dev_support/integration_testing/drivers/switchable.rb
355
370
  - lib/ndr_dev_support/integration_testing/dsl.rb
356
371
  - lib/ndr_dev_support/rake_ci/brakeman_helper.rb
372
+ - lib/ndr_dev_support/rake_ci/commit_cop.rb
373
+ - lib/ndr_dev_support/rake_ci/commit_cop/concerns/deputisable.rb
374
+ - lib/ndr_dev_support/rake_ci/commit_cop/migration_without_structure_dump.rb
375
+ - lib/ndr_dev_support/rake_ci/commit_cop/modified_migration.rb
376
+ - lib/ndr_dev_support/rake_ci/commit_cop/renamed_migration.rb
357
377
  - lib/ndr_dev_support/rake_ci/concerns/commit_metadata_persistable.rb
378
+ - lib/ndr_dev_support/rake_ci/redmine/ticket_resolver.rb
358
379
  - lib/ndr_dev_support/rake_ci/simple_cov_helper.rb
359
380
  - lib/ndr_dev_support/rubocop/executor.rb
360
381
  - lib/ndr_dev_support/rubocop/range_augmenter.rb
@@ -366,11 +387,14 @@ files:
366
387
  - lib/tasks/audit_code.rake
367
388
  - lib/tasks/ci/brakeman.rake
368
389
  - lib/tasks/ci/bundle_audit.rake
390
+ - lib/tasks/ci/commit_cop.rake
369
391
  - lib/tasks/ci/dependencies.rake
370
392
  - lib/tasks/ci/housekeep.rake
371
393
  - lib/tasks/ci/linguist.rake
394
+ - lib/tasks/ci/minitest.rake
372
395
  - lib/tasks/ci/notes.rake
373
396
  - lib/tasks/ci/prometheus.rake
397
+ - lib/tasks/ci/redmine.rake
374
398
  - lib/tasks/ci/rugged.rake
375
399
  - lib/tasks/ci/server.rake
376
400
  - lib/tasks/ci/simplecov.rake