iosappaudit 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/default_configuration.yaml +11 -0
- data/exe/iosappaudit +4 -0
- data/i18n/en.yml +35 -0
- data/iosappaudit.gemspec +43 -0
- data/lib/iosappaudit.rb +37 -0
- data/lib/iosappaudit/Helper/file_seeker.rb +20 -0
- data/lib/iosappaudit/Helper/options_parser.rb +19 -0
- data/lib/iosappaudit/Presenter/CSVReportPresenter.rb +94 -0
- data/lib/iosappaudit/Review/complexity_report.rb +70 -0
- data/lib/iosappaudit/Review/complexity_report_parser.rb +50 -0
- data/lib/iosappaudit/Review/complexity_reviewer.rb +18 -0
- data/lib/iosappaudit/Review/project_report.rb +45 -0
- data/lib/iosappaudit/Review/project_reviewer.rb +61 -0
- data/lib/iosappaudit/version.rb +3 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '020928007442acd4e62981306e047324cd18bbae4639a3abfa989d2451fb68e2'
|
4
|
+
data.tar.gz: eae2e1627e5f9e20f74cb99ae865b75fb6919a660545a92313557781aafe2358
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 74575f011ea5d86e3d027901e84155d34fcd9cc1b74ffddc1d4ba0ca29bb01efa6cb6cb5e82b1afce0e1fd9e9225b90c7549837ed9c1ecd7569b016e23ddf45c
|
7
|
+
data.tar.gz: bf7062f0c587b10604320744d7b81b4ad04a5a56c2ac817b1af0999ed94f835755dc6c3e6bb2ee52ec3d4e1fd00ea4052e3f630c623859c6009ba3617764633b
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "iosappaudit"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
project_url: "" # required
|
2
|
+
sources_url: "" # required
|
3
|
+
xcodeproj:
|
4
|
+
name: "" # first xcodeproj found will be used if empty
|
5
|
+
main_target_name: "" # first target will be used if empty
|
6
|
+
complexity:
|
7
|
+
file_line_count_threshold: 500
|
8
|
+
output_format:
|
9
|
+
adds_row_padding: true
|
10
|
+
size: 0
|
11
|
+
csv_output: audit.csv
|
data/exe/iosappaudit
ADDED
data/i18n/en.yml
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
section:
|
2
|
+
project:
|
3
|
+
title: Project
|
4
|
+
row:
|
5
|
+
name: Name
|
6
|
+
version: Version
|
7
|
+
deployment_target: Deployment Target
|
8
|
+
localizations: Localizations
|
9
|
+
settings:
|
10
|
+
title: Settings
|
11
|
+
row:
|
12
|
+
schemes: Schemes
|
13
|
+
targets: Targets
|
14
|
+
configurations: Configurations
|
15
|
+
complexity:
|
16
|
+
title: Code
|
17
|
+
row:
|
18
|
+
file_count: Files
|
19
|
+
file_count_threshold: !!pl
|
20
|
+
1: Files with more than one line
|
21
|
+
n: Files with more than %1 lines
|
22
|
+
swift_file_count: Swift files
|
23
|
+
obj_c_file_count: Objective C files
|
24
|
+
code_line_count: Lines of code
|
25
|
+
ccn_line_count: Cyclomatic Complexity Number
|
26
|
+
resources:
|
27
|
+
title: Resources
|
28
|
+
row:
|
29
|
+
xib_count: Xibs
|
30
|
+
storyboards: Storyboards
|
31
|
+
tests:
|
32
|
+
title: Tests
|
33
|
+
row:
|
34
|
+
unit_tests: Unit Tests
|
35
|
+
ui_tests: UI Tests
|
data/iosappaudit.gemspec
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "iosappaudit/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'iosappaudit'
|
8
|
+
s.version = IOSAppAudit::VERSION
|
9
|
+
s.date = '2019-01-23'
|
10
|
+
s.summary = "Audit"
|
11
|
+
s.description = "Audit"
|
12
|
+
s.authors = ["Gaétan Zanella"]
|
13
|
+
s.email = 'gaetan.zanella@gmail.com'
|
14
|
+
# use `git ls-files -coz -x *.gem` for development
|
15
|
+
|
16
|
+
s.homepage = 'http://rubygems.org/gems/hola'
|
17
|
+
s.license = 'MIT'
|
18
|
+
s.add_dependency "xcodeproj", "~> 1.4"
|
19
|
+
s.add_dependency "r18n-core", "~> 3.2"
|
20
|
+
s.add_dependency "colorize", "~> 0.8"
|
21
|
+
s.add_development_dependency "byebug", "~> 10.0"
|
22
|
+
|
23
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
24
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
25
|
+
if s.respond_to?(:metadata)
|
26
|
+
s.metadata["allowed_push_host"] = "https://rubygems.org"
|
27
|
+
s.metadata["homepage_uri"] = s.homepage
|
28
|
+
s.metadata["source_code_uri"] = "https://github.com/faberNovel/iosappaudit"
|
29
|
+
s.metadata["changelog_uri"] = "https://github.com/faberNovel/iosappaudit/blob/master/CHANGELOG.md"
|
30
|
+
else
|
31
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
32
|
+
"public gem pushes."
|
33
|
+
end
|
34
|
+
|
35
|
+
# Specify which files should be added to the gem when it is released.
|
36
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
37
|
+
s.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
38
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
39
|
+
end
|
40
|
+
s.bindir = "exe"
|
41
|
+
s.executables = ["iosappaudit"]
|
42
|
+
s.require_paths = ["lib"]
|
43
|
+
end
|
data/lib/iosappaudit.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'xcodeproj'
|
3
|
+
require 'rexml/document'
|
4
|
+
require 'r18n-core'
|
5
|
+
require 'CSV'
|
6
|
+
require 'yaml'
|
7
|
+
require 'JSON'
|
8
|
+
require 'fileutils'
|
9
|
+
require 'colorize'
|
10
|
+
|
11
|
+
require_relative "iosappaudit/Review/complexity_report.rb"
|
12
|
+
require_relative "iosappaudit/Review/complexity_report_parser.rb"
|
13
|
+
require_relative "iosappaudit/Review/complexity_reviewer.rb"
|
14
|
+
require_relative "iosappaudit/Review/project_report.rb"
|
15
|
+
require_relative "iosappaudit/Review/project_reviewer.rb"
|
16
|
+
require_relative "iosappaudit/Helper/file_seeker.rb"
|
17
|
+
require_relative "iosappaudit/Helper/options_parser.rb"
|
18
|
+
require_relative "iosappaudit/Presenter/CSVReportPresenter.rb"
|
19
|
+
|
20
|
+
options = {}
|
21
|
+
OptionParser.new do |parser|
|
22
|
+
parser.on("-o", "--option=URL") do |url|
|
23
|
+
options[:url] = url
|
24
|
+
end
|
25
|
+
end.parse!
|
26
|
+
|
27
|
+
properties_parser = Helper::OptionsParser.new
|
28
|
+
properties = properties_parser.parse options
|
29
|
+
|
30
|
+
complexity_reviewer = Review::ComplexityReviewer.new
|
31
|
+
complexity_report = complexity_reviewer.review_folder properties
|
32
|
+
|
33
|
+
project_reviewer = Review::ProjectReviewer.new
|
34
|
+
project_report = project_reviewer.review_folder properties
|
35
|
+
|
36
|
+
presenter = Presenter::CSVReportPresenter.new properties, project_report, complexity_report
|
37
|
+
presenter.generate_review
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Helper
|
2
|
+
class FileSeeker
|
3
|
+
|
4
|
+
def initialize()
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
def find_files(url, name, extension)
|
9
|
+
Dir.glob("#{url}/**/#{name}.#{extension}")
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_files_with_extension(url, extension)
|
13
|
+
Dir.glob("#{url}/**/*.#{extension}")
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_file_names_with_extension(url, extension)
|
17
|
+
find_files_with_extension(url, extension).map { |url| File.basename(url) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Helper
|
2
|
+
class OptionsParser
|
3
|
+
def parse(options)
|
4
|
+
default_properties = JSON.parse(YAML::load_file(__dir__ + "/../../../default_configuration.yaml").to_json, object_class: Hash)
|
5
|
+
properties = JSON.parse(YAML::load_file(options[:url]).to_json, object_class: Hash)
|
6
|
+
default_properties.merge! properties
|
7
|
+
symbolizeOptions default_properties
|
8
|
+
end
|
9
|
+
|
10
|
+
def symbolizeOptions(options)
|
11
|
+
options.inject({}) { |new_hash, key_value|
|
12
|
+
key, value = key_value
|
13
|
+
value = symbolizeOptions(value) if value.is_a?(Hash)
|
14
|
+
new_hash[key.to_sym] = value
|
15
|
+
new_hash
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
include R18n::Helpers
|
2
|
+
|
3
|
+
module Presenter
|
4
|
+
class CSVReportPresenter
|
5
|
+
|
6
|
+
def initialize(options, project_report, complexity_report)
|
7
|
+
@project_report = project_report
|
8
|
+
@complexity_report = complexity_report
|
9
|
+
@options = options
|
10
|
+
root = File.expand_path '../../..', File.dirname(__FILE__)
|
11
|
+
R18n.default_places = "#{root}/i18n/"
|
12
|
+
R18n.set('en')
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate_review
|
16
|
+
puts "Generating csv review..."
|
17
|
+
url = @options[:csv_output]
|
18
|
+
CSV.open(url, "wb") do |csv|
|
19
|
+
# Project
|
20
|
+
csv << section(t.section.project.title)
|
21
|
+
csv << value_row(t.section.project.row.name, @project_report.name)
|
22
|
+
csv << value_row(t.section.project.row.version, @project_report.version)
|
23
|
+
csv << value_row(t.section.project.row.deployment_target, @project_report.deployment_target.first)
|
24
|
+
csv << array_row(t.section.project.row.localizations, @project_report.localizations)
|
25
|
+
# Settings
|
26
|
+
csv << section(t.section.settings.title)
|
27
|
+
csv << array_row(t.section.settings.row.targets, @project_report.target_names)
|
28
|
+
csv << array_row(t.section.settings.row.configurations, @project_report.configuration_names)
|
29
|
+
# Complexity
|
30
|
+
csv << section(t.section.complexity.title)
|
31
|
+
csv << array_row(t.section.complexity.row.file_count, @project_report.main_target_files)
|
32
|
+
csv << array_row(t.section.complexity.row.swift_file_count, @project_report.main_target_swift_files)
|
33
|
+
csv << array_row(t.section.complexity.row.obj_c_file_count, @project_report.main_target_obj_c_files)
|
34
|
+
csv << value_row(t.section.complexity.row.code_line_count, @complexity_report.file_measure.sum_ncss)
|
35
|
+
file_line_count_threshold = @options[:complexity][:file_line_count_threshold]
|
36
|
+
metrics_ncss = @complexity_report.file_metrics_with_more_than_count_lines @options[:complexity][:file_line_count_threshold]
|
37
|
+
csv << array_row(t.section.complexity.row.file_count_threshold(file_line_count_threshold), format_ncss(metrics_ncss))
|
38
|
+
metrics_ccn = @complexity_report.file_metrics_sorted_by_ccn
|
39
|
+
csv << value_row(t.section.complexity.row.ccn_line_count, @complexity_report.file_measure.sum_ccn, format_ccn(metrics_ccn))
|
40
|
+
# Resources
|
41
|
+
csv << section(t.section.resources.title)
|
42
|
+
csv << array_row(t.section.resources.row.xib_count, @project_report.xibs)
|
43
|
+
csv << array_row(t.section.resources.row.storyboards, @project_report.storyboards)
|
44
|
+
# Tests
|
45
|
+
csv << section(t.section.tests.title)
|
46
|
+
csv << array_row(t.section.tests.row.unit_tests, @project_report.unit_test_target_files)
|
47
|
+
csv << array_row(t.section.tests.row.ui_tests, @project_report.ui_test_target_files)
|
48
|
+
end
|
49
|
+
puts "Report #{url} created 🎉".colorize(:green)
|
50
|
+
end
|
51
|
+
|
52
|
+
def section(title)
|
53
|
+
return [title]
|
54
|
+
end
|
55
|
+
|
56
|
+
def value_row(title, value, detail=[])
|
57
|
+
if value.nil?
|
58
|
+
return format_row [title, detail]
|
59
|
+
end
|
60
|
+
format_row [title, value, format_array(detail)]
|
61
|
+
end
|
62
|
+
|
63
|
+
def array_row(title, array)
|
64
|
+
if array.nil?
|
65
|
+
return [title, 0]
|
66
|
+
end
|
67
|
+
return format_row [title, array.count, format_array(array)]
|
68
|
+
end
|
69
|
+
|
70
|
+
def format_row(array)
|
71
|
+
if @options[:output_format][:adds_row_padding]
|
72
|
+
array.unshift("")
|
73
|
+
end
|
74
|
+
array
|
75
|
+
end
|
76
|
+
|
77
|
+
def format_array(array)
|
78
|
+
size = @options[:output_format][:size]
|
79
|
+
output = array.first(size).join("\n")
|
80
|
+
if array.count > size
|
81
|
+
output += "\n..."
|
82
|
+
end
|
83
|
+
output
|
84
|
+
end
|
85
|
+
|
86
|
+
def format_ncss(metrics)
|
87
|
+
metrics.map { |metric| "(#{metric.ncss}) #{metric.file_url}" }
|
88
|
+
end
|
89
|
+
|
90
|
+
def format_ccn(metrics)
|
91
|
+
metrics.map { |metric| "(#{metric.ccn}) #{metric.file_url}" }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Review
|
2
|
+
class FunctionMetric
|
3
|
+
|
4
|
+
attr_accessor :file_url, :function_name, :line_number, :ncss, :ccn
|
5
|
+
|
6
|
+
def initialize(file_url = "", function_name = "", line_number = 0, ncss = 0, ccn = 0)
|
7
|
+
@file_url = file_url
|
8
|
+
@function_name = function_name
|
9
|
+
@line_number = line_number
|
10
|
+
@ncss = ncss
|
11
|
+
@ccn = ccn
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class FileMetric
|
16
|
+
|
17
|
+
attr_accessor :file_url, :ncss, :ccn, :functions
|
18
|
+
|
19
|
+
def initialize(file_url = "", ncss = 0, ccn = 0, functions = 0)
|
20
|
+
@file_url = file_url
|
21
|
+
@ncss = ncss
|
22
|
+
@ccn = ccn
|
23
|
+
@functions = functions
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class FunctionMeasure
|
28
|
+
|
29
|
+
attr_accessor :metrics, :average_ncss, :average_ccn
|
30
|
+
|
31
|
+
def initialize(metrics = [], average_ncss = 0, average_ccn = 0)
|
32
|
+
@metrics = metrics
|
33
|
+
@average_ncss = average_ncss
|
34
|
+
@average_ccn = average_ccn
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class FileMeasure
|
39
|
+
|
40
|
+
attr_accessor :metrics, :average_ncss, :average_ccn, :average_functions, :sum_ncss, :sum_ccn, :sum_functions
|
41
|
+
|
42
|
+
def initialize(metrics = [], average_ncss = 0, average_ccn = 0, average_functions = 0, sum_ncss = 0, sum_ccn = 0, sum_functions = 0)
|
43
|
+
@metrics = metrics
|
44
|
+
@average_ncss = average_ncss
|
45
|
+
@average_ccn = average_ccn
|
46
|
+
@average_functions = average_functions
|
47
|
+
@sum_ncss = sum_ncss
|
48
|
+
@sum_ccn = sum_ccn
|
49
|
+
@sum_functions = sum_functions
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class ComplexityReport
|
54
|
+
|
55
|
+
attr_accessor :function_measure, :file_measure
|
56
|
+
|
57
|
+
def initialize(function_measure = [], file_measure = [])
|
58
|
+
@function_measure = function_measure
|
59
|
+
@file_measure = file_measure
|
60
|
+
end
|
61
|
+
|
62
|
+
def file_metrics_with_more_than_count_lines(count)
|
63
|
+
file_measure.metrics.select { |metric| metric.ncss >= count }.sort { |lhs, rhs| (lhs.ncss <=> rhs.ncss) * -1 }
|
64
|
+
end
|
65
|
+
|
66
|
+
def file_metrics_sorted_by_ccn
|
67
|
+
file_measure.metrics.sort { |lhs, rhs| (lhs.ccn <=> rhs.ccn) * -1 }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
include REXML
|
2
|
+
|
3
|
+
module Review
|
4
|
+
class ComplexityReportParser
|
5
|
+
|
6
|
+
def initialize()
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse_file(url)
|
10
|
+
file = File.new url
|
11
|
+
doc = Document.new file
|
12
|
+
|
13
|
+
function_measure = FunctionMeasure.new
|
14
|
+
file_measure = FileMeasure.new
|
15
|
+
|
16
|
+
function_xml = doc.root.elements["measure[@type='Function']"]
|
17
|
+
function_measure.average_ncss = function_xml.elements["average[@label='NCSS']"]["value"].to_s.to_f
|
18
|
+
function_measure.average_ccn = function_xml.elements["average[@label='CCN']"]["value"].to_s.to_f
|
19
|
+
function_xml.elements.each("item") do |item|
|
20
|
+
values = item.get_elements("value")
|
21
|
+
item_name = item.attributes["name"]
|
22
|
+
metric = FunctionMetric.new
|
23
|
+
metric.file_url = /(?<=( at )).+(?=:)/.match(item_name).to_s
|
24
|
+
metric.function_name = /.+(?=\(...\))/.match(item_name).to_s
|
25
|
+
metric.line_number = /(?<=:)\d+/.match(item_name).to_s.to_i
|
26
|
+
metric.ncss = values[1].text.to_i
|
27
|
+
metric.ccn = values[2].text.to_i
|
28
|
+
function_measure.metrics.push metric
|
29
|
+
end
|
30
|
+
|
31
|
+
file_xml = doc.root.elements["measure[@type='File']"]
|
32
|
+
file_measure.average_ncss = file_xml.elements["average[@label='NCSS']"]["value"].to_s.to_f
|
33
|
+
file_measure.average_ccn = file_xml.elements["average[@label='CCN']"]["value"].to_s.to_f
|
34
|
+
file_measure.average_functions = file_xml.elements["average[@label='Functions']"]["value"].to_s.to_f
|
35
|
+
file_measure.sum_ncss = file_xml.elements["sum[@label='NCSS']"]["value"].to_s.to_i
|
36
|
+
file_measure.sum_ccn = file_xml.elements["sum[@label='CCN']"]["value"].to_s.to_i
|
37
|
+
file_measure.sum_functions = file_xml.elements["sum[@label='Functions']"]["value"].to_s.to_i
|
38
|
+
file_xml.elements.each("item") do |item|
|
39
|
+
values = item.get_elements("value")
|
40
|
+
metric = FileMetric.new
|
41
|
+
metric.file_url = item.attributes["name"].to_s
|
42
|
+
metric.ncss = values[1].text.to_i
|
43
|
+
metric.ccn = values[2].text.to_i
|
44
|
+
metric.functions = values[3].text.to_i
|
45
|
+
file_measure.metrics.push metric
|
46
|
+
end
|
47
|
+
ComplexityReport.new function_measure, file_measure
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Review
|
2
|
+
class ComplexityReviewer
|
3
|
+
|
4
|
+
def initialize()
|
5
|
+
end
|
6
|
+
|
7
|
+
def review_folder(options)
|
8
|
+
puts "Reviewing source complexity... "
|
9
|
+
lizard_report = "lizard-report.xml"
|
10
|
+
source = options[:sources_url]
|
11
|
+
`lizard -m --xml #{source} > #{lizard_report}`
|
12
|
+
parser = ComplexityReportParser.new
|
13
|
+
report = parser.parse_file lizard_report
|
14
|
+
FileUtils.rm lizard_report
|
15
|
+
report
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Review
|
2
|
+
class ProjectReport
|
3
|
+
attr_accessor :name, :version, :deployment_target, :target_names, :configuration_names, :main_target_name, :main_target_files, :main_target_resources, :ui_test_target_name, :unit_test_target_name, :ui_test_target_files, :unit_test_target_files, :localizations
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@name = ""
|
7
|
+
@version = ""
|
8
|
+
@deployment_target = ""
|
9
|
+
@target_names = ""
|
10
|
+
@configuration_names = ""
|
11
|
+
@main_target_name = ""
|
12
|
+
@main_target_files = []
|
13
|
+
@main_target_resources = []
|
14
|
+
@ui_test_target_name = ""
|
15
|
+
@unit_test_targe_name = ""
|
16
|
+
@ui_test_target_files = []
|
17
|
+
@unit_test_target_files = []
|
18
|
+
@localizations = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def main_target_swift_files
|
22
|
+
main_target_files.select { |file| file.include?(".swift") }
|
23
|
+
end
|
24
|
+
|
25
|
+
def main_target_obj_c_files
|
26
|
+
main_target_files.select { |file| file.include?(".m") }
|
27
|
+
end
|
28
|
+
|
29
|
+
def has_swift_files?
|
30
|
+
main_target_swift_files.empty?
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_obj_c_files?
|
34
|
+
main_target_obj_c_files.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
def xibs
|
38
|
+
main_target_resources.select { |file| file.include?(".xib") }
|
39
|
+
end
|
40
|
+
|
41
|
+
def storyboards
|
42
|
+
main_target_resources.select { |file| file.include?(".storyboard") }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Review
|
2
|
+
class ProjectReviewer
|
3
|
+
|
4
|
+
def initialize()
|
5
|
+
end
|
6
|
+
|
7
|
+
def review_folder(options)
|
8
|
+
file_seeker = Helper::FileSeeker.new
|
9
|
+
url = options[:project_url]
|
10
|
+
project_name = options[:xcodeproj][:name]
|
11
|
+
urls = []
|
12
|
+
if project_name.empty?
|
13
|
+
urls = file_seeker.find_files_with_extension url, "xcodeproj"
|
14
|
+
else
|
15
|
+
urls = file_seeker.find_files url, project_name, "xcodeproj"
|
16
|
+
end
|
17
|
+
if urls.empty?
|
18
|
+
raise "Xcode project not found"
|
19
|
+
end
|
20
|
+
report = ProjectReport.new
|
21
|
+
project = Xcodeproj::Project.open urls.first
|
22
|
+
project_basename = File.basename(urls.first)
|
23
|
+
puts "Reviewing #{project_basename}..."
|
24
|
+
root_object = project.root_object
|
25
|
+
if root_object.targets.empty?
|
26
|
+
raise "Xcode project #{root_object.name} has no target"
|
27
|
+
end
|
28
|
+
report.name = root_object.name
|
29
|
+
report.deployment_target = root_object.targets.map { |target| target.deployment_target }.uniq
|
30
|
+
report.target_names = root_object.targets.map &:name
|
31
|
+
report.configuration_names = root_object.targets.flat_map { |target| target.build_configurations.map(&:name) }.uniq
|
32
|
+
report.localizations = findLocalizations project
|
33
|
+
main_target_name = options[:xcodeproj][:main_target_name]
|
34
|
+
main_target = nil
|
35
|
+
if main_target_name.empty?
|
36
|
+
main_target = root_object.targets[0]
|
37
|
+
else
|
38
|
+
main_target = root_object.targets.detect { |target| target.name == main_target_name }
|
39
|
+
if main_target.nil?
|
40
|
+
raise "Xcode target named #{main_target_name} not found"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
report.version = project.build_configurations.map { |config| config.build_settings["CURRENT_PROJECT_VERSION"] }.uniq.first
|
44
|
+
ui_test_target = root_object.targets.detect { |target| target.build_configurations.any? { |config| !config.build_settings["TEST_TARGET_NAME"].nil? } }
|
45
|
+
unit_test_target = root_object.targets.detect { |target| target.build_configurations.any? { |config| !config.build_settings["TEST_HOST"].nil? } }
|
46
|
+
report.main_target_name = main_target&.name
|
47
|
+
report.main_target_files = main_target&.source_build_phase&.files&.map { |file| file.display_name }
|
48
|
+
report.main_target_resources = main_target&.resources_build_phase&.files&.map { |file| file.display_name }
|
49
|
+
report.ui_test_target_name = ui_test_target&.name
|
50
|
+
report.unit_test_target_name = unit_test_target&.name
|
51
|
+
report.ui_test_target_files = ui_test_target&.source_build_phase&.files&.map { |file| file.display_name }
|
52
|
+
report.unit_test_target_files = unit_test_target&.source_build_phase&.files&.map { |file| file.display_name }
|
53
|
+
return report
|
54
|
+
end
|
55
|
+
|
56
|
+
def findLocalizations(project)
|
57
|
+
files = project.files.select { |f| f.path.include?("Localizable.strings") }
|
58
|
+
files.map { |f| f.name.nil? ? "base" : f.name }.uniq
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: iosappaudit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gaétan Zanella
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-01-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: xcodeproj
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: r18n-core
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: colorize
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.8'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
description: Audit
|
70
|
+
email: gaetan.zanella@gmail.com
|
71
|
+
executables:
|
72
|
+
- iosappaudit
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- Gemfile
|
78
|
+
- Rakefile
|
79
|
+
- bin/console
|
80
|
+
- bin/setup
|
81
|
+
- default_configuration.yaml
|
82
|
+
- exe/iosappaudit
|
83
|
+
- i18n/en.yml
|
84
|
+
- iosappaudit.gemspec
|
85
|
+
- lib/iosappaudit.rb
|
86
|
+
- lib/iosappaudit/Helper/file_seeker.rb
|
87
|
+
- lib/iosappaudit/Helper/options_parser.rb
|
88
|
+
- lib/iosappaudit/Presenter/CSVReportPresenter.rb
|
89
|
+
- lib/iosappaudit/Review/complexity_report.rb
|
90
|
+
- lib/iosappaudit/Review/complexity_report_parser.rb
|
91
|
+
- lib/iosappaudit/Review/complexity_reviewer.rb
|
92
|
+
- lib/iosappaudit/Review/project_report.rb
|
93
|
+
- lib/iosappaudit/Review/project_reviewer.rb
|
94
|
+
- lib/iosappaudit/version.rb
|
95
|
+
homepage: http://rubygems.org/gems/hola
|
96
|
+
licenses:
|
97
|
+
- MIT
|
98
|
+
metadata:
|
99
|
+
allowed_push_host: https://rubygems.org
|
100
|
+
homepage_uri: http://rubygems.org/gems/hola
|
101
|
+
source_code_uri: https://github.com/faberNovel/iosappaudit
|
102
|
+
changelog_uri: https://github.com/faberNovel/iosappaudit/blob/master/CHANGELOG.md
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubygems_version: 3.0.3
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: Audit
|
122
|
+
test_files: []
|