Commit 11616843 authored by Fabio Pelosin's avatar Fabio Pelosin

Merge pull request #219 from CocoaPods/pod-push

Pod push, pod spec lint, and minor changes
parents f9e77af9 7f7aa877
source "http://rubygems.org" source "http://rubygems.org"
gem "open4"
gem "colored" gem "colored"
gem "escape" gem "escape"
gem "json" gem "json"
......
...@@ -38,7 +38,6 @@ GEM ...@@ -38,7 +38,6 @@ GEM
faraday_middleware (~> 0.8) faraday_middleware (~> 0.8)
hashie (~> 1.2) hashie (~> 1.2)
multi_json (~> 1.0) multi_json (~> 1.0)
open4 (1.3.0)
pry (0.9.8.4) pry (0.9.8.4)
coderay (~> 1.0.5) coderay (~> 1.0.5)
method_source (~> 0.7.1) method_source (~> 0.7.1)
...@@ -64,7 +63,6 @@ DEPENDENCIES ...@@ -64,7 +63,6 @@ DEPENDENCIES
kicker kicker
mocha-on-bacon mocha-on-bacon
octokit octokit
open4
pry pry
rake rake
rb-fsevent rb-fsevent
......
...@@ -29,7 +29,6 @@ Gem::Specification.new do |s| ...@@ -29,7 +29,6 @@ Gem::Specification.new do |s|
"you are upgrading, first run: $ pod setup" "you are upgrading, first run: $ pod setup"
s.add_runtime_dependency 'xcodeproj', '~> 0.1.0' s.add_runtime_dependency 'xcodeproj', '~> 0.1.0'
s.add_runtime_dependency 'popen4', '~> 0.1.2'
s.add_runtime_dependency 'colored', '~> 1.2' s.add_runtime_dependency 'colored', '~> 1.2'
s.add_runtime_dependency 'escape', '~> 0.0.4' s.add_runtime_dependency 'escape', '~> 0.0.4'
s.add_development_dependency 'bacon', '~> 1.1' s.add_development_dependency 'bacon', '~> 1.1'
......
...@@ -39,3 +39,7 @@ class Pathname ...@@ -39,3 +39,7 @@ class Pathname
end end
end end
if ENV['COCOA_PODS_ENV'] == 'development'
require 'pry'
require 'awesome_print'
end
...@@ -6,6 +6,7 @@ module Pod ...@@ -6,6 +6,7 @@ module Pod
autoload :Install, 'cocoapods/command/install' autoload :Install, 'cocoapods/command/install'
autoload :List, 'cocoapods/command/list' autoload :List, 'cocoapods/command/list'
autoload :Presenter, 'cocoapods/command/presenter' autoload :Presenter, 'cocoapods/command/presenter'
autoload :Push, 'cocoapods/command/push'
autoload :Repo, 'cocoapods/command/repo' autoload :Repo, 'cocoapods/command/repo'
autoload :Search, 'cocoapods/command/search' autoload :Search, 'cocoapods/command/search'
autoload :Setup, 'cocoapods/command/setup' autoload :Setup, 'cocoapods/command/setup'
...@@ -18,12 +19,13 @@ module Pod ...@@ -18,12 +19,13 @@ module Pod
def message def message
[ [
@command_class.banner,
'', '',
'Options', @command_class.banner.gsub(/\$ pod (.*)/, '$ pod \1'.green),
'-------',
'', '',
options 'Options:',
'',
options,
"\n",
].join("\n") ].join("\n")
end end
...@@ -45,14 +47,10 @@ module Pod ...@@ -45,14 +47,10 @@ module Pod
end end
def self.banner def self.banner
"To see help for the available commands run:\n" \ commands = ['install', 'list', 'push', 'repo', 'search', 'setup', 'spec'].sort
"\n" \ banner = "\nTo see help for the available commands run:\n\n"
" * $ pod setup --help\n" \ commands.each {|cmd| banner << " * $ pod #{cmd.green} --help\n"}
" * $ pod search --help\n" \ banner
" * $ pod list --help\n" \
" * $ pod install --help\n" \
" * $ pod repo --help\n" \
" * $ pod spec --help"
end end
def self.options def self.options
...@@ -97,6 +95,7 @@ module Pod ...@@ -97,6 +95,7 @@ module Pod
when 'list' then List when 'list' then List
when 'setup' then Setup when 'setup' then Setup
when 'spec' then Spec when 'spec' then Spec
when 'push' then Push
end end
if show_help || command_class.nil? if show_help || command_class.nil?
......
...@@ -2,7 +2,7 @@ module Pod ...@@ -2,7 +2,7 @@ module Pod
class Command class Command
class Presenter class Presenter
def self.options def self.options
" --stats Show additional stats (like GitHub watchers and forks)\n" [["--stats", "Show additional stats (like GitHub watchers and forks)"]]
end end
autoload :CocoaPod, 'cocoapods/command/presenter/cocoa_pod' autoload :CocoaPod, 'cocoapods/command/presenter/cocoa_pod'
...@@ -43,18 +43,15 @@ module Pod ...@@ -43,18 +43,15 @@ module Pod
txt.strip.gsub(/(.{1,#{col}})( +|$)\n?|(.{#{col}})/, indent + "\\1\\3\n") txt.strip.gsub(/(.{1,#{col}})( +|$)\n?|(.{#{col}})/, indent + "\\1\\3\n")
end end
def detail(title, value, preferred_indentation = 8) def detail(title, value)
# 8 is the length of Homepage
return '' if !value return '' if !value
number_of_spaces = ((preferred_indentation - title.length) > 0) ? (preferred_indentation - title.length) : 0
spaces = ' ' * number_of_spaces
''.tap do |t| ''.tap do |t|
t << " - #{title}:" t << " - #{title}:".ljust(16)
if value.class == Array if value.class == Array
separator = "\n - " separator = "\n - "
t << separator + value.join(separator) t << separator << value.join(separator)
else else
t << " #{spaces + value.to_s}\n" t << value.to_s << "\n"
end end
end end
end end
......
require 'fileutils'
module Pod
class Command
class Push < Command
def self.banner
%{Pushing new specifications to a spec-repo:
$ pod push [REPO]
Validates `*.podspec' in the current working dir, updates
the local copy of the repository named REPO, adds specifications
to REPO, and finally it pushes REPO to its remote.}
end
def self.options
[["--allow-warnings", "Allows to push if warnings are not evitable"]].concat(super)
end
extend Executable
executable :git
def initialize(argv)
@allow_warnings = argv.option('--allow-warnings')
@repo = argv.shift_argument
super unless argv.empty? && @repo
end
def run
validate_podspec_files
check_repo_status
update_repo
add_specs_to_repo
push_repo
end
private
def update_repo
puts "Updating the `#{@repo}' repo\n".yellow unless config.silent
# show the output of git even if not verbose
Dir.chdir(repo_dir) { puts `git pull 2>&1` }
end
def push_repo
puts "\nPushing the `#{@repo}' repo\n".yellow unless config.silent
Dir.chdir(repo_dir) { puts `git push 2>&1` }
end
def repo_dir
dir = config.repos_dir + @repo
raise Informative, "[!] `#{@repo}' repo not found".red unless dir.exist?
dir
end
def check_repo_status
# TODO: add specs for staged and unstaged files (tested manually)
clean = Dir.chdir(repo_dir) { `git status --porcelain 2>&1` } == ''
raise Informative, "[!] `#{@repo}' repo not clean".red unless clean
end
def podspec_files
files = Pathname.glob("*.podspec")
raise Informative, "[!] Couldn't find .podspec file in current directory".red if files.empty?
files
end
def validate_podspec_files
puts "\nValidating specs".yellow unless config.silent
lint_argv = ["lint"]
lint_argv << "--only-errors" if @allow_warnings
lint_argv << "--silent" if config.silent
all_valid = Spec.new(ARGV.new(lint_argv)).run
end
def add_specs_to_repo
puts "\nAdding the specs to the #{@repo} repo\n".yellow unless config.silent
podspec_files.each do |spec_file|
spec = Pod::Specification.from_file(spec_file)
output_path = File.join(repo_dir, spec.name, spec.version.to_s)
if Pathname.new(output_path).exist?
message = "[Fix] #{spec}"
elsif Pathname.new(File.join(repo_dir, spec.name)).exist?
message = "[Update] #{spec}"
else
message = "[Add] #{spec}"
end
puts " - #{message}" unless config.silent
FileUtils.mkdir_p(output_path)
FileUtils.cp(Pathname.new(spec.name+'.podspec'), output_path)
Dir.chdir(repo_dir) do
git("add #{spec.name}")
git("commit -m '#{message}'")
end
end
end
end
end
end
# encoding: utf-8 # encoding: utf-8
require 'net/https'
require 'uri'
require 'octokit'
require 'json'
module Pod module Pod
class Command class Command
class Spec < Command class Spec < Command
def self.banner def self.banner
%{Managing PodSpec files: %{Managing PodSpec files:
$ pod spec create [NAME] $ pod spec create [ NAME | https://github.com/USER/REPO ]
$ pod spec create [https://github.com/USER/REPO]
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] $ pod spec lint [ NAME.podspec | REPO ]
Validates `NAME.podspec'. In case `NAME.podspec' is omitted, it defaults Validates `NAME.podspec'. In case `NAME.podspec' is omitted, it defaults
to `*.podspec' in the current working dir.} to `*.podspec' in the current working dir. If the name of a repo is
provided it validates all its specs.}
end
def self.options
[ ["--quick", "Lint skips checks that would require to donwload and build the spec"],
["--only-errors", "Lint validates even if warnings are present"] ].concat(super)
end end
def initialize(argv) def initialize(argv)
args = argv.arguments @action = argv.shift_argument
unless (args[0] == 'create' && args.size == 2) || if @action == 'create'
(args[0] == 'lint' && args.size <= 2) @name_or_url = argv.shift_argument
@url = argv.shift_argument
super if @name_or_url.nil?
elsif @action == 'lint'
@quick = argv.option('--quick')
@only_errors = argv.option('--only-errors')
@repo_or_podspec = argv.shift_argument unless argv.empty?
super unless argv.size <= 1
else
super super
end end
@action, @name_or_url = args.first(2)
super unless argv.empty?
end end
def run def run
...@@ -37,64 +46,265 @@ module Pod ...@@ -37,64 +46,265 @@ module Pod
end end
def create def create
if repo_id = @name_or_url[/github.com\/([^\/\.]*\/[^\/\.]*)\.*/, 1] if repo_id_match = (@url || @name_or_url).match(/github.com\/([^\/\.]*\/[^\/\.]*)\.*/)
require 'octokit'
repo_id = repo_id_match[1]
data = github_data_for_template(repo_id) data = github_data_for_template(repo_id)
data[:name] = @name_or_url if @url
puts semantic_versioning_notice(repo_id, data[:name]) if data[:version] == '0.0.1' puts semantic_versioning_notice(repo_id, data[:name]) if data[:version] == '0.0.1'
else else
data = default_data_for_template(@name_or_url) data = default_data_for_template(@name_or_url)
end end
spec = spec_template(data) spec = spec_template(data)
(Pathname.pwd + "#{data[:name]}.podspec").open('w') { |f| f << spec } (Pathname.pwd + "#{data[:name]}.podspec").open('w') { |f| f << spec }
puts "\nSpecification created at #{data[:name]}.podspec\n".green puts "\nSpecification created at #{data[:name]}.podspec".green
end end
def lint def lint
name = @name_or_url if (is_repo = repo_with_name_exist(@repo_or_podspec))
file = name ? Pathname.new(name) : Pathname.pwd.glob('*.podspec').first files = (config.repos_dir + @repo_or_podspec).glob('**/*.podspec')
spec = Specification.from_file(file) else
puts "\nThe #{spec.name} specification contains all the required attributes.".green if spec.validate! if @repo_or_podspec
files = [Pathname.new(@repo_or_podspec)]
raise Informative, "[!] Unable to find a spec named #{@repo_or_podspec}".red unless files[0].exist?
else
files = Pathname.pwd.glob('*.podspec')
raise Informative, "[!] No specs found in the current directory".red if files.empty?
end
end
puts
all_valid = lint_specs_files(files, is_repo)
if all_valid
puts (files.count == 1 ? "#{@repo_or_podspec} passed validation" : "All the specs passed validation").green
else
message = (files.count == 1 ? "[!] The spec did not pass validation" : "[!] Not all specs passed validation").red
raise Informative, message
end
end
private
def repo_with_name_exist(name)
name && (config.repos_dir + name).exist?
end
# Takes an array of podspec files and lints them all
#
# It returns true if **all** the files passed validation
#
def lint_specs_files(files, is_repo)
all_valid = true
files.each do |file|
file = file.realpath
file_spec = Specification.from_file(file)
specs = file_spec.recursive_subspecs.any? ? file_spec.recursive_subspecs : [file_spec]
specs.each do |spec|
# Show immediatly which pod is being processed.
# This line will be overwritten once the result is known
print " -> #{spec}\r" unless config.silent? || is_repo
$stdout.flush
# If the spec doesn't validate it raises and informative
spec.validate!
warnings = warnings_for_spec(spec, file, is_repo)
deprecations = deprecation_notices_for_spec(spec, file, is_repo)
if is_repo || @quick
build_messages, file_patterns_errors = [], []
else
set_up_lint_environment
build_messages = build_errors_for_spec(spec, file, is_repo)
file_patterns_errors = file_patterns_errors_for_spec(spec, file, is_repo)
tear_down_lint_environment
end
build_errors = build_messages.select {|msg| msg.include?('error')}
build_warnings = build_messages - build_errors
# Errors compromise the functionality of a spec, warnings can be ignored
all = warnings + deprecations + build_messages + file_patterns_errors
errors = file_patterns_errors + build_errors
warnings = all - errors
if @only_errors
all_valid = false unless errors.empty?
else
# avoid to fail validation for xcode warnings
all_valid = false unless (all - build_warnings).empty?
end
clean_duplicate_platfrom_messages(errors)
clean_duplicate_platfrom_messages(warnings)
# This overwrites the previously printed text
unless config.silent?
if errors.empty? && warnings.empty?
puts " -> ".green + "#{spec} passed validation" unless is_repo
elsif errors.empty?
puts " -> ".yellow + spec.to_s
else
puts " -> ".red + spec.to_s
end
end
warnings.each {|msg| puts " - WARN | #{msg}"} unless config.silent?
errors.each {|msg| puts " - ERROR | #{msg}"} unless config.silent?
puts unless config.silent? || ( is_repo && all.flatten.empty? )
end
end
all_valid
end
def tmp_dir
Pathname.new('/tmp/CocoaPods/Lint')
end
def set_up_lint_environment
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.doc_install = false
end
def tear_down_lint_environment
tmp_dir.rmtree
Config.instance = @original_config
end
def clean_duplicate_platfrom_messages(messages)
duplicate_candiates = messages.select {|l| l.include?("ios: ")}
duplicated = duplicate_candiates.select {|l| messages.include?(l.gsub(/ios: /,'osx: ')) }
duplicated.uniq.each do |l|
clean = l.gsub(/ios: /,'')
messages.insert(messages.index(l), clean)
messages.delete(l)
messages.delete('osx: ' + clean)
end
end
# It checks a spec for minor non fatal defects
#
# It returns a array of messages
#
def warnings_for_spec(spec, file, is_repo)
license = spec.license
source = spec.source
text = file.read
warnings = [] warnings = []
warnings << 'The name of the specification should match the name of the podspec file' unless path_matches_name?(file, spec) warnings << "The name of the spec should match the name of the file" unless path_matches_name?(file, spec)
warnings << 'Missing license[:type]' unless spec.license && spec.license[:type] warnings << "Missing license[:type]" unless license && license[:type]
warnings << 'Missing license[:file] or [:text]' unless spec.license && (spec.license[:file] || spec.license[:text]) warnings << "Github repositories should end in `.git'" if source && source[:git] =~ /github.com/ && source[:git] !~ /.*\.git/
warnings << "Github repositories should end in `.git'" if spec.source[:git] =~ /github.com/ && spec.source[:git] !~ /.*\.git/
warnings << "Github repositories should end in `.git'" if spec.source[:git] =~ /github.com/ && spec.source[:git] !~ /.*\.git/
warnings << "The description should end with a dot" if spec.description && spec.description !~ /.*\./ warnings << "The description should end with a dot" if spec.description && spec.description !~ /.*\./
warnings << "The summary should end with a dot" if spec.summary !~ /.*\./ warnings << "The summary should end with a dot" if spec.summary !~ /.*\./
warnings << "Missing license[:file] or [:text]" unless license && (license[:file] || license[:text])
warnings << "Comments must be deleted" if text =~ /^\w*#\n\w*#/ # allow a single line comment as it is generally used in subspecs
unless warnings.empty? warnings
puts "\n[!] The #{spec.name} specification raised the following warnings".yellow
warnings.each { |warn| puts ' - '+ warn }
end end
puts
def path_matches_name?(file, spec)
spec_name = spec.name.match(/[^\/]*/)[0]
file.basename.to_s == spec_name + '.podspec'
end end
private # It reads a podspec file and checks for strings corresponding
# to a feature that are or will be deprecated
#
# It returns a array of messages
#
def deprecation_notices_for_spec(spec, file, is_repo)
text = file.read
deprecations = []
deprecations << "`config.ios?' and `config.osx' will be removed in version 0.7" if text. =~ /config\..os?/
deprecations << "Currently there is no known reason to use the `post_install' hook" if text. =~ /post_install/
deprecations
end
def path_matches_name?(path, spec) # It creates a podfile in memory and builds a library containing
(path.dirname + "#{spec.name}.podspec").to_s == @name_or_url # the pod for all available platfroms with xcodebuild.
#
# It returns a array of messages
#
def build_errors_for_spec(spec, file, is_repo)
messages = []
platform_names(spec).each do |platform_name|
podfile = podfile_from_spec(spec, file, platform_name)
Installer.new(podfile).install!
return messages if `which xcodebuild`.strip.empty?
output = Dir.chdir(config.project_pods_root) { `xcodebuild 2>&1` }
clean_output = process_xcode_build_output(output).map {|l| "#{platform_name}: #{l}"}
messages += clean_output
puts("\n" + output) if config.verbose?
end
messages
end end
def suggested_ref_and_version(repo) def podfile_from_spec(spec, file, platform_name)
tags = Octokit.tags(:username => repo['owner']['login'], :repo => repo['name']).map {|tag| tag["name"]} podfile = Pod::Podfile.new do
versions_tags = {} platform platform_name
tags.each do |tag| dependency spec.name, :podspec => file.realpath.to_s
clean_tag = tag.gsub(/^v(er)? ?/,'') end
versions_tags[Gem::Version.new(clean_tag)] = tag if Gem::Version.correct?(clean_tag) 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.include?('error generated.')\
|| l.include?('warning') && !l.include?('warning generated.')\
|| l.include?('note')
end
# Remove the unnecessary tmp path
selected_lines.map {|l| l.gsub(/\/tmp\/CocoaPods\/Lint\/Pods\//,'')}
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
#
# It returns a array of messages
#
def file_patterns_errors_for_spec(spec, file, is_repo)
Dir.chdir(config.project_pods_root + spec.name ) do
messages = []
messages += check_spec_files_exists(spec, :source_files)
messages += check_spec_files_exists(spec, :resources)
messages.compact
end end
version = versions_tags.keys.sort.last || '0.0.1'
data = {:version => version}
if version == '0.0.1'
branches = Octokit.branches(:username => repo['owner']['login'], :repo => repo['name'])
master_name = repo['master_branch'] || 'master'
master = branches.select {|branch| branch['name'] == master_name }.first
data[:ref_type] = ':commit'
data[:ref] = master['commit']['sha']
else
data[:ref_type] = ':tag'
data[:ref] = versions_tags[version]
end end
def check_spec_files_exists(spec, accessor)
result = []
platform_names(spec).each do |platform_name|
patterns = spec.send(accessor)[platform_name]
unless patterns.empty?
patterns.each do |pattern|
result << "#{platform_name}: [#{accessor} = '#{pattern}'] -> did not match any file" if Pathname.pwd.glob(pattern).empty?
end
end
end
result
end
def platform_names(spec)
spec.platform.name ? [spec.platform.name] : [:ios, :osx]
end
# Templates and github information retrival for spec create
def default_data_for_template(name)
data = {}
data[:name] = name
data[:version] = '0.0.1'
data[:summary] = "A short description of #{name}."
data[:homepage] = "http://EXAMPLE/#{name}"
data[:author_name] = `git config --get user.name`.strip
data[:author_email] = `git config --get user.email`.strip
data[:source_url] = "http://EXAMPLE/#{name}.git"
data[:ref_type] = ':tag'
data[:ref] = '0.0.1'
data data
end end
...@@ -113,17 +323,25 @@ module Pod ...@@ -113,17 +323,25 @@ module Pod
data.merge suggested_ref_and_version(repo) data.merge suggested_ref_and_version(repo)
end end
def default_data_for_template(name) def suggested_ref_and_version(repo)
data = {} tags = Octokit.tags(:username => repo['owner']['login'], :repo => repo['name']).map {|tag| tag["name"]}
data[:name] = name versions_tags = {}
data[:version] = '0.0.1' tags.each do |tag|
data[:summary] = "A short description of #{name}." clean_tag = tag.gsub(/^v(er)? ?/,'')
data[:homepage] = "http://EXAMPLE/#{name}" versions_tags[Gem::Version.new(clean_tag)] = tag if Gem::Version.correct?(clean_tag)
data[:author_name] = `git config --get user.name`.strip end
data[:author_email] = `git config --get user.email`.strip version = versions_tags.keys.sort.last || '0.0.1'
data[:source_url] = "http://EXAMPLE/#{name}.git" data = {:version => version}
if version == '0.0.1'
branches = Octokit.branches(:username => repo['owner']['login'], :repo => repo['name'])
master_name = repo['master_branch'] || 'master'
master = branches.select {|branch| branch['name'] == master_name }.first
data[:ref_type] = ':commit'
data[:ref] = master['commit']['sha']
else
data[:ref_type] = ':tag' data[:ref_type] = ':tag'
data[:ref] = '0.0.1' data[:ref] = versions_tags[version]
end
data data
end end
...@@ -241,7 +459,7 @@ Pod::Spec.new do |s| ...@@ -241,7 +459,7 @@ Pod::Spec.new do |s|
# s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' } # s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' }
end end
SPEC SPEC
end end
def semantic_versioning_notice(repo_id, repo) def semantic_versioning_notice(repo_id, repo)
......
...@@ -53,16 +53,12 @@ module Pod ...@@ -53,16 +53,12 @@ module Pod
end end
def ios? def ios?
require 'colored' # TODO: deprecate in 0.7
caller.find { |line| line =~ /^(.+.podspec):\d*/ }
puts "[!] The use of `config.ios?` is deprecated and will be removed in version 0.7.#{" Called from: #{$1}" if $1}".red
podfile.target_definitions[:default].platform == :ios if podfile podfile.target_definitions[:default].platform == :ios if podfile
end end
def osx? def osx?
require 'colored' # TODO: deprecate in 0.7
caller.find { |line| line =~ /^(.+.podspec):\d*/ }
puts "[!] The use of `config.osx?` is deprecated and will be removed in version 0.7.#{" Called from: #{$1}" if $1}".red
podfile.target_definitions[:default].platform == :osx if podfile podfile.target_definitions[:default].platform == :osx if podfile
end end
......
...@@ -64,7 +64,8 @@ module Pod ...@@ -64,7 +64,8 @@ module Pod
# Returns a path, which is relative to the project_root, relative to the # Returns a path, which is relative to the project_root, relative to the
# `$(SRCROOT)` of the user's project. # `$(SRCROOT)` of the user's project.
def relative_to_srcroot(path) def relative_to_srcroot(path)
(config.project_root + path).relative_path_from(xcodeproj.dirname) raise Informative, "[!] Unable to find an Xcode project to integrate".red unless xcodeproj || !config.integrate_targets
xcodeproj ? (config.project_root + path).relative_path_from(xcodeproj.dirname) : path
end end
def relative_pods_root def relative_pods_root
......
...@@ -436,7 +436,7 @@ module Pod ...@@ -436,7 +436,7 @@ module Pod
end end
# Override the getters to always return the value of the top level parent spec. # Override the getters to always return the value of the top level parent spec.
[:version, :summary, :platform, :license, :authors, :requires_arc, :compiler_flags, :documentation].each do |attr| [:version, :summary, :platform, :license, :authors, :requires_arc, :compiler_flags, :documentation, :homepage].each do |attr|
define_method(attr) { top_level_parent.send(attr) } define_method(attr) { top_level_parent.send(attr) }
end end
......
require File.expand_path('../../../spec_helper', __FILE__)
describe Pod::Command::Push do
extend SpecHelper::Command
extend SpecHelper::Git
extend SpecHelper::TemporaryDirectory
it "complains for wrong parameters" do
lambda { run_command('push') }.should.raise Pod::Command::Help
lambda { run_command('push', '--allow-warnings') }.should.raise Pod::Command::Help
lambda { run_command('push', '--wrong-option') }.should.raise Pod::Command::Help
end
it "complains if it can't find the repo" do
repo1 = add_repo('repo1', fixture('spec-repos/master'))
Dir.chdir(fixture('banana-lib')) do
lambda { run_command('push', 'repo2') }.should.raise Pod::Informative
end
end
it "complains if it can't find a spec" do
repo1 = add_repo('repo1', fixture('spec-repos/master'))
lambda { run_command('push', 'repo1') }.should.raise Pod::Informative
end
it "it raises if the pod is not validated" do
repo1 = add_repo('repo1', fixture('spec-repos/master'))
git('repo1', 'checkout master') # checkout master, because the fixture is a submodule
repo2 = add_repo('repo2', repo1.dir)
git_config('repo2', 'remote.origin.url').should == (tmp_repos_path + 'repo1').to_s
Dir.chdir(fixture('banana-lib')) do
lambda { command('push', 'repo2', '--silent').run }.should.raise Pod::Informative
end
# (repo1.dir + 'BananaLib/1.0/BananaLib.podspec').read.should.include 'Added!'
end
before do
# prepare the repos
@upstream = add_repo('upstream', fixture('spec-repos/master'))
git('upstream', 'checkout -b master') # checkout master, because the fixture is a submodule
@local_repo = add_repo('local_repo', @upstream.dir)
git_config('local_repo', 'remote.origin.url').should == (tmp_repos_path + 'upstream').to_s
git('upstream', 'checkout -b no-master') # checkout no-master, to allow push in a non-bare repository
# prepare the spec
spec_fix = (fixture('spec-repos') + 'master/JSONKit/1.4/JSONKit.podspec').read
spec_add = spec_fix.gsub(/https:\/\/github\.com\/johnezang\/JSONKit\.git/, fixture('integration/JSONKit').to_s)
spec_add.gsub!(/'JSONKit'/, "'PushTest'")
File.open(temporary_directory + 'JSONKit.podspec', 'w') {|f| f.write(spec_fix) }
File.open(temporary_directory + 'PushTest.podspec', 'w') {|f| f.write(spec_add) }
end
it "refuses to push if the repo is not clean" do
File.open(@local_repo.dir + 'README', 'w') {|f| f.write('Added!') }
(@local_repo.dir + 'README').read.should.include 'Added!'
cmd = command('push', 'local_repo')
cmd.expects(:validate_podspec_files).returns(true)
Dir.chdir(temporary_directory) { lambda { cmd.run }.should.raise Pod::Informative }
git('upstream', 'checkout master') # checkout master, because the fixture is a submodule
(@upstream.dir + 'PushTest/1.4/PushTest.podspec').should.not.exist?
end
it "sucessfully pushes a spec" do
cmd = command('push', 'local_repo')
cmd.expects(:validate_podspec_files).returns(true)
Dir.chdir(temporary_directory) { cmd.run }
cmd.output.should.include('[Add] PushTest (1.4)')
cmd.output.should.include('[Fix] JSONKit (1.4)')
git('upstream', 'checkout master') # checkout master, because the fixture is a submodule
(@upstream.dir + 'PushTest/1.4/PushTest.podspec').read.should.include('PushTest')
end
end
...@@ -3,6 +3,7 @@ require File.expand_path('../../../spec_helper', __FILE__) ...@@ -3,6 +3,7 @@ require File.expand_path('../../../spec_helper', __FILE__)
describe "Pod::Command::Search" do describe "Pod::Command::Search" do
extend SpecHelper::Command extend SpecHelper::Command
extend SpecHelper::Git extend SpecHelper::Git
extend SpecHelper::TemporaryDirectory
before do before do
config.repos_dir = fixture('spec-repos') config.repos_dir = fixture('spec-repos')
...@@ -19,6 +20,7 @@ describe "Pod::Command::Search" do ...@@ -19,6 +20,7 @@ describe "Pod::Command::Search" do
end end
it "complains for wrong parameters" do it "complains for wrong parameters" do
lambda { run_command('search') }.should.raise Pod::Command::Help
lambda { run_command('search', 'too', 'many') }.should.raise Pod::Command::Help lambda { run_command('search', 'too', 'many') }.should.raise Pod::Command::Help
lambda { run_command('search', 'too', '--wrong') }.should.raise Pod::Command::Help lambda { run_command('search', 'too', '--wrong') }.should.raise Pod::Command::Help
lambda { run_command('search', '--wrong') }.should.raise Pod::Command::Help lambda { run_command('search', '--wrong') }.should.raise Pod::Command::Help
......
require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../../../spec_helper', __FILE__)
describe "Pod::Command::Spec" do describe Pod::Command::Spec do
extend SpecHelper::Command extend SpecHelper::Command
extend SpecHelper::Github
extend SpecHelper::TemporaryDirectory
it "runs with correct parameters" do
lambda{ run_command('spec', 'create', 'Bananas') }.should.not.raise
expect_github_repo_request
expect_github_user_request
expect_github_tags_request
lambda{ run_command('spec', 'create', 'https://github.com/lukeredpath/libPusher.git') }.should.not.raise
end
it "complains for wrong parameters" do it "complains for wrong parameters" do
lambda { run_command('spec') }.should.raise Pod::Command::Help
lambda { run_command('spec', 'create') }.should.raise Pod::Command::Help
lambda { run_command('spec', '--create') }.should.raise Pod::Command::Help lambda { run_command('spec', '--create') }.should.raise Pod::Command::Help
lambda { run_command('spec', 'NAME') }.should.raise Pod::Command::Help
lambda { run_command('spec', 'createa') }.should.raise Pod::Command::Help lambda { run_command('spec', 'createa') }.should.raise Pod::Command::Help
lambda { run_command('spec', 'create') }.should.raise Pod::Command::Help lambda { run_command('lint', 'agument1', '2') }.should.raise Pod::Command::Help
end end
end
describe "Pod::Command::Spec create" do
extend SpecHelper::Command
extend SpecHelper::Github
extend SpecHelper::TemporaryDirectory
extend SpecHelper::Git
it "creates a new podspec stub file" do it "creates a new podspec stub file" do
run_command('spec', 'create', 'Bananas') run_command('spec', 'create', 'Bananas')
...@@ -50,6 +50,22 @@ describe "Pod::Command::Spec" do ...@@ -50,6 +50,22 @@ describe "Pod::Command::Spec" do
spec.source.should == { :git => 'https://github.com/lukeredpath/libPusher.git', :tag => 'v1.3' } spec.source.should == { :git => 'https://github.com/lukeredpath/libPusher.git', :tag => 'v1.3' }
end end
it "accepts the a name when creating a podspec form github" do
expect_github_repo_request
expect_github_user_request
expect_github_tags_request
run_command('spec', 'create', 'other_name', 'https://github.com/lukeredpath/libPusher.git')
path = temporary_directory + 'other_name.podspec'
spec = Pod::Specification.from_file(path)
spec.name.should == 'other_name'
spec.license.should == { :type => "MIT", :file => "LICENSE" }
spec.version.should == Pod::Version.new('1.3')
spec.summary.should == 'An Objective-C interface to Pusher (pusherapp.com)'
spec.homepage.should == 'https://github.com/lukeredpath/libPusher'
spec.authors.should == {"Luke Redpath"=>"luke@lukeredpath.co.uk"}
spec.source.should == { :git => 'https://github.com/lukeredpath/libPusher.git', :tag => 'v1.3' }
end
it "correctly suggests the head commit if a suitable tag is not available on github" do it "correctly suggests the head commit if a suitable tag is not available on github" do
expect_github_repo_request expect_github_repo_request
expect_github_user_request expect_github_user_request
...@@ -71,8 +87,96 @@ describe "Pod::Command::Spec" do ...@@ -71,8 +87,96 @@ describe "Pod::Command::Spec" do
output.should.include 'MARKDOWN TEMPLATE' output.should.include 'MARKDOWN TEMPLATE'
output.should.include 'Please add semantic version tags' output.should.include 'Please add semantic version tags'
end end
end end
describe "Pod::Command::Spec lint" do
extend SpecHelper::Command
extend SpecHelper::TemporaryDirectory
extend SpecHelper::Git
before do
config.repos_dir = fixture('spec-repos')
end
after do
config.repos_dir = tmp_repos_path
end
it "lints a repo" do
# the fixture master repo has warnings and does not validates
lambda { run_command('spec', 'lint', 'master') }.should.raise Pod::Informative
end
it "lints a repo with --only-errors option and show the warnings" do
output = run_command('spec', 'lint', 'master', '--only-errors')
output.should.include "passed validation"
output.should.include "WARN"
end
it "complains if no repo name or url are provided and there a no specs in the current working directory" do
Dir.chdir(fixture('spec-repos') + 'master/JSONKit/') do
lambda { command('spec', 'lint').run }.should.raise Pod::Informative
end
end
it "lints the current working directory" do
Dir.chdir(fixture('spec-repos') + 'master/JSONKit/1.4/') do
output = command('spec', 'lint', '--quick', '--only-errors').run
output.should.include "passed validation"
end
end
it "lints a givent podspec" do
spec_file = fixture('spec-repos') + 'master/JSONKit/1.4/JSONKit.podspec'
output = run_command('spec', 'lint', '--quick', spec_file)
output.should.include "passed validation"
end
before do
spec = (fixture('spec-repos') + 'master/JSONKit/1.4/JSONKit.podspec').read
spec.gsub!(/https:\/\/github\.com\/johnezang\/JSONKit\.git/, fixture('integration/JSONKit').to_s)
spec.gsub!(/s\.source_files = 'JSONKit\.\*'/, "s.source_files = 'JSONKit.*'\ns.resources = 'WRONG_FOLDER'")
File.open(temporary_directory + 'JSONKit.podspec', 'w') {|f| f.write(spec) }
end
it "fails if there are warnings" do
cmd = command('spec', 'lint', '--quick')
Dir.chdir(temporary_directory) { lambda { cmd.run }.should.raise Pod::Informative }
cmd.output.should.include "- WARN | Missing license[:file] or [:text]"
end
it "respects the --only-errors option" do
cmd = command('spec', 'lint', '--quick', '--only-errors')
Dir.chdir(temporary_directory) { lambda { cmd.run }.should.not.raise Pod::Informative }
cmd.output.should.include "- WARN | Missing license[:file] or [:text]"
cmd.output.should.include "passed validation"
end
it "respects the --quick option" do
cmd = command('spec', '--quick', 'lint')
Dir.chdir(temporary_directory) { lambda { cmd.run }.should.raise Pod::Informative }
cmd.output.should.not.include "JSONKit/JSONKit.m:1640:27: warning: equality comparison with extraneous parentheses"
end
it "uses xcodebuild to generate warnings and checks for file patterns" do
# those two checks are merged because pod install is computationally expensive
cmd = command('spec', 'lint')
Dir.chdir(temporary_directory) { lambda { cmd.run }.should.raise Pod::Informative }
unless `which xcodebuild`.strip.empty?
cmd.output.should.include "JSONKit/JSONKit.m:1640:27: warning: equality comparison with extraneous parentheses"
end
cmd.output.should.include "- ERROR | [resources = 'WRONG_FOLDER'] -> did not match any file"
end
before do
spec = (fixture('spec-repos') + 'master/JSONKit/1.4/JSONKit.podspec').read
spec.gsub!(/s\.source_files = 'JSONKit\.\*'/, "s.source_files = 'JSONKit.*'\n if config.ios?\nend")
File.open(temporary_directory + 'JSONKit.podspec', 'w') {|f| f.write(spec) }
end
it "produces deprecation notices" do
cmd = command('spec', '--quick', 'lint')
Dir.chdir(temporary_directory) { lambda { cmd.run }.should.raise Pod::Informative }
cmd.output.should.include "- WARN | `config.ios?' and `config.osx' will be removed in version 0.7"
end
end
...@@ -260,6 +260,15 @@ describe "Pod::Podfile" do ...@@ -260,6 +260,15 @@ describe "Pod::Podfile" do
@target_definition.relative_pods_root.should == '${SRCROOT}/../Pods' @target_definition.relative_pods_root.should == '${SRCROOT}/../Pods'
end end
it "simply returns the $(PODS_ROOT) path if no xcodeproj file is available and doesn't needs to integrate" do
config.integrate_targets.should.equal true
config.integrate_targets = false
@target_definition.relative_pods_root.should == '${SRCROOT}/../Pods'
@target_definition.stubs(:xcodeproj).returns(nil)
@target_definition.relative_pods_root.should == '${SRCROOT}/Pods'
config.integrate_targets = true
end
it "returns the xcconfig file path relative to the project's $(SRCROOT)" do it "returns the xcconfig file path relative to the project's $(SRCROOT)" do
@target_definition.xcconfig_relative_path.should == '../Pods/Pods.xcconfig' @target_definition.xcconfig_relative_path.should == '../Pods/Pods.xcconfig'
end end
...@@ -271,6 +280,17 @@ describe "Pod::Podfile" do ...@@ -271,6 +280,17 @@ describe "Pod::Podfile" do
end end
describe "concerning validations" do describe "concerning validations" do
it "raises if it should integrate and can't find an xcodeproj" do
config.integrate_targets.should.equal true
target_definition = Pod::Podfile.new {}.target_definitions[:default]
target_definition.stubs(:xcodeproj).returns(nil)
exception = lambda {
target_definition.relative_pods_root
}.should.raise Pod::Informative
exception.message.should.include "Xcode project"
end
xit "raises if no platform is specified" do xit "raises if no platform is specified" do
exception = lambda { exception = lambda {
Pod::Podfile.new {}.validate! Pod::Podfile.new {}.validate!
......
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