Commit 28c50294 authored by Fabio Pelosin's avatar Fabio Pelosin

[Linter] Extracted the Linter and added `pod repo lint`.

Closes #423.
parent f500613a
...@@ -5,6 +5,7 @@ module Pod ...@@ -5,6 +5,7 @@ module Pod
autoload :ErrorReport, 'cocoapods/command/error_report' autoload :ErrorReport, 'cocoapods/command/error_report'
autoload :Install, 'cocoapods/command/install' autoload :Install, 'cocoapods/command/install'
autoload :List, 'cocoapods/command/list' autoload :List, 'cocoapods/command/list'
autoload :Linter, 'cocoapods/command/linter'
autoload :Presenter, 'cocoapods/command/presenter' autoload :Presenter, 'cocoapods/command/presenter'
autoload :Push, 'cocoapods/command/push' autoload :Push, 'cocoapods/command/push'
autoload :Repo, 'cocoapods/command/repo' autoload :Repo, 'cocoapods/command/repo'
......
module Pod
class Command
class Linter
include Config::Mixin
# TODO: Add check to ensure that attributes inherited by subspecs are not duplicated ?
attr_accessor :quick, :lenient, :no_clean
attr_reader :spec, :file
attr_reader :errors, :warnings, :notes
def initialize(podspec)
@file = podspec
end
def spec_name
file.basename('.*').to_s
end
# Takes an array of podspec files and lints them all
#
# It returns true if the spec passed validation
#
def lint
@errors, @warnings, @notes = [], [], []
@platform_errors, @platform_warnings, @platform_notes = {}, {}, {}
if !deprecation_errors.empty?
@errors = deprecation_errors
else
@spec = Specification.from_file(file)
platforms = spec.available_platforms
platforms.each do |platform|
@platform_errors[platform], @platform_warnings[platform], @platform_notes[platform] = [], [], []
spec.activate_platform(platform)
@platform = platform
puts "\n\n#{spec} - Analyzing on #{platform} platform.".green.reversed if config.verbose? && !@quick
# Skip validation if there are errors in the podspec as it would result in a crash
if !podspec_errors.empty?
@platform_errors[platform] += podspec_errors
@platform_notes[platform] << "#{platform.name} [!] Fatal errors found skipping the rest of the validation"
else
@platform_warnings[platform] += podspec_warnings
peform_extensive_analysis unless quick
end
end
# Get common messages
@errors = @platform_errors.values.reduce(:&)
@warnings = @platform_warnings.values.reduce(:&)
@notes = @platform_notes.values.reduce(:&)
platforms.each do |platform|
# Mark platform specific messages
@errors += (@platform_errors[platform] - @errors).map {|m| "[#{platform}] #{m}"}
@warnings += (@platform_warnings[platform] - @warnings).map {|m| "[#{platform}] #{m}"}
@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
return :error unless errors.empty?
return :warning unless warnings.empty?
return :note unless notes.empty?
:success
end
# Performs platform specific analysis.
# It requires to download the source at each iteration
#
def peform_extensive_analysis
set_up_lint_environment
install_pod
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 }
@platform_errors[@platform] += file_patterns_errors
@platform_warnings[@platform] += file_patterns_warnings
tear_down_lint_environment
end
def install_pod
podfile = podfile_from_spec
config.verbose
installer = Installer.new(podfile)
installer.install!
@pod = installer.pods.find { |pod| pod.top_specification == spec }
config.silent
end
def podfile_from_spec
name = spec.name
podspec = file.realpath.to_s
platform = @platform
podfile = Pod::Podfile.new do
platform(platform.to_sym, platform.deployment_target)
pod name, :podspec => podspec
end
podfile
end
def set_up_lint_environment
tmp_dir.rmtree if tmp_dir.exist?
tmp_dir.mkpath
@original_config = Config.instance.clone
config.project_root = tmp_dir
config.project_pods_root = tmp_dir + 'Pods'
config.silent = !config.verbose
config.integrate_targets = false
config.generate_docs = false
end
def tear_down_lint_environment
tmp_dir.rmtree unless no_clean
Config.instance = @original_config
end
def tmp_dir
Pathname.new('/tmp/CocoaPods/Lint')
end
def pod_dir
tmp_dir + 'Pods' + spec.name
end
# It reads a podspec file and checks for strings corresponding
# to features that are or will be deprecated
#
# @return [Array<String>]
#
def deprecation_errors
text = @file.read
deprecations = []
deprecations << "`config.ios?' and `config.osx?' are deprecated" if text. =~ /config\..?os.?/
deprecations << "clean_paths are deprecated and ignored (use preserve_paths)" if text. =~ /clean_paths/
deprecations
end
# @return [Array<String>] List of the fatal defects detected in a podspec
def podspec_errors
messages = []
messages << "The name of the spec should match the name of the file" unless names_match?
messages << "Unrecognized platfrom" unless platform_valid?
messages << "Missing name" unless spec.name
messages << "Missing version" unless spec.version
messages << "Missing summary" unless spec.summary
messages << "Missing homepage" unless spec.homepage
messages << "Missing author(s)" unless spec.authors
messages << "Missing or invalid source: #{spec.source}" unless source_valid?
# attributes with multiplatform values
return messages unless platform_valid?
messages << "The spec appears to be empty (no source files, resources, or preserve paths)" if spec.source_files.empty? && spec.subspecs.empty? && spec.resources.empty? && spec.preserve_paths.empty?
messages += paths_starting_with_a_slash_errors
messages += deprecation_errors
messages
end
def names_match?
return true unless spec.name
root_name = spec.name.match(/[^\/]*/)[0]
file.basename.to_s == root_name + '.podspec'
end
def platform_valid?
!spec.platform || [:ios, :osx].include?(spec.platform.name)
end
def source_valid?
spec.source && !(spec.source =~ /http:\/\/EXAMPLE/)
end
def paths_starting_with_a_slash_errors
messages = []
%w[source_files public_header_files resources clean_paths].each do |accessor|
patterns = spec.send(accessor.to_sym)
# Some values are multiplaform
patterns = patterns.is_a?(Hash) ? patterns.values.flatten(1) : patterns
patterns = patterns.compact # some patterns may be nil (public_header_files, for instance)
patterns.each do |pattern|
# Skip FileList that would otherwise be resolved from the working directory resulting
# in a potentially very expensi operation
next if pattern.is_a?(FileList)
invalid = pattern.is_a?(Array) ? pattern.any? { |path| path.start_with?('/') } : pattern.start_with?('/')
if invalid
messages << "Paths cannot start with a slash (#{accessor})"
break
end
end
end
messages
end
# @return [Array<String>] List of the **non** fatal defects detected in a podspec
def podspec_warnings
license = spec.license || {}
source = spec.source || {}
text = @file.read
messages = []
messages << "Missing license type" unless license[:type]
messages << "Sample license type" if license[:type] && license[:type] =~ /\(example\)/
messages << "Invalid license type" if license[:type] && license[:type] =~ /\n/
messages << "The summary is not meaningful" if spec.summary =~ /A short description of/
messages << "The description is not meaningful" if spec.description && spec.description =~ /An optional longer description of/
messages << "The summary should end with a dot" if spec.summary !~ /.*\./
messages << "The description should end with a dot" if spec.description !~ /.*\./ && spec.description != spec.summary
messages << "Git sources should specify either a tag or a commit" if source[:git] && !source[:commit] && !source[:tag]
messages << "Github repositories should end in `.git'" if github_source? && source[:git] !~ /.*\.git/
messages << "Github repositories should use `https' link" if github_source? && source[:git] !~ /https:\/\/github.com/
messages << "Comments must be deleted" if text.scan(/^\s*#/).length > 24
messages
end
def github_source?
spec.source && spec.source[:git] =~ /github.com/
end
# It creates a podfile in memory and builds a library containing
# the pod for all available platfroms with xcodebuild.
#
# @return [Array<String>]
#
def xcodebuild_output
return [] if `which xcodebuild`.strip.empty?
messages = []
output = Dir.chdir(config.project_pods_root) { `xcodebuild clean build 2>&1` }
clean_output = process_xcode_build_output(output)
messages += clean_output
puts(output) if config.verbose?
messages
end
def process_xcode_build_output(output)
output_by_line = output.split("\n")
selected_lines = output_by_line.select do |l|
l.include?('error: ') && (l !~ /errors? generated\./) && (l !~ /error: \(null\)/)\
|| l.include?('warning: ') && (l !~ /warnings? generated\./)\
|| l.include?('note: ') && (l !~ /expanded from macro/)
end
selected_lines.map do |l|
new = l.gsub(/\/tmp\/CocoaPods\/Lint\/Pods\//,'') # Remove the unnecessary tmp path
new.gsub!(/^ */,' ') # Remove indentation
"XCODEBUILD > " << new # Mark
end
end
# It checks that every file pattern specified in a spec yields
# at least one file. It requires the pods to be alredy present
# in the current working directory under Pods/spec.name.
#
# @return [Array<String>]
#
def file_patterns_errors
messages = []
messages << "The sources did not match any file" if !spec.source_files.empty? && @pod.source_files.empty?
messages << "The resources did not match any file" if !spec.resources.empty? && @pod.resource_files.empty?
messages << "The preserve_paths did not match any file" if !spec.preserve_paths.empty? && @pod.preserve_files.empty?
messages << "The exclude_header_search_paths did not match any file" if !spec.exclude_header_search_paths.empty? && @pod.headers_excluded_from_search_paths.empty?
messages
end
def file_patterns_warnings
messages = []
unless @pod.license_file || spec.license && ( spec.license[:type] == 'Public Domain' || spec.license[:text] )
messages << "Unable to find a license file"
end
messages
end
end
end
end
...@@ -11,10 +11,16 @@ module Pod ...@@ -11,10 +11,16 @@ module Pod
Clones `URL' in the local spec-repos directory at `~/.cocoapods'. The Clones `URL' in the local spec-repos directory at `~/.cocoapods'. The
remote can later be referred to by `NAME'. remote can later be referred to by `NAME'.
$ pod repo update NAME $ pod repo update [NAME]
Updates the local clone of the spec-repo `NAME'. If `NAME' is omitted Updates the local clone of the spec-repo `NAME'. If `NAME' is omitted
this will update all spec-repos in `~/.cocoapods'.} this will update all spec-repos in `~/.cocoapods'.
$ pod repo update [NAME | DIRECTORY]
Lints the spec-repo `NAME'. If a directory is provided it is assumed
to be the root of a repo. Finally, if NAME is not provided this will
lint all the spec-repos known to CocoaPods.}
end end
extend Executable extend Executable
...@@ -27,7 +33,7 @@ module Pod ...@@ -27,7 +33,7 @@ module Pod
raise Informative, "#{@action == 'add' ? 'Adding' : 'Updating the remote of'} a repo needs a `name' and a `url'." raise Informative, "#{@action == 'add' ? 'Adding' : 'Updating the remote of'} a repo needs a `name' and a `url'."
end end
@branch = argv.arguments[3] @branch = argv.arguments[3]
when 'update' when 'update', 'lint'
@name = argv.arguments[1] @name = argv.arguments[1]
else else
super super
...@@ -66,6 +72,50 @@ module Pod ...@@ -66,6 +72,50 @@ module Pod
end end
end end
def lint
if @name
dirs = File.exists?(@name) ? [ Pathname.new(@name) ] : [ dir ]
else
dirs = config.repos_dir.children.select {|c| c.directory?}
end
dirs.each do |dir|
check_versions(dir)
puts "\nLinting spec repo `#{dir.realpath.basename}'\n".yellow
podspecs = dir.glob('**/*.podspec')
invalid_count = 0
podspecs.each do |podspec|
linter = Linter.new(podspec)
linter.lenient = true
linter.quick = true
linter.lint
unless linter.result_type == :success
invalid_count += 1
case linter.result_type
when :error
color = :red
when :warning
color = :yellow
end
puts " -> ".send(color) << linter.spec_name
print_messages('ERROR', linter.errors)
print_messages('WARN', linter.warnings)
print_messages('NOTE', linter.notes)
puts unless config.silent?
end
end
puts "Analyzed #{podspecs.count} podspecs files.\n\n" unless config.silent?
invalid_count
end
end
def print_messages(type, messages)
return if config.silent?
messages.each {|msg| puts " - #{type.ljust(5)} | #{msg}"}
end
def check_versions(dir) def check_versions(dir)
versions = versions(dir) versions = versions(dir)
unless is_compatilbe(versions) unless is_compatilbe(versions)
......
...@@ -73,7 +73,7 @@ module Pod ...@@ -73,7 +73,7 @@ module Pod
def lint def lint
puts puts
invalid_count = lint_podspecs invalid_count = lint_podspecs
count = specs_to_lint.count count = podspecs_to_lint.count
if invalid_count == 0 if invalid_count == 0
lint_passed_message = count == 1 ? "#{podspecs_to_lint.first.basename} passed validation." : "All the specs passed validation." 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? puts lint_passed_message.green << "\n\n" unless config.silent?
...@@ -87,26 +87,27 @@ module Pod ...@@ -87,26 +87,27 @@ module Pod
def lint_podspecs def lint_podspecs
invalid_count = 0 invalid_count = 0
specs_to_lint.each do |spec| podspecs_to_lint.each do |podspec|
# Show immediatly which pod is being processed. linter = Linter.new(podspec)
print " -> #{spec}\r" unless config.silent? || @multiple_files
$stdout.flush
linter = Linter.new(spec)
linter.lenient = @only_errors linter.lenient = @only_errors
linter.quick = @quick || @multiple_files linter.quick = @quick || @multiple_files
linter.no_clean = @no_clean linter.no_clean = @no_clean
# Show immediatly which pod is being processed.
print " -> #{linter.spec_name}\r" unless config.silent? || @multiple_files
$stdout.flush
invalid_count += 1 unless linter.lint invalid_count += 1 unless linter.lint
# This overwrites the previously printed text # This overwrites the previously printed text
puts " -> ".send(lint_result_color(linter)) << spec.to_s unless config.silent? || should_skip?(linter) puts " -> ".send(lint_result_color(linter)) << linter.spec_name unless config.silent? || should_skip?(linter)
print_messages(spec, 'ERROR', linter.errors) print_messages('ERROR', linter.errors)
print_messages(spec, 'WARN', linter.warnings) print_messages('WARN', linter.warnings)
print_messages(spec, 'NOTE', linter.notes) print_messages('NOTE', linter.notes)
puts unless config.silent? || should_skip?(linter) puts unless config.silent? || should_skip?(linter)
end end
puts "Analyzed #{specs_to_lint.count} specs in #{podspecs_to_lint.count} podspecs files.\n\n" if @multiple_files && !config.silent? puts "Analyzed #{podspecs_to_lint.count} podspecs files.\n\n" if @multiple_files && !config.silent?
invalid_count invalid_count
end end
...@@ -124,7 +125,7 @@ module Pod ...@@ -124,7 +125,7 @@ module Pod
@multiple_files && linter.errors.empty? && linter.warnings.empty? && linter.notes.empty? @multiple_files && linter.errors.empty? && linter.warnings.empty? && linter.notes.empty?
end end
def print_messages(spec, type, messages) def print_messages(type, messages)
return if config.silent? return if config.silent?
messages.each {|msg| puts " - #{type.ljust(5)} | #{msg}"} messages.each {|msg| puts " - #{type.ljust(5)} | #{msg}"}
end end
...@@ -158,278 +159,6 @@ module Pod ...@@ -158,278 +159,6 @@ module Pod
Pathname.new('/tmp/CocoaPods/Lint_podspec') Pathname.new('/tmp/CocoaPods/Lint_podspec')
end end
def specs_to_lint
@specs_to_lint ||= begin
podspecs_to_lint.map do |podspec|
root_spec = Specification.from_file(podspec)
# TODO find a way to lint subspecs
# root_spec.preferred_dependency ? root_spec.subspec_dependencies : root_spec
end.flatten
end
end
# Linter class
#
class Linter
include Config::Mixin
# TODO: Add check to ensure that attributes inherited by subspecs are not duplicated ?
attr_accessor :quick, :lenient, :no_clean
attr_reader :spec, :file
attr_reader :errors, :warnings, :notes
def initialize(spec)
@spec = spec
@file = spec.defined_in_file.realpath
end
# Takes an array of podspec files and lints them all
#
# It returns true if the spec passed validation
#
def lint
@platform_errors, @platform_warnings, @platform_notes = {}, {}, {}
platforms = @spec.available_platforms
platforms.each do |platform|
@platform_errors[platform], @platform_warnings[platform], @platform_notes[platform] = [], [], []
@spec.activate_platform(platform)
@platform = platform
puts "\n\n#{spec} - Analyzing on #{platform} platform.".green.reversed if config.verbose? && !@quick
# Skip validation if there are errors in the podspec as it would result in a crash
if !podspec_errors.empty?
@platform_errors[platform] += podspec_errors
@platform_notes[platform] << "#{platform.name} [!] Fatal errors found skipping the rest of the validation"
else
@platform_warnings[platform] += podspec_warnings + deprecation_warnings
peform_extensive_analysis unless quick
end
end
# Get common messages
@errors = @platform_errors.values.reduce(:&) || []
@warnings = @platform_warnings.values.reduce(:&) || []
@notes = @platform_notes.values.reduce(:&) || []
platforms.each do |platform|
# Mark platform specific messages
@errors += (@platform_errors[platform] - @errors).map {|m| "[#{platform}] #{m}"}
@warnings += (@platform_warnings[platform] - @warnings).map {|m| "[#{platform}] #{m}"}
@notes += (@platform_notes[platform] - @notes).map {|m| "[#{platform}] #{m}"}
end
valid?
end
def valid?
lenient ? errors.empty? : ( errors.empty? && warnings.empty? )
end
# Performs platform specific analysis.
# It requires to download the source at each iteration
#
def peform_extensive_analysis
set_up_lint_environment
install_pod
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 }
@platform_errors[@platform] += file_patterns_errors
@platform_warnings[@platform] += file_patterns_warnings
tear_down_lint_environment
end
def install_pod
podfile = podfile_from_spec
config.verbose
installer = Installer.new(podfile)
installer.install!
@pod = installer.pods.find { |pod| pod.top_specification == @spec }
config.silent
end
def podfile_from_spec
name = spec.name
podspec = file.realpath.to_s
platform = @platform
podfile = Pod::Podfile.new do
platform(platform.to_sym, platform.deployment_target)
pod name, :podspec => podspec
end
podfile
end
def set_up_lint_environment
tmp_dir.rmtree if tmp_dir.exist?
tmp_dir.mkpath
@original_config = Config.instance.clone
config.project_root = tmp_dir
config.project_pods_root = tmp_dir + 'Pods'
config.silent = !config.verbose
config.integrate_targets = false
config.generate_docs = false
end
def tear_down_lint_environment
tmp_dir.rmtree unless no_clean
Config.instance = @original_config
end
def tmp_dir
Pathname.new('/tmp/CocoaPods/Lint')
end
def pod_dir
tmp_dir + 'Pods' + spec.name
end
# @return [Array<String>] List of the fatal defects detected in a podspec
def podspec_errors
messages = []
messages << "The name of the spec should match the name of the file" unless names_match?
messages << "Unrecognized platfrom" unless platform_valid?
messages << "Missing name" unless spec.name
messages << "Missing version" unless spec.version
messages << "Missing summary" unless spec.summary
messages << "Missing homepage" unless spec.homepage
messages << "Missing author(s)" unless spec.authors
messages << "Missing or invalid source: #{spec.source}" unless source_valid?
# attributes with multiplatform values
return messages unless platform_valid?
messages << "The spec appears to be empty (no source files, resources, or preserve paths)" if spec.source_files.empty? && spec.subspecs.empty? && spec.resources.empty? && spec.preserve_paths.empty?
messages += paths_starting_with_a_slash_errors
messages
end
def names_match?
return true unless spec.name
root_name = spec.name.match(/[^\/]*/)[0]
file.basename.to_s == root_name + '.podspec'
end
def platform_valid?
!spec.platform || [:ios, :osx].include?(spec.platform.name)
end
def source_valid?
spec.source && !(spec.source =~ /http:\/\/EXAMPLE/)
end
def paths_starting_with_a_slash_errors
messages = []
%w[source_files public_header_files resources clean_paths].each do |accessor|
patterns = spec.send(accessor.to_sym)
# Some values are multiplaform
patterns = patterns.is_a?(Hash) ? patterns.values.flatten(1) : patterns
patterns = patterns.compact # some patterns may be nil (public_header_files, for instance)
patterns.each do |pattern|
# Skip FileList that would otherwise be resolved from the working directory resulting
# in a potentially very expensi operation
next if pattern.is_a?(FileList)
invalid = pattern.is_a?(Array) ? pattern.any? { |path| path.start_with?('/') } : pattern.start_with?('/')
if invalid
messages << "Paths cannot start with a slash (#{accessor})"
break
end
end
end
messages
end
# @return [Array<String>] List of the **non** fatal defects detected in a podspec
def podspec_warnings
license = @spec.license || {}
source = @spec.source || {}
text = @file.read
messages = []
messages << "Missing license type" unless license[:type]
messages << "Sample license type" if license[:type] && license[:type] =~ /\(example\)/
messages << "Invalid license type" if license[:type] && license[:type] =~ /\n/
messages << "The summary is not meaningful" if spec.summary =~ /A short description of/
messages << "The description is not meaningful" if spec.description && spec.description =~ /An optional longer description of/
messages << "The summary should end with a dot" if @spec.summary !~ /.*\./
messages << "The description should end with a dot" if @spec.description !~ /.*\./ && @spec.description != @spec.summary
messages << "Git sources should specify either a tag or a commit" if source[:git] && !source[:commit] && !source[:tag]
messages << "Github repositories should end in `.git'" if github_source? && source[:git] !~ /.*\.git/
messages << "Github repositories should use `https' link" if github_source? && source[:git] !~ /https:\/\/github.com/
messages << "Comments must be deleted" if text.scan(/^\s*#/).length > 24
messages
end
def github_source?
@spec.source && @spec.source[:git] =~ /github.com/
end
# It reads a podspec file and checks for strings corresponding
# to features that are or will be deprecated
#
# @return [Array<String>]
#
def deprecation_warnings
text = @file.read
deprecations = []
deprecations << "`config.ios?' and `config.osx?' are deprecated" if text. =~ /config\..?os.?/
deprecations << "clean_paths are deprecated and ignored (use preserve_paths)" if text. =~ /clean_paths/
deprecations
end
# It creates a podfile in memory and builds a library containing
# the pod for all available platfroms with xcodebuild.
#
# @return [Array<String>]
#
def xcodebuild_output
return [] if `which xcodebuild`.strip.empty?
messages = []
output = Dir.chdir(config.project_pods_root) { `xcodebuild clean build 2>&1` }
clean_output = process_xcode_build_output(output)
messages += clean_output
puts(output) if config.verbose?
messages
end
def process_xcode_build_output(output)
output_by_line = output.split("\n")
selected_lines = output_by_line.select do |l|
l.include?('error: ') && (l !~ /errors? generated\./) && (l !~ /error: \(null\)/)\
|| l.include?('warning: ') && (l !~ /warnings? generated\./)\
|| l.include?('note: ') && (l !~ /expanded from macro/)
end
selected_lines.map do |l|
new = l.gsub(/\/tmp\/CocoaPods\/Lint\/Pods\//,'') # Remove the unnecessary tmp path
new.gsub!(/^ */,' ') # Remove indentation
"XCODEBUILD > " << new # Mark
end
end
# It checks that every file pattern specified in a spec yields
# at least one file. It requires the pods to be alredy present
# in the current working directory under Pods/spec.name.
#
# @return [Array<String>]
#
def file_patterns_errors
messages = []
messages << "The sources did not match any file" if !@spec.source_files.empty? && @pod.source_files.empty?
messages << "The resources did not match any file" if !@spec.resources.empty? && @pod.resource_files.empty?
messages << "The preserve_paths did not match any file" if !@spec.preserve_paths.empty? && @pod.preserve_files.empty?
messages << "The exclude_header_search_paths did not match any file" if !@spec.exclude_header_search_paths.empty? && @pod.headers_excluded_from_search_paths.empty?
messages
end
def file_patterns_warnings
messages = []
unless @pod.license_file || @spec.license && ( @spec.license[:type] == 'Public Domain' || @spec.license[:text] )
messages << "Unable to find a license file"
end
messages
end
end
# Templates and github information retrival for spec create # Templates and github information retrival for spec create
def default_data_for_template(name) def default_data_for_template(name)
......
...@@ -9,6 +9,7 @@ describe "Pod::Command::Repo" do ...@@ -9,6 +9,7 @@ describe "Pod::Command::Repo" do
it "runs with correct parameters" do it "runs with correct parameters" do
lambda { run_command('repo', 'add', 'NAME', 'URL') }.should.not.raise lambda { run_command('repo', 'add', 'NAME', 'URL') }.should.not.raise
lambda { run_command('repo', 'update') }.should.not.raise lambda { run_command('repo', 'update') }.should.not.raise
lambda { run_command('repo', 'lint', temporary_directory.to_s) }.should.not.raise
end end
it "complains for wrong parameters" do it "complains for wrong parameters" do
...@@ -49,6 +50,16 @@ describe "Pod::Command::Repo" do ...@@ -49,6 +50,16 @@ describe "Pod::Command::Repo" do
(repo2.dir + 'README').read.should.include 'Added!' (repo2.dir + 'README').read.should.include 'Added!'
(repo3.dir + 'README').read.should.include 'Added!' (repo3.dir + 'README').read.should.include 'Added!'
end end
before do
config.repos_dir = fixture('spec-repos')
end
it "lints a repo" do
cmd = command('repo', 'lint', 'master')
lambda { cmd.run }.should.not.raise Pod::Informative
cmd.output.should.include "Missing license type"
end
end end
describe "Concerning a repo support" do describe "Concerning a repo support" do
......
...@@ -97,17 +97,6 @@ describe "Pod::Command::Spec#lint" do ...@@ -97,17 +97,6 @@ describe "Pod::Command::Spec#lint" do
extend SpecHelper::TemporaryDirectory extend SpecHelper::TemporaryDirectory
extend SpecHelper::TemporaryRepos extend SpecHelper::TemporaryRepos
before do
config.repos_dir = fixture('spec-repos')
end
it "lints a repo" do
# The fixture has warnings so it raises
cmd = command('spec', 'lint', "#{config.repos_dir}/master")
lambda { cmd.run }.should.raise Pod::Informative
cmd.output.should.include "WARN"
end
it "complains if it can't find any spec to lint" do it "complains if it can't find any spec to lint" do
Dir.chdir(temporary_directory) do Dir.chdir(temporary_directory) do
lambda { command('spec', 'lint').run }.should.raise Pod::Informative lambda { command('spec', 'lint').run }.should.raise Pod::Informative
......
require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../../../spec_helper', __FILE__)
describe "Pod::Command::Spec::Linter" do describe "Pod::Command::Linter" do
extend SpecHelper::TemporaryDirectory extend SpecHelper::TemporaryDirectory
def write_podspec(text, name = 'JSONKit.podspec') def write_podspec(text, name = 'JSONKit.podspec')
file = temporary_directory + 'JSONKit.podspec' file = temporary_directory + 'JSONKit.podspec'
File.open(file, 'w') {|f| f.write(text) } File.open(file, 'w') {|f| f.write(text) }
spec = Pod::Specification.from_file(file) file
[spec, file]
end end
def stub_podspec(pattern = nil, replacement = nil) def stub_podspec(pattern = nil, replacement = nil)
...@@ -18,16 +17,16 @@ describe "Pod::Command::Spec::Linter" do ...@@ -18,16 +17,16 @@ describe "Pod::Command::Spec::Linter" do
end end
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
spec, file = write_podspec('Pod::Spec.new do |s| end') file = write_podspec('Pod::Spec.new do |s| end')
linter = Pod::Command::Spec::Linter.new(spec) linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = true, true linter.lenient, linter.quick = true, true
linter.lint.should == false linter.lint.should == false
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
spec, 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(spec) linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = true, true linter.lenient, linter.quick = true, true
linter.lint.should == false linter.lint.should == false
linter.errors.count.should == 1 linter.errors.count.should == 1
...@@ -35,8 +34,8 @@ describe "Pod::Command::Spec::Linter" do ...@@ -35,8 +34,8 @@ describe "Pod::Command::Spec::Linter" do
end end
it "fails a specification if a path starts with a slash" do it "fails a specification if a path starts with a slash" do
spec, 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(spec) linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = true, true linter.lenient, linter.quick = true, true
linter.lint.should == false linter.lint.should == false
linter.errors.count.should == 1 linter.errors.count.should == 1
...@@ -44,8 +43,8 @@ describe "Pod::Command::Spec::Linter" do ...@@ -44,8 +43,8 @@ describe "Pod::Command::Spec::Linter" do
end end
it "fails a specification if the platform is unrecognized" do it "fails a specification if the platform is unrecognized" do
spec, 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(spec) linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = true, true linter.lenient, linter.quick = true, true
linter.lint.should == false linter.lint.should == false
linter.errors.count.should == 1 linter.errors.count.should == 1
...@@ -53,8 +52,8 @@ describe "Pod::Command::Spec::Linter" do ...@@ -53,8 +52,8 @@ describe "Pod::Command::Spec::Linter" do
end end
it "fails validation if the specification contains warnings" do it "fails validation if the specification contains warnings" do
spec, file = write_podspec(stub_podspec(/.*license.*/, "")) file = write_podspec(stub_podspec(/.*license.*/, ""))
linter = Pod::Command::Spec::Linter.new(spec) linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = false, true linter.lenient, linter.quick = false, true
linter.lint.should == false linter.lint.should == false
linter.errors.should.be.empty linter.errors.should.be.empty
...@@ -62,8 +61,8 @@ describe "Pod::Command::Spec::Linter" do ...@@ -62,8 +61,8 @@ describe "Pod::Command::Spec::Linter" do
end end
it "validates in lenient mode if there are no errors but there are warnings" do it "validates in lenient mode if there are no errors but there are warnings" do
spec, file = write_podspec(stub_podspec(/.*license.*/, "")) file = write_podspec(stub_podspec(/.*license.*/, ""))
linter = Pod::Command::Spec::Linter.new(spec) linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = true, true linter.lenient, linter.quick = true, true
linter.lint.should == true linter.lint.should == true
linter.errors.should.be.empty linter.errors.should.be.empty
...@@ -71,8 +70,8 @@ describe "Pod::Command::Spec::Linter" do ...@@ -71,8 +70,8 @@ describe "Pod::Command::Spec::Linter" do
end end
it "respects quick mode" do it "respects quick mode" do
spec, file = write_podspec(stub_podspec) file = write_podspec(stub_podspec)
linter = Pod::Command::Spec::Linter.new(spec) linter = Pod::Command::Spec::Linter.new(file)
linter.expects(:peform_multiplatform_analysis).never linter.expects(:peform_multiplatform_analysis).never
linter.expects(:install_pod).never linter.expects(:install_pod).never
linter.expects(:xcodebuild_output_for_platfrom).never linter.expects(:xcodebuild_output_for_platfrom).never
...@@ -82,25 +81,25 @@ describe "Pod::Command::Spec::Linter" do ...@@ -82,25 +81,25 @@ describe "Pod::Command::Spec::Linter" do
end end
it "produces deprecation notices" do it "produces deprecation notices" do
spec, 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(spec) linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = false, true linter.lenient, linter.quick = false, true
linter.lint.should == false linter.lint.should == false
linter.errors.should.be.empty linter.warnings.should.be.empty
linter.warnings.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
spec, file = write_podspec(stub_podspec) file = write_podspec(stub_podspec)
linter = Pod::Command::Spec::Linter.new(spec) linter = Pod::Command::Spec::Linter.new(file)
linter.lenient, linter.quick = false, false linter.lenient, linter.quick = false, false
linter.lint.should == false linter.lint.should == false
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
spec, 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(spec) linter = Pod::Command::Spec::Linter.new(file)
linter.stubs(:xcodebuild_output).returns([]) linter.stubs(:xcodebuild_output).returns([])
linter.lenient, linter.quick = false, false linter.lenient, linter.quick = false, false
linter.lint.should == false linter.lint.should == false
...@@ -108,9 +107,8 @@ describe "Pod::Command::Spec::Linter" do ...@@ -108,9 +107,8 @@ describe "Pod::Command::Spec::Linter" do
end end
it "uses the deployment target of the specification" do it "uses the deployment target of the specification" do
spec, file = write_podspec(stub_podspec) file = write_podspec(stub_podspec(/s.name *= 'JSONKit'/, "s.name = 'JSONKit'; s.platform = :ios, '5.0'"))
spec.stubs(:available_platforms).returns([Pod::Platform.new(:ios, "5.0")]) linter = Pod::Command::Spec::Linter.new(file)
linter = Pod::Command::Spec::Linter.new(spec)
linter.quick = true linter.quick = true
linter.lint linter.lint
podfile = linter.podfile_from_spec podfile = linter.podfile_from_spec
...@@ -118,3 +116,4 @@ describe "Pod::Command::Spec::Linter" do ...@@ -118,3 +116,4 @@ describe "Pod::Command::Spec::Linter" do
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