cocoapods 0.5.1 → 0.6.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. data/CHANGELOG.md +229 -2
  2. data/README.md +50 -20
  3. data/bin/pod +3 -2
  4. data/lib/cocoapods.rb +23 -9
  5. data/lib/cocoapods/command.rb +71 -30
  6. data/lib/cocoapods/command/error_report.rb +102 -0
  7. data/lib/cocoapods/command/install.rb +27 -19
  8. data/lib/cocoapods/command/list.rb +51 -8
  9. data/lib/cocoapods/command/presenter.rb +61 -0
  10. data/lib/cocoapods/command/presenter/cocoa_pod.rb +123 -0
  11. data/lib/cocoapods/command/push.rb +102 -0
  12. data/lib/cocoapods/command/repo.rb +70 -14
  13. data/lib/cocoapods/command/search.rb +7 -10
  14. data/lib/cocoapods/command/setup.rb +76 -15
  15. data/lib/cocoapods/command/spec.rb +581 -97
  16. data/lib/cocoapods/config.rb +23 -26
  17. data/lib/cocoapods/dependency.rb +86 -40
  18. data/lib/cocoapods/downloader.rb +30 -18
  19. data/lib/cocoapods/downloader/git.rb +125 -15
  20. data/lib/cocoapods/downloader/http.rb +73 -0
  21. data/lib/cocoapods/downloader/mercurial.rb +3 -9
  22. data/lib/cocoapods/downloader/subversion.rb +3 -9
  23. data/lib/cocoapods/executable.rb +26 -3
  24. data/lib/cocoapods/generator/acknowledgements.rb +37 -0
  25. data/lib/cocoapods/generator/acknowledgements/markdown.rb +38 -0
  26. data/lib/cocoapods/generator/acknowledgements/plist.rb +63 -0
  27. data/lib/cocoapods/generator/copy_resources_script.rb +8 -4
  28. data/lib/cocoapods/generator/documentation.rb +99 -0
  29. data/lib/cocoapods/generator/dummy_source.rb +14 -0
  30. data/lib/cocoapods/installer.rb +140 -109
  31. data/lib/cocoapods/installer/target_installer.rb +78 -83
  32. data/lib/cocoapods/installer/user_project_integrator.rb +162 -0
  33. data/lib/cocoapods/local_pod.rb +240 -0
  34. data/lib/cocoapods/platform.rb +41 -18
  35. data/lib/cocoapods/podfile.rb +234 -21
  36. data/lib/cocoapods/project.rb +67 -0
  37. data/lib/cocoapods/resolver.rb +62 -32
  38. data/lib/cocoapods/sandbox.rb +63 -0
  39. data/lib/cocoapods/source.rb +42 -20
  40. data/lib/cocoapods/specification.rb +294 -271
  41. data/lib/cocoapods/specification/set.rb +10 -28
  42. data/lib/cocoapods/specification/statistics.rb +112 -0
  43. metadata +124 -11
  44. data/lib/cocoapods/xcodeproj_pods.rb +0 -111
@@ -0,0 +1,102 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rbconfig'
4
+ require 'cgi'
5
+ require 'rubygems'
6
+
7
+ module Pod
8
+ class Command
9
+ module ErrorReport
10
+ class << self
11
+ def report(error)
12
+ return <<-EOS
13
+
14
+ #{'――― MARKDOWN TEMPLATE ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――'.reversed}
15
+
16
+ ### Report
17
+
18
+ * What did you do?
19
+
20
+ * What did you expect to happen?
21
+
22
+ * What happened instead?
23
+
24
+
25
+ ### Stack
26
+
27
+ ```
28
+ CocoaPods : #{Pod::VERSION}
29
+ Ruby : #{RUBY_DESCRIPTION}
30
+ RubyGems : #{Gem::VERSION}
31
+ Host : #{host_information}
32
+ Xcode : #{xcode_information}
33
+ Ruby lib dir : #{RbConfig::CONFIG['libdir']}
34
+ Repositories : #{repo_information.join("\n ")}
35
+ ```
36
+ #{markdown_podfile}
37
+ ### Error
38
+
39
+ ```
40
+ #{error.message}
41
+ #{error.backtrace.join("\n")}
42
+ ```
43
+
44
+ #{'――― TEMPLATE END ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――'.reversed}
45
+
46
+ #{'[!] Oh no, an error occurred.'.red}
47
+ #{error_from_podfile(error)}
48
+ #{'Search for existing github issues similar to yours:'.yellow}
49
+ #{"https://github.com/CocoaPods/CocoaPods/issues/search?q=#{CGI.escape(error.message)}"}
50
+
51
+ #{'If none exists, create a ticket, with the template displayed above, on:'.yellow}
52
+ https://github.com/CocoaPods/CocoaPods/issues/new
53
+
54
+ Don't forget to anonymize any private data!
55
+
56
+ EOS
57
+ end
58
+
59
+ private
60
+
61
+ def markdown_podfile
62
+ return '' unless Config.instance.project_podfile && Config.instance.project_podfile.exist?
63
+ <<-EOS
64
+
65
+ ### Podfile
66
+
67
+ ```ruby
68
+ #{Config.instance.project_podfile.read.strip}
69
+ ```
70
+ EOS
71
+ end
72
+
73
+ def error_from_podfile(error)
74
+ if error.message =~ /Podfile:(\d*)/
75
+ "\nIt appears to have originated from your Podfile at line #{$1}.\n"
76
+ end
77
+ end
78
+
79
+ def host_information
80
+ product, version, build =`sw_vers`.strip.split("\n").map { |line| line.split(":").last.strip }
81
+ "#{product} #{version} (#{build})"
82
+ end
83
+
84
+ def xcode_information
85
+ version, build = `xcodebuild -version`.strip.split("\n").map { |line| line.split(" ").last }
86
+ "#{version} (#{build})"
87
+ end
88
+
89
+ def repo_information
90
+ Pod::Source.all.map do |source|
91
+ repo = source.repo
92
+ Dir.chdir(repo) do
93
+ url = `git config --get remote.origin.url`.strip
94
+ sha = `git rev-parse HEAD`.strip
95
+ "#{repo.basename} - #{url} @ #{sha}"
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -4,45 +4,53 @@ module Pod
4
4
  def self.banner
5
5
  %{Installing dependencies of a project:
6
6
 
7
- $ pod install [PROJECT]
7
+ $ pod install
8
8
 
9
9
  Downloads all dependencies defined in `Podfile' and creates an Xcode
10
10
  Pods library project in `./Pods'.
11
11
 
12
- In case `PROJECT' is given, it configures it to use the specified Pods
13
- and generates a workspace with the Pods project and `PROJECT'. (It is
14
- important that once you have run this you open the workspace instead of
15
- `PROJECT'.) You usually specify `PROJECT' only the first time that you
16
- run `pod install'.
12
+ The Xcode project file should be specified in your `Podfile` like this:
13
+
14
+ xcodeproj 'path/to/XcodeProject'
15
+
16
+ If no xcodeproj is specified, then a search for an Xcode project will
17
+ be made. If more than one Xcode project is found, the command will
18
+ raise an error.
19
+
20
+ This will configure the project to reference the Pods static library,
21
+ add a build configuration file, and add a post build script to copy
22
+ Pod resources.
17
23
  }
18
24
  end
19
25
 
20
26
  def self.options
21
- " --no-clean Leave SCM dirs like `.git' and `.svn' in tact after downloading\n" +
22
- " --no-update Skip running `pod repo update` before install\n" +
23
- super
27
+ [
28
+ ["--no-clean", "Leave SCM dirs like `.git' and `.svn' intact after downloading"],
29
+ ["--no-doc", "Skip documentation generation with appledoc"],
30
+ ["--no-integrate", "Skip integration of the Pods libraries in the Xcode project(s)"],
31
+ ["--no-update", "Skip running `pod repo update` before install"],
32
+ ].concat(super)
24
33
  end
25
34
 
26
35
  def initialize(argv)
27
- config.clean = !argv.option('--no-clean')
28
- @update_repo = !argv.option('--no-update')
29
- @projpath = argv.shift_argument
36
+ config.clean = !argv.option('--no-clean')
37
+ config.generate_docs = !argv.option('--no-doc')
38
+ config.integrate_targets = !argv.option('--no-integrate')
39
+ @update_repo = !argv.option('--no-update')
30
40
  super unless argv.empty?
31
41
  end
32
42
 
33
43
  def run
34
- unless podfile = config.rootspec
44
+ unless podfile = config.podfile
35
45
  raise Informative, "No `Podfile' found in the current working directory."
36
46
  end
37
- if @projpath && !File.exist?(@projpath)
38
- raise Informative, "The specified project `#{@projpath}' does not exist."
39
- end
47
+
40
48
  if @update_repo
49
+ print_title 'Updating Spec Repositories', true
41
50
  Repo.new(ARGV.new(["update"])).run
42
51
  end
43
- installer = Installer.new(podfile)
44
- installer.install!
45
- installer.configure_project(@projpath) if @projpath
52
+
53
+ Installer.new(podfile).install!
46
54
  end
47
55
  end
48
56
  end
@@ -2,24 +2,67 @@ module Pod
2
2
  class Command
3
3
  class List < Command
4
4
  def self.banner
5
- %{List all pods:
5
+ %{List all pods:
6
6
 
7
7
  $ pod list
8
8
 
9
- Lists all available pods.}
9
+ Lists all available pods.
10
+
11
+ $ pod list new
12
+
13
+ Lists the pods introduced in the master repository since the last check.}
14
+ end
15
+
16
+ def self.options
17
+ [["--update", "Run `pod repo update` before listing"]].concat(Presenter.options).concat(super)
10
18
  end
11
19
 
20
+ extend Executable
21
+ executable :git
22
+
12
23
  def initialize(argv)
24
+ @update = argv.option('--update')
25
+ @new = argv.option('new')
26
+ @presenter = Presenter.new(argv)
27
+ super unless argv.empty?
13
28
  end
14
29
 
15
- def run
16
- Source.all.each do |source|
17
- source.pod_sets.each do |set|
18
- puts "==> #{set.name} (#{set.versions.reverse.join(", ")})"
19
- puts " #{set.specification.summary.strip}"
20
- puts
30
+ def list_all
31
+ sets = Source.all_sets
32
+ sets.each {|s| puts @presenter.describe(s)}
33
+ puts "\n#{sets.count} pods were found"
34
+ end
35
+
36
+ def list_new
37
+ days = [1,2,3,5,8]
38
+ dates, groups = {}, {}
39
+ days.each {|d| dates[d] = Time.now - 60 * 60 * 24 * d}
40
+ sets = Source.all_sets
41
+ creation_dates = Pod::Specification::Statistics.instance.creation_dates(sets)
42
+
43
+ sets.each do |set|
44
+ set_date = creation_dates[set.name]
45
+ days.each do |d|
46
+ if set_date >= dates[d]
47
+ groups[d] = [] unless groups[d]
48
+ groups[d] << set
49
+ break
50
+ end
21
51
  end
22
52
  end
53
+ days.reverse.each do |d|
54
+ sets = groups[d]
55
+ next unless sets
56
+ puts "\nPods added in the last #{d == 1 ? 'day' : "#{d} days"}".yellow
57
+ sets.sort_by {|s| creation_dates[s.name]}.each {|s| puts @presenter.describe(s)}
58
+ end
59
+ end
60
+
61
+ def run
62
+ puts "\nUpdating Spec Repositories\n".yellow if @update && config.verbose?
63
+ Repo.new(ARGV.new(["update"])).run if @update
64
+ @new ? list_new : list_all
65
+ puts
23
66
  end
24
67
  end
25
68
  end
@@ -0,0 +1,61 @@
1
+ module Pod
2
+ class Command
3
+ class Presenter
4
+ def self.options
5
+ [["--stats", "Show additional stats (like GitHub watchers and forks)"]]
6
+ end
7
+
8
+ autoload :CocoaPod, 'cocoapods/command/presenter/cocoa_pod'
9
+
10
+ def initialize(argv)
11
+ @stats = argv.option('--stats')
12
+ end
13
+
14
+ def render(array)
15
+ result = "\n"
16
+ seats.each {|s| puts describe(s)}
17
+ result
18
+ end
19
+
20
+ def describe(set)
21
+ pod = CocoaPod.new(set)
22
+ result = "\n--> #{pod.name} (#{pod.versions})\n".green
23
+ result << wrap_string(pod.summary)
24
+ result << detail('Homepage', pod.homepage)
25
+ result << detail('Source', pod.source_url)
26
+ if @stats
27
+ result << detail('Pushed', pod.github_last_activity)
28
+ result << detail('Authors', pod.authors) if pod.authors =~ /,/
29
+ result << detail('Author', pod.authors) if pod.authors !~ /,/
30
+ result << detail('License', pod.license)
31
+ result << detail('Platform', pod.platform)
32
+ result << detail('Watchers', pod.github_watchers)
33
+ result << detail('Forks', pod.github_forks)
34
+ end
35
+ result << detail('Sub specs', pod.subspecs)
36
+ result
37
+ end
38
+
39
+ private
40
+
41
+ # adapted from http://blog.macromates.com/2006/wrapping-text-with-regular-expressions/
42
+ def wrap_string(txt, col = 80, indentation = 4)
43
+ indent = ' ' * indentation
44
+ txt.strip.gsub(/(.{1,#{col}})( +|$)\n?|(.{#{col}})/, indent + "\\1\\3\n")
45
+ end
46
+
47
+ def detail(title, value)
48
+ return '' if !value
49
+ ''.tap do |t|
50
+ t << " - #{title}:".ljust(16)
51
+ if value.class == Array
52
+ separator = "\n - "
53
+ t << separator << value.join(separator)
54
+ else
55
+ t << value.to_s << "\n"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,123 @@
1
+ module Pod
2
+ class Command
3
+ class Presenter
4
+ class CocoaPod
5
+ attr_accessor :set
6
+
7
+ def initialize(set)
8
+ @set = set
9
+ end
10
+
11
+ # set information
12
+ def name
13
+ @set.name
14
+ end
15
+
16
+ def version
17
+ @set.versions.last
18
+ end
19
+
20
+ def versions
21
+ @set.versions.reverse.join(", ")
22
+ end
23
+
24
+ # specification information
25
+ def spec
26
+ @set.specification
27
+ end
28
+
29
+ def authors
30
+ oxfordify spec.authors.keys
31
+ end
32
+
33
+ def homepage
34
+ spec.homepage
35
+ end
36
+
37
+ def description
38
+ spec.description
39
+ end
40
+
41
+ def summary
42
+ spec.summary
43
+ end
44
+
45
+ def source_url
46
+ spec.source.reject {|k,_| k == :commit || k == :tag }.values.first
47
+ end
48
+
49
+ def platform
50
+ spec.available_platforms.sort { |a,b| a.to_s.downcase <=> b.to_s.downcase }.join(' - ')
51
+ end
52
+
53
+ def license
54
+ spec.license[:type] if spec.license
55
+ end
56
+
57
+ # will return array of all subspecs (recursevly) or nil
58
+ def subspecs
59
+ (spec.recursive_subspecs.any? && spec.recursive_subspecs) || nil
60
+ end
61
+
62
+ # Statistics information
63
+ def creation_date
64
+ Pod::Specification::Statistics.instance.creation_date(@set)
65
+ end
66
+
67
+ def github_watchers
68
+ Pod::Specification::Statistics.instance.github_watchers(@set)
69
+ end
70
+
71
+ def github_forks
72
+ Pod::Specification::Statistics.instance.github_forks(@set)
73
+ end
74
+
75
+ def github_last_activity
76
+ distance_from_now_in_words(Pod::Specification::Statistics.instance.github_pushed_at(@set))
77
+ end
78
+
79
+ def ==(other)
80
+ self.class === other && @set == other.set
81
+ end
82
+
83
+ def eql?(other)
84
+ self.class === other && name.eql?(other.name)
85
+ end
86
+
87
+ def hash
88
+ name.hash
89
+ end
90
+
91
+ private
92
+ def oxfordify words
93
+ if words.size < 3
94
+ words.join ' and '
95
+ else
96
+ "#{words[0..-2].join(', ')}, and #{words.last}"
97
+ end
98
+ end
99
+
100
+ def distance_from_now_in_words(from_time)
101
+ return nil unless from_time
102
+ from_time = Time.parse(from_time)
103
+ to_time = Time.now
104
+ distance_in_days = (((to_time - from_time).abs)/60/60/24).round
105
+
106
+ case distance_in_days
107
+ when 0..7
108
+ "less than a week ago"
109
+ when 8..29
110
+ "#{distance_in_days} days ago"
111
+ when 30..45
112
+ "1 month ago"
113
+ when 46..365
114
+ "#{(distance_in_days.to_f / 30).round} months ago"
115
+ else
116
+ "more than a year ago"
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+
@@ -0,0 +1,102 @@
1
+ require 'fileutils'
2
+
3
+ module Pod
4
+ class Command
5
+ class Push < Command
6
+ def self.banner
7
+ %{Pushing new specifications to a spec-repo:
8
+
9
+ $ pod push [REPO]
10
+
11
+ Validates `*.podspec' in the current working dir, updates
12
+ the local copy of the repository named REPO, adds specifications
13
+ to REPO, and finally it pushes REPO to its remote.}
14
+ end
15
+
16
+ def self.options
17
+ [["--allow-warnings", "Allows to push if warnings are not evitable"]].concat(super)
18
+ end
19
+
20
+ extend Executable
21
+ executable :git
22
+
23
+ def initialize(argv)
24
+ @allow_warnings = argv.option('--allow-warnings')
25
+ @repo = argv.shift_argument
26
+ super unless argv.empty? && @repo
27
+ end
28
+
29
+ def run
30
+ validate_podspec_files
31
+ check_repo_status
32
+ update_repo
33
+ add_specs_to_repo
34
+ push_repo
35
+ puts
36
+ end
37
+
38
+ private
39
+
40
+ def update_repo
41
+ puts "Updating the `#{@repo}' repo\n".yellow unless config.silent
42
+ # show the output of git even if not verbose
43
+ Dir.chdir(repo_dir) { puts `git pull 2>&1` }
44
+ end
45
+
46
+ def push_repo
47
+ puts "\nPushing the `#{@repo}' repo\n".yellow unless config.silent
48
+ Dir.chdir(repo_dir) { puts `git push 2>&1` }
49
+ end
50
+
51
+ def repo_dir
52
+ dir = config.repos_dir + @repo
53
+ raise Informative, "[!] `#{@repo}' repo not found".red unless dir.exist?
54
+ dir
55
+ end
56
+
57
+ def check_repo_status
58
+ # TODO: add specs for staged and unstaged files (tested manually)
59
+ clean = Dir.chdir(repo_dir) { `git status --porcelain 2>&1` } == ''
60
+ raise Informative, "[!] `#{@repo}' repo not clean".red unless clean
61
+ end
62
+
63
+ def podspec_files
64
+ files = Pathname.glob("*.podspec")
65
+ raise Informative, "[!] Couldn't find .podspec file in current directory".red if files.empty?
66
+ files
67
+ end
68
+
69
+ def validate_podspec_files
70
+ puts "\nValidating specs".yellow unless config.silent
71
+ lint_argv = ["lint"]
72
+ lint_argv << "--only-errors" if @allow_warnings
73
+ lint_argv << "--silent" if config.silent
74
+ all_valid = Spec.new(ARGV.new(lint_argv)).run
75
+ end
76
+
77
+ def add_specs_to_repo
78
+ puts "\nAdding the specs to the #{@repo} repo\n".yellow unless config.silent
79
+ podspec_files.each do |spec_file|
80
+ spec = Pod::Specification.from_file(spec_file)
81
+ output_path = File.join(repo_dir, spec.name, spec.version.to_s)
82
+ if Pathname.new(output_path).exist?
83
+ message = "[Fix] #{spec}"
84
+ elsif Pathname.new(File.join(repo_dir, spec.name)).exist?
85
+ message = "[Update] #{spec}"
86
+ else
87
+ message = "[Add] #{spec}"
88
+ end
89
+ puts " - #{message}" unless config.silent
90
+
91
+ FileUtils.mkdir_p(output_path)
92
+ FileUtils.cp(Pathname.new(spec.name+'.podspec'), output_path)
93
+ Dir.chdir(repo_dir) do
94
+ git("add #{spec.name}")
95
+ # Bypass the pre-commit hook because we already performed validation
96
+ git("commit --no-verify -m '#{message}'")
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end