Commit 18e1ccbb authored by Fabio Pelosin's avatar Fabio Pelosin

[Linter] Improvements.

- `spec lint` can now accept multiple files passed
  as arguments.
- the linter can work in environments with xcodebuild.
- Other misc improvements.
parent 741cece1
......@@ -5,7 +5,7 @@ module Pod
# TODO: Add check to ensure that attributes inherited by subspecs are not duplicated ?
attr_accessor :quick, :lenient, :no_clean, :repo_path
attr_accessor :quick, :no_clean, :repo_path
attr_reader :spec, :file
attr_reader :errors, :warnings, :notes
......@@ -15,7 +15,11 @@ module Pod
def spec_name
name = file.basename('.*').to_s
name << ( @spec ? " (#{spec.version})" : " (#{file.dirname.basename})")
if @spec
name << " (#{spec.version})"
elsif @repo_path
name << " (#{file.dirname.basename})"
end
name
end
......@@ -69,12 +73,6 @@ module Pod
@notes += (@platform_notes[platform] - @notes).map {|m| "[#{platform}] #{m}"}
end
end
valid?
end
def valid?
lenient ? errors.empty? : ( errors.empty? && warnings.empty? )
end
def result_type
......@@ -90,9 +88,13 @@ module Pod
def peform_extensive_analysis
set_up_lint_environment
install_pod
if `which xcodebuild`.strip.empty?
puts "Skipping compilation with `xcodebuild' because it can't be found.\n".yellow if config.verbose?
else
puts "Building with xcodebuild.\n".yellow if config.verbose?
# treat xcodebuild warnings as notes because the spec maintainer might not be the author of the library
xcodebuild_output.each { |msg| ( msg.include?('error: ') ? @platform_errors[@platform] : @platform_notes[@platform] ) << msg }
end
@platform_errors[@platform] += file_patterns_errors
@platform_warnings[@platform] += file_patterns_warnings
tear_down_lint_environment
......
......@@ -93,20 +93,22 @@ module Pod
podspecs.each do |podspec|
linter = Linter.new(podspec)
linter.lenient = true
linter.quick = true
linter.repo_path = dir
linter.lint
if (!@only_errors && linter.result_type != :success) || linter.result_type == :error
case linter.result_type
when :error
invalid_count += 1
color = :red
should_display = true
when :warning
color = :yellow
should_display = !@only_errors
end
if should_display
puts " -> ".send(color) << linter.spec_name
print_messages('ERROR', linter.errors)
unless @only_errors
......
......@@ -11,12 +11,11 @@ module Pod
Creates a PodSpec, in the current working dir, called `NAME.podspec'.
If a GitHub url is passed the spec is prepopulated.
$ pod spec lint [ NAME.podspec | DIRECTORY | http://PATH/NAME.podspec ]
$ pod spec lint [ NAME.podspec | DIRECTORY | http://PATH/NAME.podspec, ... ]
Validates `NAME.podspec'. If a directory is provided it performs a quick
validation on all the podspec files found, including subfolders. In case
the argument is omitted, it defaults to the current working dir.
}
Validates `NAME.podspec'. If a directory is provided it validates
the podspec files found, including subfolders. In case
the argument is omitted, it defaults to the current working dir.}
end
def self.options
......@@ -31,16 +30,15 @@ module Pod
@name_or_url = argv.shift_argument
@url = argv.shift_argument
super if @name_or_url.nil?
super unless argv.empty?
elsif @action == 'lint'
@quick = argv.option('--quick')
@only_errors = argv.option('--only-errors')
@no_clean = argv.option('--no-clean')
@repo_or_podspec = argv.shift_argument unless argv.empty?
super unless argv.size <= 1
@podspecs_paths = argv
else
super
end
super unless argv.empty?
end
def run
......@@ -72,58 +70,49 @@ module Pod
def lint
puts
invalid_count = lint_podspecs
count = podspecs_to_lint.count
if invalid_count == 0
lint_passed_message = count == 1 ? "#{podspecs_to_lint.first.basename} passed validation." : "All the specs passed validation."
puts lint_passed_message.green << "\n\n" unless config.silent?
else
raise Informative, count == 1 ? "The spec did not pass validation." : "#{invalid_count} out of #{count} specs failed validation."
end
podspecs_tmp_dir.rmtree if podspecs_tmp_dir.exist?
end
private
def lint_podspecs
invalid_count = 0
podspecs_to_lint.each do |podspec|
linter = Linter.new(podspec)
linter.lenient = @only_errors
linter.quick = @quick || @multiple_files
linter.quick = @quick
linter.no_clean = @no_clean
# Show immediatly which pod is being processed.
print " -> #{linter.spec_name}\r" unless config.silent? || @multiple_files
print " -> #{linter.spec_name}\r" unless config.silent?
$stdout.flush
invalid_count += 1 unless linter.lint
linter.lint
case linter.result_type
when :error
invalid_count += 1
color = :red
when :warning
invalid_count += 1 unless @only_errors
color = :yellow
else
color = :green
end
# This overwrites the previously printed text
puts " -> ".send(lint_result_color(linter)) << linter.spec_name unless config.silent? || should_skip?(linter)
puts " -> ".send(color) << linter.spec_name unless config.silent?
print_messages('ERROR', linter.errors)
print_messages('WARN', linter.warnings)
print_messages('NOTE', linter.notes)
puts unless config.silent? || should_skip?(linter)
end
puts "Analyzed #{podspecs_to_lint.count} podspecs files.\n\n" if @multiple_files && !config.silent?
invalid_count
puts unless config.silent?
end
def lint_result_color(linter)
if linter.errors.empty? && linter.warnings.empty?
:green
elsif linter.errors.empty?
:yellow
puts "Analyzed #{podspecs_to_lint.count} podspecs files.\n\n" unless config.silent?
count = podspecs_to_lint.count
if invalid_count == 0
lint_passed_message = count == 1 ? "#{podspecs_to_lint.first.basename} passed validation." : "All the specs passed validation."
puts lint_passed_message.green << "\n\n" unless config.silent?
else
:red
raise Informative, count == 1 ? "The spec did not pass validation." : "#{invalid_count} out of #{count} specs failed validation."
end
podspecs_tmp_dir.rmtree if podspecs_tmp_dir.exist?
end
def should_skip?(linter)
@multiple_files && linter.errors.empty? && linter.warnings.empty? && linter.notes.empty?
end
private
def print_messages(type, messages)
return if config.silent?
......@@ -132,24 +121,25 @@ module Pod
def podspecs_to_lint
@podspecs_to_lint ||= begin
if @repo_or_podspec =~ /https?:\/\//
files = []
@podspecs_paths << '.' if @podspecs_paths.empty?
@podspecs_paths.each do |path|
if path =~ /https?:\/\//
require 'open-uri'
output_path = podspecs_tmp_dir + File.basename(@repo_or_podspec)
output_path = podspecs_tmp_dir + File.basename(path)
output_path.dirname.mkpath
open(@repo_or_podspec) do |io|
open(path) do |io|
output_path.open('w') { |f| f << io.read }
end
return [output_path]
end
path = Pathname.new(@repo_or_podspec || '.')
if path.directory?
files = path.glob('**/*.podspec')
files << output_path
else if (pathname = Pathname.new(path)).directory?
files += pathname.glob('**/*.podspec')
raise Informative, "No specs found in the current directory." if files.empty?
@multiple_files = true
else
files = [path]
raise Informative, "Unable to find a spec named `#{@repo_or_podspec}'." unless files[0].exist? && @repo_or_podspec.include?('.podspec')
files << (pathname = Pathname.new(path))
raise Informative, "Unable to find a spec named `#{path}'." unless pathname.exist? && path.include?('.podspec')
end
end
end
files
end
......
......@@ -19,16 +19,18 @@ describe "Pod::Command::Linter" do
it "fails a specifications that does not contain the minimum required attributes" do
file = write_podspec('Pod::Spec.new do |s| end')
linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = true, true
linter.lint.should == false
linter.quick = true
linter.lint
linter.result_type.should == :error
linter.errors.join(' | ') =~ /name.*version.*summary.*homepage.*authors.*(source.*part_of).*source_files/
end
it "fails specifications if the name does not match the name of the file" do
file = write_podspec(stub_podspec(/s.name *= 'JSONKit'/, "s.name = 'JSONKitAAA'"))
linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = true, true
linter.lint.should == false
linter.quick = true
linter.lint
linter.result_type.should == :error
linter.errors.count.should == 1
linter.errors[0].should =~ /The name of the spec should match the name of the file/
end
......@@ -36,8 +38,9 @@ describe "Pod::Command::Linter" do
it "fails a specification if a path starts with a slash" do
file = write_podspec(stub_podspec(/s.source_files = 'JSONKit\.\*'/, "s.source_files = '/JSONKit.*'"))
linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = true, true
linter.lint.should == false
linter.quick = true
linter.lint
linter.result_type.should == :error
linter.errors.count.should == 1
linter.errors[0].should =~ /Paths cannot start with a slash/
end
......@@ -45,8 +48,9 @@ describe "Pod::Command::Linter" do
it "fails a specification if the platform is unrecognized" do
file = write_podspec(stub_podspec(/s.name *= 'JSONKit'/, "s.name = 'JSONKit'\ns.platform = :iososx\n"))
linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = true, true
linter.lint.should == false
linter.quick = true
linter.lint
linter.result_type.should == :error
linter.errors.count.should == 1
linter.errors[0].should =~ /Unrecognized platfrom/
end
......@@ -54,19 +58,19 @@ describe "Pod::Command::Linter" do
it "fails validation if the specification contains warnings" do
file = write_podspec(stub_podspec(/.*license.*/, ""))
linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = false, true
linter.lint.should == false
linter.quick = true
linter.lint
linter.result_type.should == :warning
linter.errors.should.be.empty
linter.warnings.should.not.be.empty
end
it "validates in lenient mode if there are no errors but there are warnings" do
it "correctly report specification that only contain warnings" do
file = write_podspec(stub_podspec(/.*license.*/, ""))
linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = true, true
linter.lint.should == true
linter.errors.should.be.empty
linter.warnings.should.not.be.empty
linter.quick = true
linter.lint
linter.result_type.should == :warning
end
it "respects quick mode" do
......@@ -76,15 +80,16 @@ describe "Pod::Command::Linter" do
linter.expects(:install_pod).never
linter.expects(:xcodebuild_output_for_platfrom).never
linter.expects(:file_patterns_errors_for_platfrom).never
linter.lenient, linter.quick = false, true
linter.quick = true
linter.lint
end
it "produces deprecation notices" do
file = write_podspec(stub_podspec(/s\.source_files = 'JSONKit\.\*'/, "s.source_files = 'JSONKit.*'\n if config.ios?\nend"))
linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = false, true
linter.lint.should == false
linter.quick = true
linter.lint
linter.result_type.should == :error
linter.warnings.should.be.empty
linter.errors.join(' | ').should =~ /`config.ios\?' and `config.osx\?' are deprecated/
end
......@@ -92,8 +97,8 @@ describe "Pod::Command::Linter" do
it "uses xcodebuild to generate notes and warnings" do
file = write_podspec(stub_podspec)
linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = false, false
linter.lint.should == false
linter.lint
linter.result_type.should == :warning
linter.notes.join(' | ').should.include "JSONKit/JSONKit.m:1640:27: warning: equality comparison with extraneous parentheses" unless `which xcodebuild`.strip.empty?
end
......@@ -101,8 +106,9 @@ describe "Pod::Command::Linter" do
file = write_podspec(stub_podspec(/s\.source_files = 'JSONKit\.\*'/, "s.source_files = 'JSONKit.*'\ns.resources = 'WRONG_FOLDER'"))
linter = Pod::Command::Spec::Linter.new(file)
linter.stubs(:xcodebuild_output).returns([])
linter.lenient, linter.quick = false, false
linter.lint.should == false
linter.quick = false
linter.lint
linter.result_type.should == :error
linter.errors.join(' | ').should.include "The resources did not match any file"
end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment