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
@@ -0,0 +1,54 @@
|
|
1
|
+
namespace :ci do
|
2
|
+
desc 'Brakeman'
|
3
|
+
task brakeman: 'ci:rugged:setup' do
|
4
|
+
next unless defined?(Rails)
|
5
|
+
|
6
|
+
require 'ndr_dev_support/rake_ci/brakeman_helper'
|
7
|
+
# Usage: bundle exec rake ci:brakeman
|
8
|
+
|
9
|
+
@metrics ||= []
|
10
|
+
@attachments ||= []
|
11
|
+
|
12
|
+
brakeman = NdrDevSupport::RakeCI::BrakemanHelper.new
|
13
|
+
brakeman.commit = @commit
|
14
|
+
brakeman.run
|
15
|
+
|
16
|
+
Brakeman::Warning::TEXT_CONFIDENCE.each do |confidence, text|
|
17
|
+
metric = {
|
18
|
+
name: 'brakeman_warnings',
|
19
|
+
type: :gauge,
|
20
|
+
label_set: { confidence: text },
|
21
|
+
value: brakeman.warning_counts_by_confidence[confidence] || 0
|
22
|
+
}
|
23
|
+
@metrics << metric
|
24
|
+
puts metric.inspect
|
25
|
+
end
|
26
|
+
|
27
|
+
unless brakeman.new_fingerprints.empty?
|
28
|
+
# new warnings found
|
29
|
+
attachment = {
|
30
|
+
color: 'danger',
|
31
|
+
title: "#{brakeman.new_fingerprints.size} new Brakeman warning(s) :rotating_light:",
|
32
|
+
text: '_Brakeman_ warning fingerprint(s):' \
|
33
|
+
"```#{brakeman.new_fingerprints.to_a.join("\n")}```",
|
34
|
+
footer: 'bundle exec rake ci:brakeman',
|
35
|
+
mrkdwn_in: ['text']
|
36
|
+
}
|
37
|
+
@attachments << attachment
|
38
|
+
puts attachment.inspect
|
39
|
+
end
|
40
|
+
|
41
|
+
unless brakeman.old_fingerprints.empty?
|
42
|
+
# old warnings missing
|
43
|
+
attachment = {
|
44
|
+
color: 'good',
|
45
|
+
title: "#{brakeman.old_fingerprints.size} Brakeman warning(s) resolved :+1:",
|
46
|
+
footer: 'bundle exec rake ci:brakeman'
|
47
|
+
}
|
48
|
+
@attachments << attachment
|
49
|
+
puts attachment.inspect
|
50
|
+
end
|
51
|
+
|
52
|
+
brakeman.save_current_fingerprints
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
namespace :ci do
|
2
|
+
# Checks bundle audit and converts advisories into "attachments"
|
3
|
+
# Usage: bin/rake ci:bundle_audit
|
4
|
+
desc 'Patch-level verification for Bundler'
|
5
|
+
task :bundle_audit do
|
6
|
+
require 'English'
|
7
|
+
|
8
|
+
# Update ruby-advisory-db
|
9
|
+
`bundle audit update`
|
10
|
+
# Check for insecure dependencies
|
11
|
+
output = `bundle audit check`
|
12
|
+
next if $CHILD_STATUS.exitstatus.zero?
|
13
|
+
|
14
|
+
output.split("\n\n").each do |advisory|
|
15
|
+
lines = advisory.split("\n")
|
16
|
+
next if lines.count == 1
|
17
|
+
|
18
|
+
hash = {}
|
19
|
+
lines.each do |line|
|
20
|
+
matchdata = line.match(/\A([^:]+):\s(.*)\z/)
|
21
|
+
next if matchdata.nil?
|
22
|
+
|
23
|
+
hash[matchdata[1]] = matchdata[2]
|
24
|
+
end
|
25
|
+
title = hash.delete('Title')
|
26
|
+
url = hash.delete('URL')
|
27
|
+
solution = hash.delete('Solution')
|
28
|
+
criticality = hash['Criticality']
|
29
|
+
|
30
|
+
attachment = {
|
31
|
+
color: criticality == 'High' ? 'danger' : 'warning',
|
32
|
+
fallback: title,
|
33
|
+
title: title,
|
34
|
+
title_link: url,
|
35
|
+
text: solution,
|
36
|
+
fields: hash.map { |key, value| { title: key, value: value, short: true } },
|
37
|
+
footer: 'bundle exec rake ci:bundle_audit'
|
38
|
+
}
|
39
|
+
|
40
|
+
@attachments ||= []
|
41
|
+
@attachments << attachment
|
42
|
+
puts attachment.inspect
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
namespace :ci do
|
2
|
+
desc 'Install bundled gems'
|
3
|
+
task :bundle_install do
|
4
|
+
require 'English'
|
5
|
+
|
6
|
+
# `gem install bundler`
|
7
|
+
`bundle install --local`
|
8
|
+
next if $CHILD_STATUS.exitstatus.zero?
|
9
|
+
|
10
|
+
attachment = {
|
11
|
+
color: 'danger',
|
12
|
+
fallback: 'Failure running bundle install --local',
|
13
|
+
text: 'Failure running `bundle install --local`',
|
14
|
+
footer: 'bundle exec rake ci:bundle_install'
|
15
|
+
}
|
16
|
+
|
17
|
+
@attachments ||= []
|
18
|
+
@attachments << attachment
|
19
|
+
puts attachment.inspect
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
namespace :ci do
|
2
|
+
desc 'Housekeep cached and temporary files'
|
3
|
+
task :housekeep do
|
4
|
+
# If running in a Rails project we invoke the standard tmp/ and log/ clearing tasks
|
5
|
+
Rake::Task['log:clear'].invoke if Rake::Task.task_defined?('log:clear')
|
6
|
+
Rake::Task['tmp:clear'].invoke if Rake::Task.task_defined?('tmp:clear')
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
namespace :ci do
|
2
|
+
desc 'linguist'
|
3
|
+
task linguist: 'ci:rugged:setup' do
|
4
|
+
require 'linguist'
|
5
|
+
|
6
|
+
def linguist_loc_metrics(language, line_count, percent)
|
7
|
+
metrics = [
|
8
|
+
{
|
9
|
+
name: 'linguist_code_count', type: :gauge,
|
10
|
+
label_set: { language: language }, value: line_count
|
11
|
+
},
|
12
|
+
{
|
13
|
+
name: 'linguist_code_percent', type: :gauge,
|
14
|
+
label_set: { language: language }, value: percent.round(1)
|
15
|
+
}
|
16
|
+
]
|
17
|
+
puts metrics.inspect
|
18
|
+
metrics
|
19
|
+
end
|
20
|
+
|
21
|
+
@metrics ||= []
|
22
|
+
|
23
|
+
project = Linguist::Repository.new(@repo, @repo.head.target_id)
|
24
|
+
total_line_count = project.languages.values.reduce(:+)
|
25
|
+
other_line_count = 0
|
26
|
+
|
27
|
+
project.languages.each do |language, line_count|
|
28
|
+
percent = 100.0 * line_count / total_line_count
|
29
|
+
|
30
|
+
if percent > 1
|
31
|
+
@metrics.concat(linguist_loc_metrics(language, line_count, percent))
|
32
|
+
else
|
33
|
+
other_line_count += line_count
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
if other_line_count > 0
|
38
|
+
other_percent = 100.0 * other_line_count / total_line_count
|
39
|
+
@metrics.concat(linguist_loc_metrics('Other', other_line_count, other_percent))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
namespace :ci do
|
2
|
+
# Runs the Rails rake notes task (if using Rails) and converts annotation counts into "metrics"
|
3
|
+
# Usage: bundle exec rake ci:notes
|
4
|
+
desc 'Count notes and other annotations'
|
5
|
+
task :notes do
|
6
|
+
next unless Rake::Task.task_defined?('notes')
|
7
|
+
|
8
|
+
hash = {}
|
9
|
+
`bundle exec rake notes | grep "\\["`.split(/\n/).map do |line|
|
10
|
+
matchdata = line.match(/\[\s*\d+\] \[([^\]]+)\]/)
|
11
|
+
annotation = matchdata[1]
|
12
|
+
hash[annotation] ||= 0
|
13
|
+
hash[annotation] += 1
|
14
|
+
end
|
15
|
+
|
16
|
+
hash.each do |annotation, count|
|
17
|
+
metric = {
|
18
|
+
name: 'annotation_count',
|
19
|
+
type: :gauge,
|
20
|
+
label_set: {
|
21
|
+
annotation: annotation
|
22
|
+
},
|
23
|
+
value: count
|
24
|
+
}
|
25
|
+
@metrics ||= []
|
26
|
+
@metrics << metric
|
27
|
+
puts metric.inspect
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
namespace :ci do
|
2
|
+
namespace :prometheus do
|
3
|
+
desc 'Set up Prometheus'
|
4
|
+
task :setup do
|
5
|
+
@metrics = []
|
6
|
+
|
7
|
+
ENV['PROMETHEUS_PUSHGATEWAY'] ||= ask('Prometheus pushgateway (host:port): ')
|
8
|
+
ENV['PROMETHEUS_PUSHGATEWAY'] = nil if ENV['PROMETHEUS_PUSHGATEWAY'] == ''
|
9
|
+
|
10
|
+
ENV['PROMETHEUS_PROJECTNAME'] ||= ask('Prometheus project name: ')
|
11
|
+
ENV['PROMETHEUS_PROJECTNAME'] = nil if ENV['PROMETHEUS_PROJECTNAME'] == ''
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Push Prometheus stats'
|
15
|
+
task publish: :setup do
|
16
|
+
next if @metrics.empty? || ENV['PROMETHEUS_PUSHGATEWAY'].nil?
|
17
|
+
|
18
|
+
require 'prometheus/client'
|
19
|
+
require 'prometheus/client/push'
|
20
|
+
|
21
|
+
# returns a default registry
|
22
|
+
prometheus = Prometheus::Client.registry
|
23
|
+
|
24
|
+
@metrics.each do |metric|
|
25
|
+
name = "ci_#{metric[:name]}".to_sym
|
26
|
+
# TODO: Add :docstring where required
|
27
|
+
docstring = metric[:docstring] || 'TODO'
|
28
|
+
label_set = metric[:label_set] || {}
|
29
|
+
value = metric[:value]
|
30
|
+
|
31
|
+
case metric[:type]
|
32
|
+
when :gauge
|
33
|
+
gauge =
|
34
|
+
if prometheus.exist?(name)
|
35
|
+
prometheus.get(name)
|
36
|
+
else
|
37
|
+
prometheus.gauge(name, docstring, project: ENV['PROMETHEUS_PROJECTNAME'])
|
38
|
+
end
|
39
|
+
gauge.set(label_set, value)
|
40
|
+
else
|
41
|
+
raise "Unknown metric type (#{metric.inspect})"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
Prometheus::Client::Push.new(
|
46
|
+
'rake-ci-job', nil, ENV['PROMETHEUS_PUSHGATEWAY']
|
47
|
+
).add(prometheus)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
namespace :ci do
|
2
|
+
namespace :rugged do
|
3
|
+
desc 'Setup Rugged, get the current commit and friendly version of the revision name'
|
4
|
+
task :setup do
|
5
|
+
require 'ndr_dev_support/daemon/ci_server'
|
6
|
+
|
7
|
+
@repo = Rugged::Repository.new('.')
|
8
|
+
@commit = @repo.lookup(@repo.head.target_id)
|
9
|
+
@friendly_revision_name = NdrDevSupport::Daemon::CIServer.friendly_revision_name(@commit)
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'Show details of the commit and make it the first attachment'
|
13
|
+
task commit_details: :setup do
|
14
|
+
fields = [
|
15
|
+
{ title: 'Author', value: @commit.author[:name], short: true },
|
16
|
+
{ title: 'Revision', value: @friendly_revision_name, short: true }
|
17
|
+
]
|
18
|
+
|
19
|
+
if File.exist?('.ruby-version')
|
20
|
+
fields << {
|
21
|
+
title: 'Target Ruby Version',
|
22
|
+
value: File.read('.ruby-version').chomp,
|
23
|
+
short: true
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
attachment = {
|
28
|
+
fallback: @commit.message,
|
29
|
+
text: @commit.message,
|
30
|
+
fields: fields,
|
31
|
+
ts: @commit.author[:time].to_i
|
32
|
+
}
|
33
|
+
|
34
|
+
@attachments ||= []
|
35
|
+
@attachments.unshift(attachment)
|
36
|
+
puts attachment.inspect
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
namespace :ci do
|
2
|
+
namespace :simplecov do
|
3
|
+
desc 'setup'
|
4
|
+
task :setup do
|
5
|
+
# Usage: bundle exec rake ci:simplecov:setup test
|
6
|
+
require 'simplecov'
|
7
|
+
require 'ndr_dev_support/rake_ci/simple_cov_helper'
|
8
|
+
|
9
|
+
SimpleCov.at_exit do
|
10
|
+
result = SimpleCov.result
|
11
|
+
result.format! if ENV['RAKECI_HEADLESS'].nil?
|
12
|
+
NdrDevSupport::RakeCI::SimpleCovHelper.new.save_current_result(result)
|
13
|
+
end
|
14
|
+
|
15
|
+
SimpleCov.start
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'process'
|
19
|
+
task :process do
|
20
|
+
require 'simplecov'
|
21
|
+
require 'ndr_dev_support/rake_ci/simple_cov_helper'
|
22
|
+
|
23
|
+
helper = NdrDevSupport::RakeCI::SimpleCovHelper.new
|
24
|
+
result = helper.load_current_result
|
25
|
+
next if result.nil?
|
26
|
+
|
27
|
+
metrics = [
|
28
|
+
{ name: 'simplecov_covered_percent', type: :gauge, value: result.covered_percent },
|
29
|
+
{ name: 'simplecov_covered_lines', type: :gauge, value: result.covered_lines },
|
30
|
+
{ name: 'simplecov_total_lines', type: :gauge, value: result.total_lines }
|
31
|
+
]
|
32
|
+
@metrics ||= []
|
33
|
+
@metrics.concat(metrics)
|
34
|
+
puts metrics.inspect
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
namespace :ci do
|
2
|
+
namespace :slack do
|
3
|
+
desc 'Set up Slack'
|
4
|
+
task :setup do
|
5
|
+
@attachments = []
|
6
|
+
|
7
|
+
ENV['SLACK_WEBHOOK_URL'] ||= ask('Slack Webhook URL: ')
|
8
|
+
ENV['SLACK_WEBHOOK_URL'] = nil if ENV['SLACK_WEBHOOK_URL'] == ''
|
9
|
+
|
10
|
+
ENV['SLACK_CHANNEL'] ||= ask('Slack Channel: ')
|
11
|
+
ENV['SLACK_CHANNEL'] = nil if ENV['SLACK_CHANNEL'] == ''
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'publish'
|
15
|
+
task publish: :setup do
|
16
|
+
next if @attachments.empty? || ENV['SLACK_WEBHOOK_URL'].nil?
|
17
|
+
|
18
|
+
require 'ndr_dev_support/slack_message_publisher'
|
19
|
+
|
20
|
+
# We have attachments so prepend them with basic commit details
|
21
|
+
Rake::Task['ci:rugged:commit_details'].invoke
|
22
|
+
|
23
|
+
slack_publisher = NdrDevSupport::SlackMessagePublisher.new(ENV['SLACK_WEBHOOK_URL'],
|
24
|
+
username: 'Rake CI',
|
25
|
+
icon_emoji: ':robot_face:',
|
26
|
+
channel: ENV['SLACK_CHANNEL'])
|
27
|
+
slack_publisher.post(attachments: @attachments)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
namespace :ci do
|
2
|
+
# Runs the Rails rake stats task (if using Rails) and converts counts into "metrics"
|
3
|
+
# Usage: bundle exec rake ci:stats
|
4
|
+
desc 'stats'
|
5
|
+
task :stats do
|
6
|
+
next unless Rake::Task.task_defined?('stats')
|
7
|
+
|
8
|
+
@metrics ||= []
|
9
|
+
|
10
|
+
summary_line = `bundle exec rake stats 2>/dev/null | tail -n 2 | head -n 1`
|
11
|
+
matchdata = summary_line.match(/Code LOC: (\d+)\b\s+Test LOC: (\d+)\b/)
|
12
|
+
code_loc = matchdata[1].to_i
|
13
|
+
test_loc = matchdata[2].to_i
|
14
|
+
test_ratio = 100.0 * test_loc / code_loc
|
15
|
+
|
16
|
+
metrics = [
|
17
|
+
{ name: 'stats_code_loc', type: :gauge, value: code_loc },
|
18
|
+
{ name: 'stats_test_loc', type: :gauge, value: test_loc },
|
19
|
+
{ name: 'stats_test_ratio', type: :gauge, value: test_ratio }
|
20
|
+
]
|
21
|
+
@metrics.concat(metrics)
|
22
|
+
puts metrics.inspect
|
23
|
+
end
|
24
|
+
end
|
data/ndr_dev_support.gemspec
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# coding: utf-8
|
2
1
|
lib = File.expand_path('../lib', __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'ndr_dev_support/version'
|
@@ -22,17 +21,30 @@ Gem::Specification.new do |spec|
|
|
22
21
|
|
23
22
|
spec.add_dependency 'pry'
|
24
23
|
|
24
|
+
# Audit dependencies:
|
25
|
+
spec.add_dependency 'highline', '>= 1.6.0'
|
26
|
+
|
25
27
|
# Rubocop dependencies:
|
26
|
-
spec.add_dependency 'rubocop', '0.52.1'
|
27
28
|
spec.add_dependency 'parser'
|
28
29
|
spec.add_dependency 'rainbow'
|
30
|
+
spec.add_dependency 'rubocop', '0.52.1'
|
29
31
|
|
30
32
|
# Integration test dependencies:
|
31
33
|
spec.add_dependency 'capybara'
|
32
34
|
spec.add_dependency 'capybara-screenshot'
|
35
|
+
spec.add_dependency 'chromedriver-helper'
|
33
36
|
spec.add_dependency 'poltergeist', '>= 1.8.0'
|
37
|
+
spec.add_dependency 'selenium-webdriver'
|
38
|
+
|
39
|
+
# CI server dependencies:
|
40
|
+
spec.add_dependency 'activesupport', '< 6.0.0'
|
41
|
+
spec.add_dependency 'brakeman', '>= 4.2.0'
|
42
|
+
spec.add_dependency 'bundler-audit'
|
43
|
+
spec.add_dependency 'github-linguist'
|
44
|
+
spec.add_dependency 'prometheus-client'
|
45
|
+
spec.add_dependency 'rugged'
|
34
46
|
|
35
47
|
spec.add_development_dependency 'bundler', '~> 1.7'
|
48
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
36
49
|
spec.add_development_dependency 'rake', '~> 10.0'
|
37
|
-
spec.add_development_dependency "minitest", "~> 5.0"
|
38
50
|
end
|