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