Commit 86c03f40 authored by Eloy Durán's avatar Eloy Durán

First pass at using the extracted CLI lib CLAide.

parent 835a0faf
......@@ -2,6 +2,9 @@ source "http://rubygems.org"
gemspec
# TODO
gem "cli_aide", :git => "git://github.com/alloy/CLAide.git"
group :development do
gem "xcodeproj", :git => "git://github.com/CocoaPods/Xcodeproj.git"
gem "mocha", "~> 0.11.4"
......
......@@ -6,6 +6,12 @@ GIT
activesupport (~> 3.2.6)
colored (~> 1.2)
GIT
remote: git://github.com/alloy/CLAide.git
revision: 914c38cabeae22c946e96952b32708edf5e5030a
specs:
cli_aide (0.0.1)
GIT
remote: https://github.com/alloy/kicker.git
revision: 6430787ebf8b9305acc2d2f89ae5cf01d2cd5488
......@@ -87,6 +93,7 @@ PLATFORMS
DEPENDENCIES
awesome_print
bacon
cli_aide!
cocoapods!
github-markup
kicker!
......
......@@ -13,4 +13,4 @@ end
require 'cocoapods'
Pod::Command.run(*ARGV)
Pod::Command.run(ARGV)
# -*- encoding: utf-8 -*-
$:.unshift File.expand_path('../lib', __FILE__)
require 'cocoapods'
require 'cocoapods/version'
Gem::Specification.new do |s|
s.name = "cocoapods"
......
......@@ -11,10 +11,11 @@ unless Gem::Version::Requirement.new('>= 1.4.0').satisfied_by?(Gem::Version.new(
exit 1
end
module Pod
VERSION = '0.16.0.rc2'
require 'cocoapods/version'
require 'cli_aide'
class PlainInformative < StandardError
module Pod
class PlainInformative < CLIAide::Command::Informative
end
class Informative < PlainInformative
......@@ -41,7 +42,7 @@ module Pod
autoload :Spec, 'cocoapods/specification'
autoload :Specification, 'cocoapods/specification'
autoload :UI, 'cocoapods/user_interface'
autoload :Version, 'cocoapods/version'
autoload :Version, 'cocoapods/version_class'
autoload :Pathname, 'pathname'
autoload :FileList, 'cocoapods/file_list'
......
require 'colored'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/string/strip.rb'
module Pod
class Command
class Command < CLIAide::Command
autoload :ErrorReport, 'cocoapods/command/error_report'
autoload :Install, 'cocoapods/command/install'
autoload :List, 'cocoapods/command/list'
autoload :Linter, 'cocoapods/command/linter'
autoload :Outdated, 'cocoapods/command/outdated'
autoload :Push, 'cocoapods/command/push'
autoload :Repo, 'cocoapods/command/repo'
autoload :Search, 'cocoapods/command/search'
autoload :Setup, 'cocoapods/command/setup'
autoload :Spec, 'cocoapods/command/spec'
autoload :Update, 'cocoapods/command/update'
class Help < Informative
def initialize(command_class, argv, unrecognized_command = nil)
@command_class, @argv, @unrecognized_command = command_class, argv, unrecognized_command
end
def message
message = [
'',
@command_class.banner.gsub(/\$ pod (.*)/, '$ pod \1'.green),
'',
'Options:',
'',
options,
"\n",
].join("\n")
message << "[!] Unrecognized command: `#{@unrecognized_command}'\n".red if @unrecognized_command
message << "[!] Unrecognized argument#{@argv.count > 1 ? 's' : ''}: `#{@argv.join(' - ')}'\n".red unless @argv.empty?
message
end
private
def options
options = @command_class.options
keys = options.map(&:first)
key_size = keys.inject(0) { |size, key| key.size > size ? key.size : size }
options.map { |key, desc| " #{key.ljust(key_size)} #{desc}" }.join("\n")
end
end
class ARGV < Array
def options; select { |x| x.to_s[0,1] == '-' }; end
def arguments; self - options; end
def option(name); !!delete(name); end
def shift_argument; (arg = arguments[0]) && delete(arg); end
end
def self.banner
commands = ['install', 'update', 'outdated', 'list', 'push', 'repo', 'search', 'setup', 'spec'].sort
banner = "To see help for the available commands run:\n\n"
banner + commands.map { |cmd| " * $ pod #{cmd.green} --help" }.join("\n")
end
self.abstract_command = true
self.description = 'CocoaPods, the Objective-C library package manager.'
def self.options
[
['--help', 'Show help information'],
['--silent', 'Print nothing'],
['--no-color', 'Print output without color'],
['--verbose', 'Print more information while working'],
['--version', 'Prints the version of CocoaPods'],
]
end
].concat(super)
end
#def self.run(argv)
#super
#p Config.instance.verbose?
#end
#def self.run(*argv)
#sub_command = parse(*argv)
#unless sub_command.is_a?(Setup) || ENV['SKIP_SETUP']
#Setup.new(ARGV.new).run_if_needed
#end
#sub_command.run
#UI.puts
#rescue Interrupt
#puts "[!] Cancelled".red
#Config.instance.verbose? ? raise : exit(1)
#rescue Exception => e
#if e.is_a?(PlainInformative) || ENV['COCOA_PODS_ENV'] == 'development' # also catches Informative
#puts e.message
#puts *e.backtrace if Config.instance.verbose? || ENV['COCOA_PODS_ENV'] == 'development'
#else
#puts ErrorReport.report(e)
#end
#exit 1
#end
#def self.parse(*argv)
#argv = ARGV.new(argv)
#if argv.option('--version')
#puts VERSION
#exit!(0)
#end
#show_help = argv.option('--help')
#Config.instance.silent = argv.option('--silent')
#Config.instance.verbose = argv.option('--verbose')
#String.send(:define_method, :colorize) { |string , _| string } if argv.option( '--no-color' )
#command_class = case command_argument = argv.shift_argument
#when 'install' then Install
#when 'list' then List
#when 'outdated' then Outdated
#when 'push' then Push
#when 'repo' then Repo
#when 'search' then Search
#when 'setup' then Setup
#when 'spec' then Spec
#when 'update' then Update
#end
#if command_class.nil?
#raise Help.new(self, argv, command_argument)
#elsif show_help
#raise Help.new(command_class, argv)
#else
#command_class.new(argv)
#end
#end
def self.run(*argv)
sub_command = parse(*argv)
unless sub_command.is_a?(Setup) || ENV['SKIP_SETUP']
Setup.new(ARGV.new).run_if_needed
end
sub_command.run
UI.puts
rescue Interrupt
puts "[!] Cancelled".red
Config.instance.verbose? ? raise : exit(1)
rescue Exception => e
if e.is_a?(PlainInformative) || ENV['COCOA_PODS_ENV'] == 'development' # also catches Informative
puts e.message
puts *e.backtrace if Config.instance.verbose? || ENV['COCOA_PODS_ENV'] == 'development'
else
puts ErrorReport.report(e)
end
exit 1
end
def self.parse(*argv)
argv = ARGV.new(argv)
if argv.option('--version')
puts VERSION
exit!(0)
end
show_help = argv.option('--help')
Config.instance.silent = argv.option('--silent')
Config.instance.verbose = argv.option('--verbose')
String.send(:define_method, :colorize) { |string , _| string } if argv.option( '--no-color' )
command_class = case command_argument = argv.shift_argument
when 'install' then Install
when 'list' then List
when 'outdated' then Outdated
when 'push' then Push
when 'repo' then Repo
when 'search' then Search
when 'setup' then Setup
when 'spec' then Spec
when 'update' then Update
end
if command_class.nil?
raise Help.new(self, argv, command_argument)
elsif show_help
raise Help.new(command_class, argv)
else
command_class.new(argv)
end
def initialize(argv)
super
config.verbose = verbose?
end
include Config::Mixin
def initialize(argv)
raise Help.new(self.class, argv)
end
private
def verify_podfile_exists!
......@@ -145,3 +102,11 @@ module Pod
end
end
require 'cocoapods/command/list'
require 'cocoapods/command/outdated'
require 'cocoapods/command/project'
require 'cocoapods/command/push'
require 'cocoapods/command/repo'
require 'cocoapods/command/search'
require 'cocoapods/command/setup'
require 'cocoapods/command/spec'
module Pod
class Command
class List < Command
def self.banner
%{List all pods:
$ pod list
Lists all available pods.
$ pod list new
Lists the pods introduced in the master repository since the last check.}
end
self.summary = 'Lists all available pods.'
def self.options
[[
......@@ -24,19 +14,33 @@ module Pod
executable :git
def initialize(argv)
@update = argv.option('--update')
@stats = argv.option('--stats')
@new = argv.option('new')
super unless argv.empty?
@update = argv.flag?('update')
@stats = argv.flag?('stats')
super
end
def list_all
def run
update_if_necessary!
sets = Source.all_sets
sets.each { |set| UI.pod(set, :name) }
UI.puts "\n#{sets.count} pods were found"
end
def list_new
def update_if_necessary!
if @update && config.verbose?
UI.section("\nUpdating Spec Repositories\n".yellow) do
Repo.new(ARGV.new(["update"])).run
end
end
end
class New < List
self.summary = 'Lists the pods introduced in the master repository since the last check.'
def run
update_if_necessary!
days = [1,2,3,5,8]
dates, groups = {}, {}
days.each {|d| dates[d] = Time.now - 60 * 60 * 24 * d}
......@@ -62,12 +66,6 @@ module Pod
end
end
end
def run
UI.section("\nUpdating Spec Repositories\n".yellow) do
Repo.new(ARGV.new(["update"])).run
end if @update && config.verbose?
@new ? list_new : list_all
end
end
end
......
module Pod
class Command
class Outdated < Command
def self.banner
%{Show outdated pods:
$ pod outdated
self.summary = 'Show outdated project dependencies.'
self.description = <<-DESC
Shows the outdated pods in the current Podfile.lock, but only those from
spec repos, not those from local/external sources or `:head' versions.}
end
spec repos, not those from local/external sources or `:head' versions.
DESC
def self.options
[["--no-update", "Skip running `pod repo update` before install"]].concat(super)
end
def initialize(argv)
config.skip_repo_update = argv.option('--no-update')
super unless argv.empty?
config.skip_repo_update = argv.flag?('update', true)
super
end
def run
......
module Pod
class Command
module Project
def self.options
[
["--no-clean", "Leave SCM dirs like `.git' and `.svn' intact after downloading"],
["--no-doc", "Skip documentation generation with appledoc"],
["--no-integrate", "Skip integration of the Pods libraries in the Xcode project(s)"],
["--no-update", "Skip running `pod repo update` before install"],
].concat(super)
end
def initialize(argv)
config.clean = argv.flag?('clean', true)
config.generate_docs = argv.flag?('doc', true)
config.integrate_targets = argv.flag?('integrate', true)
config.skip_repo_update = !argv.flag?('update', true)
super
end
def run_install_with_update(update)
sandbox = Sandbox.new(config.project_pods_root)
resolver = Resolver.new(config.podfile, config.lockfile, sandbox)
resolver.update_mode = update
Installer.new(resolver).install!
end
end
class Install < Command
def self.banner
%{Installing dependencies of a project:
include Project
$ pod install
self.summary = 'Installs dependencies of a project.'
self.description = <<-DESC
Downloads all dependencies defined in `Podfile' and creates an Xcode
Pods library project in `./Pods'.
......@@ -19,37 +45,27 @@ module Pod
This will configure the project to reference the Pods static library,
add a build configuration file, and add a post build script to copy
Pod resources.}
end
Pod resources.
DESC
def self.options
[
["--no-clean", "Leave SCM dirs like `.git' and `.svn' intact after downloading"],
["--no-doc", "Skip documentation generation with appledoc"],
["--no-integrate", "Skip integration of the Pods libraries in the Xcode project(s)"],
["--no-update", "Skip running `pod repo update` before install"],
].concat(super)
def run
verify_podfile_exists!
run_install_with_update(false)
end
def initialize(argv)
config.clean = !argv.option('--no-clean')
config.generate_docs = !argv.option('--no-doc')
config.integrate_targets = !argv.option('--no-integrate')
config.skip_repo_update = argv.option('--no-update')
super unless argv.empty?
end
def run_install_with_update(update)
sandbox = Sandbox.new(config.project_pods_root)
resolver = Resolver.new(config.podfile, config.lockfile, sandbox)
resolver.update_mode = update
Installer.new(resolver).install!
end
class Update < Command
include Project
self.summary = 'Update outdated project dependencies.'
def run
verify_podfile_exists!
run_install_with_update(false)
verify_lockfile_exists!
run_install_with_update(true)
end
end
end
end
......@@ -4,16 +4,16 @@ require 'active_support/core_ext/string/inflections'
module Pod
class Command
class Push < Command
def self.banner
%{Pushing new specifications to a spec-repo:
$ pod push REPO [NAME.podspec]
self.summary = 'Push new specifications to a spec-repo.'
self.description = <<-DESC
Validates NAME.podspec or `*.podspec' in the current working dir, creates
a directory and version folder for the pod in the local copy of
REPO (~/.cocoapods/[REPO]), copies the podspec file into the version directory,
and finally it pushes REPO to its remote.}
end
and finally it pushes REPO to its remote.
DESC
self.arguments = 'REPO [NAME.podspec]'
def self.options
[ ["--allow-warnings", "Allows to push if warnings are not evitable"],
......@@ -24,11 +24,16 @@ module Pod
executable :git
def initialize(argv)
@allow_warnings = argv.option('--allow-warnings')
@local_only = argv.option('--local-only')
@allow_warnings = argv.flag?('allow-warnings')
@local_only = argv.flag?('local-only')
@repo = argv.shift_argument
@podspec = argv.shift_argument
super unless argv.empty? && @repo
super
end
def validate_argv!
super
help! "A spec-repo name is required." unless @repo
end
def run
......
......@@ -3,59 +3,34 @@ require 'fileutils'
module Pod
class Command
class Repo < Command
def self.banner
%{Managing spec-repos:
self.abstract_command = true
$ pod repo add NAME URL [BRANCH]
# TODO should not show a usage banner!
self.summary = 'Managed spec repos.'
class Add < Repo
self.summary = 'Add a spec repo.'
self.description = <<-DESC
Clones `URL' in the local spec-repos directory at `~/.cocoapods'. The
remote can later be referred to by `NAME'.
DESC
$ pod repo update [NAME]
Updates the local clone of the spec-repo `NAME'. If `NAME' is omitted
this will update all spec-repos in `~/.cocoapods'.
$ pod repo lint [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
def self.options
[["--only-errors", "Lint presents only the errors"]].concat(super)
end
extend Executable
executable :git
self.arguments = 'NAME URL [BRANCH]'
def initialize(argv)
case @action = argv.arguments[0]
when 'add'
unless (@name = argv.arguments[1]) && (@url = argv.arguments[2])
raise Informative, "#{@action == 'add' ? 'Adding' : 'Updating the remote of'} a repo needs a `name' and a `url'."
end
@branch = argv.arguments[3]
when 'update'
@name = argv.arguments[1]
when 'lint'
@name = argv.arguments[1]
@only_errors = argv.option('--only-errors')
else
@name, @url, @branch = argv.shift_argument, argv.shift_argument, argv.shift_argument
super
end
end
def dir
config.repos_dir + @name
def validate_argv!
super
unless @name && @url
help! "Adding a repo needs a `NAME' and a `URL."
end
def run
send @action.gsub('-', '_')
end
def add
def run
UI.section("Cloning spec repo `#{@name}' from `#{@url}'#{" (branch `#{@branch}')" if @branch}") do
config.repos_dir.mkpath
Dir.chdir(config.repos_dir) { git!("clone '#{@url}' #{@name}") }
......@@ -63,8 +38,24 @@ module Pod
check_versions(dir)
end
end
end
class Update < Repo
self.summary = 'Update a spec repo.'
self.description = <<-DESC
Updates the local clone of the spec-repo `NAME'. If `NAME' is omitted
this will update all spec-repos in `~/.cocoapods'.
DESC
self.arguments = '[NAME]'
def initialize(argv)
@name = argv.shift_argument
super
end
def update
def run
dirs = @name ? [dir] : config.repos_dir.children.select {|c| c.directory?}
dirs.each do |dir|
UI.section "Updating spec repo `#{dir.basename}'" do
......@@ -80,8 +71,30 @@ module Pod
check_versions(dir)
end
end
end
def lint
class Lint < Repo
self.summary = 'Validates all specs in a repo.'
self.description = <<-DESC
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.
DESC
self.arguments = '[ NAME | DIRECTORY ]'
def self.options
[["--only-errors", "Lint presents only the errors"]].concat(super)
end
def initialize(argv)
@name = argv.shift_argument
@only_errors = argv.flag?('only-errors')
super
end
def run
if @name
dirs = File.exists?(@name) ? [ Pathname.new(@name) ] : [ dir ]
else
......@@ -129,6 +142,18 @@ module Pod
end
end
end
end
extend Executable
executable :git
# TODO some of the following methods can probably move to one of the subclasses.
protected
def dir
config.repos_dir + @name
end
def print_messages(type, messages)
return if config.silent?
......
module Pod
class Command
class Search < Command
def self.banner
%{Search pods:
$ pod search [QUERY]
self.summary = 'Search available pods.'
self.description = <<-DESC
Searches for pods, ignoring case, whose name matches `QUERY'. If the
`--full' option is specified, this will also search in the summary and
description of the pods.}
end
description of the pods.
DESC
self.arguments = '[QUERY]'
def self.options
[[
......@@ -19,10 +19,10 @@ module Pod
end
def initialize(argv)
@full_text_search = argv.option('--full')
@stats = argv.option('--stats')
@full_text_search = argv.flag?('full')
@stats = argv.flag?('stats')
@query = argv.shift_argument
super unless argv.empty? && @query
super
end
def run
......
module Pod
class Command
class Setup < Command
def self.banner
%{Setup CocoaPods environment:
$ pod setup
self.summary = 'Setup the CocoaPods environment.'
self.description = <<-DESC
Creates a directory at `~/.cocoapods' which will hold your spec-repos.
This is where it will create a clone of the public `master' spec-repo from:
https://github.com/CocoaPods/Specs
If the clone already exists, it will ensure that it is up-to-date.}
end
If the clone already exists, it will ensure that it is up-to-date.
DESC
def self.options
[["--push", "Use this option to enable push access once granted"]].concat(super)
......@@ -22,8 +20,8 @@ module Pod
executable :git
def initialize(argv)
@push_option = argv.option('--push')
super unless argv.empty?
@push_option = argv.flag?('push')
super
end
def dir
......@@ -75,7 +73,7 @@ module Pod
end
def update_master_repo
Repo.new(ARGV.new(['update', 'master'])).run
Repo::Update.run(['master'])
end
def set_master_repo_branch
......
......@@ -5,51 +5,30 @@ require 'active_support/core_ext/string/inflections'
module Pod
class Command
class Spec < Command
def self.banner
%{Managing PodSpec files:
# TODO should not show a usage banner!
self.summary = 'Managing PodSpec files'
$ pod spec create [ NAME | https://github.com/USER/REPO ]
class Create < Spec
self.summary = 'Create spec file stub.'
self.description = <<-DESC
Creates a PodSpec, in the current working dir, called `NAME.podspec'.
If a GitHub url is passed the spec is prepopulated.
DESC
$ pod spec lint [ NAME.podspec | DIRECTORY | http://PATH/NAME.podspec, ... ]
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
[ ["--quick", "Lint skips checks that would require to download and build the spec"],
["--local", "Lint a podspec against the local files contained in its directory"],
["--only-errors", "Lint validates even if warnings are present"],
["--no-clean", "Lint leaves the build directory intact for inspection"] ].concat(super)
end
self.arguments = '[ NAME | https://github.com/USER/REPO ]'
def initialize(argv)
@action = argv.shift_argument
if @action == 'create'
@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')
@local = argv.option('--local')
@only_errors = argv.option('--only-errors')
@no_clean = argv.option('--no-clean')
@podspecs_paths = argv
else
@url, @name_or_url = argv.shift_argument, argv.shift_argument
super
end
end
def run
send @action
def validate_argv!
super
help! "A pod name or repo URL is required." unless @name_or_url
end
def create
def run
if repo_id_match = (@url || @name_or_url).match(/github.com\/([^\/\.]*\/[^\/\.]*)\.*/)
# This is to make sure Faraday doesn't warn the user about the `system_timer` gem missing.
old_warn, $-w = $-w, nil
......@@ -71,8 +50,36 @@ module Pod
(Pathname.pwd + "#{data[:name]}.podspec").open('w') { |f| f << spec }
UI.puts "\nSpecification created at #{data[:name]}.podspec".green
end
end
class Lint < Spec
self.summary = 'Validates a spec file.'
def lint
self.description = <<-DESC
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.
DESC
self.arguments = '[ NAME.podspec | DIRECTORY | http://PATH/NAME.podspec, ... ]'
def self.options
[ ["--quick", "Lint skips checks that would require to download and build the spec"],
["--local", "Lint a podspec against the local files contained in its directory"],
["--only-errors", "Lint validates even if warnings are present"],
["--no-clean", "Lint leaves the build directory intact for inspection"] ].concat(super)
end
def initialize(argv)
@quick = argv.flag?('quick')
@local = argv.flag?('local')
@only_errors = argv.flag?('only-errors')
@no_clean = argv.flag?('clean', false)
@podspecs_paths = argv.arguments!
super
end
def run
UI.puts
invalid_count = 0
podspecs_to_lint.each do |podspec|
......@@ -116,7 +123,9 @@ module Pod
end
podspecs_tmp_dir.rmtree if podspecs_tmp_dir.exist?
end
end
# TODO some of the following methods can probably move to one of the subclasses.
private
def print_messages(type, messages)
......
module Pod
class Command
class Update < Install
def self.banner
%{Updating dependencies of a project:
$ pod update
Updates all dependencies.}
end
def self.options
[["--no-update", "Skip running `pod repo update` before install"]].concat(super)
end
def initialize(argv)
config.skip_repo_update = argv.option('--no-update')
super unless argv.empty?
end
def run
verify_podfile_exists!
verify_lockfile_exists!
run_install_with_update(true)
end
end
end
end
module Pod
class Version < Gem::Version
# @returns A Version described by its #to_s method.
#
# @TODO The `from' part of the regexp should be remove before 1.0.0.
#
def self.from_string(string)
if string =~ /HEAD (based on|from) (.*)/
v = Version.new($2)
v.head = true
v
else
Version.new(string)
end
end
attr_accessor :head
alias_method :head?, :head
def to_s
head? ? "HEAD based on #{super}" : super
end
end
VERSION = '0.16.0.rc2'
end
module Pod
class Version < Gem::Version
# @returns A Version described by its #to_s method.
#
# @TODO The `from' part of the regexp should be remove before 1.0.0.
#
def self.from_string(string)
if string =~ /HEAD (based on|from) (.*)/
v = Version.new($2)
v.head = true
v
else
Version.new(string)
end
end
attr_accessor :head
alias_method :head?, :head
def to_s
head? ? "HEAD based on #{super}" : super
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