Repository URL to install this package:
|
Version:
0.1.2 ▾
|
# frozen_string_literal: true
module Teak
module Dev
# Class for setting up our Rake tasks.
class Tasks # rubocop:disable Metrics/ClassLength
include Rake::DSL if defined? Rake::DSL
attr_reader :service
def self.install(opts)
new(opts[:service]).install
end
def initialize(service)
@service = service
@rubocop_todo_before = nil
end
def install
install_lint
install_report
install_rubocop
install_terraform
end
private
def install_lint # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
lint_tasks = %w[lint:rubocop lint:bundler_audit]
namespace :lint do
desc 'Run Rubocop.'
task :rubocop do
# N.B. Not using `RuboCop::RakeTask.new` because it's not available during image builds, which
# was causing issues since we _do_ need rake tasks available.
sh 'bundle exec rubocop'
end
has_reek = true
begin
require 'reek/version'
rescue LoadError
has_reek = false
end
if has_reek
lint_tasks << 'lint:reek'
desc 'Run Reek.'
task :reek do
sh 'bundle exec reek'
end
end
desc 'Run bundler-audit.'
task :bundler_audit do
sh 'bundle exec bundle-audit check --update'
end
end
desc 'Run all lint tasks.'
task lint: lint_tasks
namespace :fix do
desc 'Run Rubocop with the autocorrect option. This will only apply fixes deemed safe.'
task :rubocop do
# N.B. Not using `RuboCop::RakeTask.new` because it's not available during image builds, which
# was causing issues since we _do_ need rake tasks available.
sh 'bundle exec rubocop --autocorrect'
end
end
desc 'Run linters in fix mode, where appropriate.'
task fix: %w[
fix:rubocop
]
end
def install_report # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
has_librato = true
begin
require 'librato/metrics'
rescue LoadError
has_librato = false
end
return unless has_librato
namespace :report do
desc 'Report count of unresolved Rubocop offenses (ignoring TODO file) to Librato.'
task :rubocop do
with_no_rubocop_todos do
raw_output = `bundle exec rubocop --format=offenses 2>/dev/null | grep -v =`
puts raw_output
total_line = raw_output.split("\n").find { |line| line.include?('Total in') }
total = total_line.split(/\s+/).first.to_i
report_for_branch("rubocop.#{service}.offenses", total)
end
end
desc 'Report Ruby code coverage to Librato'
task :coverage do
require 'simplecov'
SimpleCov.collate(Dir['/tmp/coverage/**/.resultset.json'])
total_covered_percent = SimpleCov.result.coverage_statistics[:line].percent
puts "Total coverage: #{total_covered_percent}%"
report_for_branch("coverage.#{service}.rspec", total_covered_percent)
end
end
end
def with_no_rubocop_todos
if File.exist?('.rubocop_todo.yml')
@rubocop_todo_before = File.read('.rubocop_todo.yml')
File.write('.rubocop_todo.yml', '')
end
begin
yield
ensure
restore_rubocop_todos
end
end
def restore_rubocop_todos
File.write('.rubocop_todo.yml', @rubocop_todo_before) if @rubocop_todo_before
end
def report_for_branch(metric_name, value)
Librato::Metrics.authenticate(ENV.fetch('LIBRATO_USER'), ENV.fetch('LIBRATO_TOKEN'))
Librato::Metrics.submit(metric_name => {
type: :gauge,
value: value,
source: ENV.fetch('CIRCLE_BRANCH', 'unknown').gsub(/[^\.a-zA-Z0-9_-]/, '--')[0..255],
sporadic: true,
})
end
def install_rubocop # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/MethodLength
namespace :rubocop do # rubocop:disable Metrics/BlockLength
desc 'Regenerate the .rubocop_todo.yml file.'
task :regenerate_todo do
FileUtils.rm_f('.rubocop_todo.yml')
sh 'bundle exec rubocop --auto-gen-config --no-exclude-limit --auto-gen-only-exclude ' \
'--no-auto-gen-enforced-style'
end
desc 'Find candidates for incremental TODO paydown.'
task :paydown_candidates do # rubocop:disable Metrics/BlockLength
todo_contents = File.read('.rubocop_todo.yml')
File.write('.rubocop_todo.yml', '')
raw_offense_info =
`bundle exec rubocop --format=offenses`
.split("\n")
.grep(/^\d+/)
.map { |line| line.split(/\s+/) }
.map { |pieces| [pieces[0].to_i, pieces[1], pieces[2]&.sub('[', '') || 'N/A'] }
.reject { |offense| offense[1] == 'Style/FrozenStringLiteralComment' }
# Prioritize offenses that are safe to correct...
most_violated_cop = raw_offense_info.find { |offense| offense[2] == 'Safe' }
# Then prioritize ones we're just going to mark with ignore comments...
most_violated_cop ||= raw_offense_info.find { |offense| offense[2] == 'N/A' }
# Then just pick the most violated cop if we have to...
most_violated_cop ||= raw_offense_info.first
is_uncorrectable = most_violated_cop[2] == 'N/A'
is_unsafe = most_violated_cop[2] == 'Unsafe'
is_rspec = most_violated_cop[1] =~ %r{^RSpec/}
puts "Most violated cop: #{most_violated_cop[1]} (Correctability: #{most_violated_cop[2]}; " \
"#{most_violated_cop[0]} offenses total)"
puts
raw_offender_info = `bundle exec rubocop --format=worst --only #{most_violated_cop[1]}`
.split("\n")
.grep(/^\d+/)
.grep_v(/Total/)
offense_count = 0
worst_offenders = []
unless is_uncorrectable
# N.B. If it's unsafe, but it's RSpec, we can plow ahead because it'll surface quickly if something
# breaks.
#
# Also: This is madness!!
offense_threshold = is_unsafe && !is_rspec ? 30 : 300
file_threshold = is_unsafe && !is_rspec ? 10 : 50
raw_offender_info.each do |line|
pieces = line.split(/\s+/)
offense_count += pieces[0].to_i
worst_offenders << pieces[1]
break if offense_count >= offense_threshold
break if worst_offenders.length >= file_threshold
end
puts "Worst offenders (#{offense_count} offenses total):"
puts(worst_offenders.map { |offender| " #{offender}" })
puts
end
common_prefix = "echo > .rubocop_todo.yml; rubocop --only #{most_violated_cop[1]}"
common_suffix = '; rake rubocop:regenerate_todo'
puts 'Next steps:'
if is_uncorrectable
puts "#{common_prefix} --autocorrect --disable-uncorrectable#{common_suffix}"
elsif is_unsafe
puts "#{common_prefix} --autocorrect-all #{worst_offenders.join(' ')}#{common_suffix}"
puts
puts 'WARNING: This is an unsafe correction! You MUST validate ALL changes! ' \
'You MUST include the fact that this is an unsafe correction when filing your PR!'
else
puts "#{common_prefix} --autocorrect #{worst_offenders.join(' ')}#{common_suffix}"
end
ensure
File.write('.rubocop_todo.yml', todo_contents)
end
end
end
# :reek:TooManyStatements
def install_terraform
return unless Dir.exist?('./terraform')
namespace :terraform do
desc 'Updates all terraform providers and modules'
task :update do
sh 'terraform -chdir=terraform init -upgrade'
sh 'terraform -chdir=terraform providers lock -platform=darwin_arm64 -platform=darwin_amd64 ' \
'-platform=linux_arm64 -platform=linux_amd64'
end
end
end
end
end
end