Skip to content

Instantly share code, notes, and snippets.

@ShimShtein
Last active August 13, 2017 18:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ShimShtein/341b746f15826261053e97c2f435ff1a to your computer and use it in GitHub Desktop.
Save ShimShtein/341b746f15826261053e97c2f435ff1a to your computer and use it in GitHub Desktop.
Inspecting provisioning templates with Rubocop
class ErbStrip
ERB_REGEX = /(?<start_tag><%[-=]?)(?<script>([^#]).*?)(?<end_tag>-?%>)/m
def initialize(source)
@source = source
end
def strip
out = ''
current_position = 0
while (match = next_erb(current_position))
out << fill_newlines(current_position, match.begin(2) - 1)
out << match[:script]
out << ';'
out << ' ' * (match[:end_tag].length - 1)
current_position = match.end(3)
end
out
end
def next_erb(start_pos)
@source.match(ERB_REGEX, start_pos)
end
def fill_newlines(start, finish)
newlines = @source[start..finish].split("\n")
newlines.map { |line| ' ' * line.length }.join("\n")
end
end
# frozen_string_literal: true
module RuboCop
module Cop
module Foreman
# This cop checks for calls to foreman_url without params
class ForemanUrl < Cop
MSG = 'Call foreman_url with (\'built\') `%s`.'.freeze
def_node_matcher :foreman_url_call?, '(send nil :foreman_url)'
def on_send(node)
return unless foreman_url_call?(node)
add_offense(node, :expression)
end
private
def message(node)
format(MSG, node.source)
end
end
end
end
end
# RuboCop::TargetFinder.new(nil)
module RuboCop
class TargetFinder
def ruby_file_with_erb?(file)
ruby_file_without_erb?(file) || File.extname(file) == '.erb'
end
alias_method :ruby_file_without_erb?, :ruby_file?
alias_method :ruby_file?, :ruby_file_with_erb?
end
end
#! /bin/ruby
unless ARGV.length == 1
print <<-MESSAGE
Usage:
inspect_template /path/to/template_source.erb
MESSAGE
exit
end
require 'tempfile'
load 'erb_strip.rb'
load 'rubocop_json_processor.rb'
template_source_file = ARGV[0]
template_source_buffer = File.read(template_source_file)
template_destination = File.basename(template_source_file, '.erb')
Tempfile.open([template_destination, '.erb']) do |modified_template|
modified_template_buffer = ErbStrip.new(template_source_buffer).strip
modified_template.write(modified_template_buffer)
modified_template.flush
json_temp_file = Tempfile.new(['template_report', '.json'])
json_temp_file_name = json_temp_file.path
json_temp_file.close # we don't need this file to remain open.
`cat #{modified_template.path} | rubocop -d -s "#{template_source_file}" -r "#{Dir.pwd}/foreman_callback_cop.rb" --only "Foreman/ForemanUrl" -r "#{Dir.pwd}/foreman_erb_monkey_patch.rb" --format json --out #{json_temp_file_name}`
RubocopJsonProcessor.new(json_temp_file_name).to_clang
json_temp_file.unlink
end
exit 0
require 'json'
class RubocopJsonProcessor
def initialize(json_file)
@report = JSON.parse(File.read(json_file))
end
def to_clang
@report['files'].each do |file_report|
report_file(file_report['path'], file_report['offenses'])
end
end
private
def report_file(filename, offenses)
report "File: \"#{filename}\""
buffer = File.read(filename)
lines = buffer.split("\n")
offenses.each do |offense_report|
report_offense(lines, offense_report)
end
end
def report_offense(lines, offense)
report "Message: #{offense['message']}"
report 'Source:'
report_source(lines, offense['location'])
end
def report_source(lines, location)
report lines[location['line'] - 1]
highlight = (' ' * (location['column'] - 1)) + ('^' * location['length'])
report highlight
end
def report(line)
puts line
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment