Created
June 18, 2015 23:06
-
-
Save jawwad/60a3f86a0fd78ebffa5e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'colorize' | |
PROJECT_NAME = 'provide' | |
PROJECT_BUILD_COMMAND = "xcodebuild -workspace #{PROJECT_NAME}.xcworkspace -scheme #{PROJECT_NAME} > /dev/null" | |
ALL_SWIFT_FILES = Dir["#{PROJECT_NAME}/**/*.swift"] | |
def main | |
# checks_to_perform = [ | |
# # :unnecessary_self, | |
# :private_methods, | |
# :private_unused_methods, | |
# :public_unused_methods, | |
# :class_unused_methods, | |
# :unused_functions, | |
# :private_vars, | |
# :private_set_to_fully_private, | |
# :private_init_methods, | |
# :private_subscript_methods, | |
# :private_unused_vars, | |
# :unused_lets, | |
# :unused_weak_vars, | |
# :unused_vars, | |
# :var_instead_of_let, | |
# ] | |
# This must be empty to perform all checks | |
checks_to_perform = [ | |
:var_instead_of_let, | |
# :all_unused_methods, | |
# :unused_lets, | |
# :unused_vars, | |
] | |
checks_to_skip = [ | |
# :unnecessary_self, | |
# :unused_lets, | |
# :unused_vars, | |
] | |
# Remove checks to skip | |
checks_to_delete = [] | |
checks_to_skip.each do |check_name| | |
$checks_array.each do |check_obj| | |
if check_name == check_obj[:name] | |
checks_to_delete << check_obj | |
puts "Skipping Check: #{check_name}".yellow | |
end | |
end | |
end | |
checks_to_delete.each { |check_obj| $checks_array.delete(check_obj)} | |
checks_to_delete = [] | |
unless checks_to_perform.empty? | |
puts "Only Performing Checks: #{checks_to_perform}".yellow | |
$checks_array.each do |check_obj| | |
if !checks_to_perform.include?(check_obj[:name]) | |
checks_to_delete << check_obj | |
puts "Skipping Check: #{check_obj[:name]}".yellow | |
end | |
end | |
end | |
checks_to_delete.each { |check_obj| $checks_array.delete(check_obj)} | |
# Get counts (to estimate time) | |
$checks_array.each do |check_obj| | |
before = check_obj[:before] | |
after = check_obj[:after] | |
match_count = 0 | |
ALL_SWIFT_FILES.each do |swift_file| | |
lines = File.readlines(swift_file) | |
lines.each do |line| | |
next if should_ignore line | |
if line =~ /#{before}/ | |
match_count = match_count + 1 | |
end | |
end | |
end | |
max_check_name_length = $checks_array.map { |check_obj| check_obj[:name].length }.max | |
printf "Check: %-#{max_check_name_length}s - Match Count: #{match_count}\n", check_obj[:name] | |
check_obj[:match_count] = match_count | |
end | |
# sort by lowest matches first | |
# checks_array.sort! { |h1, h2| h1[:match_count] <=> h2[:match_count] } | |
match_counts = $checks_array.map { |check_obj| check_obj[:match_count] } | |
total_match_count = match_counts.inject(:+) | |
puts "Total match count: #{total_match_count}".cyan | |
# create branch xcodebuild_audit | |
system('git checkout -b xcodebuild_audit') | |
system('git checkout .') | |
completed_count = 0 | |
$checks_array.each do |check_obj| | |
check_name = check_obj[:name] | |
puts "Performing Check: #{check_name}".cyan | |
perform_check check_obj, completed_count, total_match_count | |
completed_count += check_obj[:match_count] | |
end | |
end | |
def perform_check check_obj, completed_count, total_match_count | |
# check_to_perform | |
before = check_obj[:before] | |
after = check_obj[:after] | |
# count the total number of matches | |
match_count = 0 | |
ALL_SWIFT_FILES.each do |swift_file| | |
lines = File.readlines(swift_file) | |
lines.each do |line| | |
next if should_ignore line | |
if line =~ /#{before}/ | |
match_count = match_count + 1 | |
end | |
end | |
end | |
# print the count | |
puts "Match Count: #{match_count}".cyan | |
# start_time | |
start_time = Time.now | |
match_index = 0 | |
# Make sure the project builds | |
xcodebuild_success = system(PROJECT_BUILD_COMMAND) | |
# Exit if the project doesn't build | |
unless xcodebuild_success | |
puts "Compile Failed!".red | |
exit | |
end | |
ALL_SWIFT_FILES.each_with_index do |swift_file, index| | |
puts "File: #{index + 1}/#{ALL_SWIFT_FILES.count}: #{swift_file}".green | |
lines = File.readlines(swift_file) | |
lines.each do |line| | |
next if should_ignore line | |
original_line = line.dup # to use for undo | |
if line.sub!(/#{before}/, after) # try substitution | |
match_index += 1 | |
File.open(swift_file, 'w') { |file| file.puts lines } | |
puts "\n#{match_index + completed_count}/#{total_match_count}" | |
puts "\n#{match_index}/#{match_count} - Before and After: ".cyan | |
puts "#{original_line}#{line}" | |
xcodebuild_success = system(PROJECT_BUILD_COMMAND) | |
if xcodebuild_success | |
system("git add #{swift_file}") | |
puts "Passed".green | |
else | |
line.replace original_line | |
puts "Failed".red | |
end | |
# Print Time Stats | |
time_diff_this_run = Time.now - start_time | |
average_this_run = time_diff_this_run / match_index | |
total_expected_this_run = average_this_run * match_count | |
remaining_time_expected_this_run = total_expected_this_run - time_diff_this_run | |
grand_total_expected_time = average_this_run * total_match_count | |
grand_total_completed_count = completed_count + match_index | |
grand_total_remaining_count = total_match_count - grand_total_completed_count | |
grand_total_remaining_time = grand_total_remaining_count * average_this_run | |
puts "Elapsed: #{time_diff_this_run.duration}" | |
puts "Average: #{average_this_run}" | |
puts "Total Expected: #{total_expected_this_run.duration}" | |
puts "Remaining Expected: #{remaining_time_expected_this_run.duration}" | |
puts "Grand Total Expected: #{grand_total_expected_time.duration}" | |
puts "Grand Remaining Expected: #{grand_total_remaining_time.duration}" | |
end | |
end | |
File.open(swift_file, 'w') { |file| file.puts lines } | |
end | |
# commit changes for this check | |
system("git commit -m #{check_obj[:name]}") | |
end | |
def should_ignore line | |
$methods_to_ignore.each do |method| | |
if line =~ /#{method}\(/ | |
# puts "Skipping: #{line}".yellow | |
return true | |
end | |
end | |
$lines_to_ignore.each do |match| | |
if line =~ /.*#{match}.*/ | |
# puts "Skipping: #{line}".yellow | |
return true | |
end | |
end | |
return false | |
end | |
class Numeric | |
def duration | |
secs = self.to_int | |
mins = secs / 60 | |
hours = mins / 60 | |
if hours > 0 | |
"#{hours} hr #{mins % 60} min" | |
elsif mins > 0 | |
"#{mins} min #{secs % 60} sec" | |
elsif secs >= 0 | |
"#{secs} sec" | |
end | |
end | |
end | |
# All Checks | |
$checks_array = [ | |
{ | |
:name => :unnecessary_self, | |
:before => '\bself\.', | |
:after => '', | |
}, | |
{ | |
:name => :all_unused_methods, | |
:before => '\bfunc\s+', | |
:after => 'func unused_' | |
}, | |
{ | |
:name => :unused_lets, | |
:before => '\blet ', | |
:after => 'let unused_', | |
}, | |
{ | |
:name => :unused_vars, | |
:before => '\bvar ', | |
:after => 'var unused_', | |
}, | |
{ | |
:name => :var_instead_of_let, | |
:before => '\bvar ', | |
:after => 'let ', | |
}, | |
{ | |
:name => :private_methods, | |
:before => '^ func', | |
:after => ' private func' | |
}, | |
{ | |
:name => :private_unused_methods, | |
:before => '^ private func ', | |
:after => ' private func unused_', | |
}, | |
{ | |
:name => :public_unused_methods, | |
:before => '^ func ', | |
:after => ' func unused_', | |
}, | |
{ | |
:name => :class_unused_methods, | |
:before => '^ class func ', | |
:after => ' class func unused_', | |
}, | |
{ | |
:name => :unused_functions, | |
:before => '^func ', | |
:after => 'func unused_', | |
}, | |
{ | |
:name => :private_vars, | |
:before => '^ var', | |
:after => ' private var', | |
}, | |
{ | |
:name => :private_set_to_fully_private, | |
:before => 'private(set)', | |
:after => 'private', | |
}, | |
{ | |
:name => :private_init_methods, | |
:before => ' init\b', | |
:after => ' private init', | |
}, | |
{ | |
:name => :private_subscript_methods, | |
:before => ' subscript\b', | |
:after => ' private subscript', | |
}, | |
{ | |
:name => :private_unused_vars, | |
:before => '^ private var ', | |
:after => ' private var unused_', | |
}, | |
{ | |
:name => :unused_weak_vars, | |
:before => 'weak var ', | |
:after => 'weak var unused_', | |
}, | |
] | |
# Methods To Ignore | |
$methods_to_ignore = %w[ | |
application | |
applicationDidBecomeActive | |
applicationDidEnterBackground | |
applicationWillEnterForeground | |
applicationWillResignActive | |
applicationWillTerminate | |
clearSDImageCache | |
collectionView | |
connection | |
connectionDidFinishLoading | |
gestureRecognizer | |
pageViewController | |
performSegueOnce | |
pickerView | |
scrollViewDidEndDecelerating | |
scrollViewDidEndScrolling | |
tableView | |
textField | |
textFieldDidBeginEditing | |
textFieldDidEndEditing | |
textFieldShouldEndEditing | |
textFieldShouldReturn | |
textView | |
textViewDidBeginEditing | |
textViewDidChange | |
textViewDidEndEditing | |
webViewDidFailLoadWithError | |
webViewDidFinishLoad | |
].map { |method_name| "func #{method_name}"} | |
$lines_to_ignore = [ | |
'(let _)', | |
'var window: UIWindow', | |
'class var KIF_SCREENSHOTS', | |
'var enableInputClicksWhenVisible: Bool', # UIInputViewAudioFeedback protocol | |
'let CurrentBuildConfig', | |
'let CurrentEnvironment', | |
'because the purpose var gets changed', | |
'read-only var in Swift casts', | |
'var keyboardType: UIKeyboardType', # UITextInputTraits protocol | |
] | |
# Run Main | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment