cocoapods 0.16.4 → 0.17.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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +108 -0
  3. data/README.md +3 -3
  4. data/bin/pod +1 -1
  5. data/lib/cocoapods.rb +31 -31
  6. data/lib/cocoapods/command.rb +62 -107
  7. data/lib/cocoapods/command/inter_process_communication.rb +103 -0
  8. data/lib/cocoapods/command/list.rb +45 -44
  9. data/lib/cocoapods/command/outdated.rb +28 -25
  10. data/lib/cocoapods/command/project.rb +90 -0
  11. data/lib/cocoapods/command/push.rb +50 -32
  12. data/lib/cocoapods/command/repo.rb +125 -155
  13. data/lib/cocoapods/command/search.rb +23 -12
  14. data/lib/cocoapods/command/setup.rb +103 -64
  15. data/lib/cocoapods/command/spec.rb +329 -90
  16. data/lib/cocoapods/config.rb +197 -44
  17. data/lib/cocoapods/downloader.rb +47 -34
  18. data/lib/cocoapods/executable.rb +98 -41
  19. data/lib/cocoapods/external_sources.rb +325 -0
  20. data/lib/cocoapods/file_list.rb +8 -1
  21. data/lib/cocoapods/gem_version.rb +7 -0
  22. data/lib/cocoapods/generator/acknowledgements.rb +71 -7
  23. data/lib/cocoapods/generator/acknowledgements/markdown.rb +10 -9
  24. data/lib/cocoapods/generator/acknowledgements/plist.rb +9 -8
  25. data/lib/cocoapods/generator/copy_resources_script.rb +2 -2
  26. data/lib/cocoapods/generator/documentation.rb +153 -37
  27. data/lib/cocoapods/generator/prefix_header.rb +82 -0
  28. data/lib/cocoapods/generator/target_header.rb +58 -0
  29. data/lib/cocoapods/generator/xcconfig.rb +130 -0
  30. data/lib/cocoapods/hooks/installer_representation.rb +123 -0
  31. data/lib/cocoapods/hooks/library_representation.rb +79 -0
  32. data/lib/cocoapods/hooks/pod_representation.rb +74 -0
  33. data/lib/cocoapods/installer.rb +398 -147
  34. data/lib/cocoapods/installer/analyzer.rb +556 -0
  35. data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +253 -0
  36. data/lib/cocoapods/installer/file_references_installer.rb +179 -0
  37. data/lib/cocoapods/installer/pod_source_installer.rb +289 -0
  38. data/lib/cocoapods/installer/target_installer.rb +307 -112
  39. data/lib/cocoapods/installer/user_project_integrator.rb +140 -176
  40. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +193 -0
  41. data/lib/cocoapods/library.rb +195 -0
  42. data/lib/cocoapods/open_uri.rb +16 -14
  43. data/lib/cocoapods/project.rb +175 -52
  44. data/lib/cocoapods/resolver.rb +151 -164
  45. data/lib/cocoapods/sandbox.rb +276 -54
  46. data/lib/cocoapods/sandbox/file_accessor.rb +210 -0
  47. data/lib/cocoapods/sandbox/headers_store.rb +96 -0
  48. data/lib/cocoapods/sandbox/path_list.rb +178 -0
  49. data/lib/cocoapods/sources_manager.rb +218 -0
  50. data/lib/cocoapods/user_interface.rb +82 -18
  51. data/lib/cocoapods/{command → user_interface}/error_report.rb +5 -5
  52. data/lib/cocoapods/validator.rb +379 -0
  53. metadata +74 -55
  54. data/lib/cocoapods/command/install.rb +0 -55
  55. data/lib/cocoapods/command/linter.rb +0 -317
  56. data/lib/cocoapods/command/update.rb +0 -25
  57. data/lib/cocoapods/dependency.rb +0 -285
  58. data/lib/cocoapods/downloader/git.rb +0 -276
  59. data/lib/cocoapods/downloader/http.rb +0 -99
  60. data/lib/cocoapods/downloader/mercurial.rb +0 -26
  61. data/lib/cocoapods/downloader/subversion.rb +0 -42
  62. data/lib/cocoapods/local_pod.rb +0 -620
  63. data/lib/cocoapods/lockfile.rb +0 -274
  64. data/lib/cocoapods/platform.rb +0 -127
  65. data/lib/cocoapods/podfile.rb +0 -551
  66. data/lib/cocoapods/source.rb +0 -223
  67. data/lib/cocoapods/specification.rb +0 -579
  68. data/lib/cocoapods/specification/set.rb +0 -175
  69. data/lib/cocoapods/specification/statistics.rb +0 -112
  70. data/lib/cocoapods/user_interface/ui_pod.rb +0 -130
  71. data/lib/cocoapods/version.rb +0 -26
@@ -1,17 +1,8 @@
1
1
  module Pod
2
2
  class Command
3
3
  class List < Command
4
- def self.banner
5
- %{List all pods:
6
-
7
- $ pod list
8
-
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
4
+ self.summary = 'List pods'
5
+ self.description = 'Lists all available pods.'
15
6
 
16
7
  def self.options
17
8
  [[
@@ -24,51 +15,61 @@ module Pod
24
15
  executable :git
25
16
 
26
17
  def initialize(argv)
27
- @update = argv.option('--update')
28
- @stats = argv.option('--stats')
29
- @new = argv.option('new')
30
- super unless argv.empty?
18
+ @update = argv.flag?('update')
19
+ @stats = argv.flag?('stats')
20
+ super
31
21
  end
32
22
 
33
- def list_all
34
- sets = Source.all_sets
23
+ def run
24
+ update_if_necessary!
25
+
26
+ sets = SourcesManager.all_sets
35
27
  sets.each { |set| UI.pod(set, :name) }
36
28
  UI.puts "\n#{sets.count} pods were found"
37
29
  end
38
30
 
39
- def list_new
40
- days = [1,2,3,5,8]
41
- dates, groups = {}, {}
42
- days.each {|d| dates[d] = Time.now - 60 * 60 * 24 * d}
43
- sets = Source.all_sets
44
- creation_dates = Pod::Specification::Statistics.instance.creation_dates(sets)
31
+ def update_if_necessary!
32
+ if @update && config.verbose?
33
+ UI.section("\nUpdating Spec Repositories\n".yellow) do
34
+ Repo.new(ARGV.new(["update"])).run
35
+ end
36
+ end
37
+ end
45
38
 
46
- sets.each do |set|
47
- set_date = creation_dates[set.name]
48
- days.each do |d|
49
- if set_date >= dates[d]
50
- groups[d] = [] unless groups[d]
51
- groups[d] << set
52
- break
39
+ #-----------------------------------------------------------------------#
40
+
41
+ class New < List
42
+ self.summary = 'Lists pods introduced in the master spec-repo since the last check'
43
+
44
+ def run
45
+ update_if_necessary!
46
+
47
+ days = [1,2,3,5,8]
48
+ dates, groups = {}, {}
49
+ days.each {|d| dates[d] = Time.now - 60 * 60 * 24 * d}
50
+ sets = SourcesManager.all_sets
51
+ creation_dates = Specification::Set::Statistics.instance.creation_dates(sets)
52
+
53
+ sets.each do |set|
54
+ set_date = creation_dates[set.name]
55
+ days.each do |d|
56
+ if set_date >= dates[d]
57
+ groups[d] = [] unless groups[d]
58
+ groups[d] << set
59
+ break
60
+ end
53
61
  end
54
62
  end
55
- end
56
- days.reverse.each do |d|
57
- sets = groups[d]
58
- next unless sets
59
- UI.section("\nPods added in the last #{"day".pluralize(d)}".yellow) do
60
- sorted = sets.sort_by {|s| creation_dates[s.name]}
61
- sorted.each { |set| UI.pod(set, (@stats ? :stats : :name)) }
63
+ days.reverse.each do |d|
64
+ sets = groups[d]
65
+ next unless sets
66
+ UI.section("\nPods added in the last #{"day".pluralize(d)}".yellow) do
67
+ sorted = sets.sort_by {|s| creation_dates[s.name]}
68
+ sorted.each { |set| UI.pod(set, (@stats ? :stats : :name)) }
69
+ end
62
70
  end
63
71
  end
64
72
  end
65
-
66
- def run
67
- UI.section("\nUpdating Spec Repositories\n".yellow) do
68
- Repo.new(ARGV.new(["update"])).run
69
- end if @update && config.verbose?
70
- @new ? list_new : list_all
71
- end
72
73
  end
73
74
  end
74
75
  end
@@ -1,47 +1,50 @@
1
1
  module Pod
2
2
  class Command
3
3
  class Outdated < Command
4
- def self.banner
5
- %{Show outdated pods:
4
+ self.summary = 'Show outdated project dependencies'
6
5
 
7
- $ pod outdated
8
-
9
- Shows the outdated pods in the current Podfile.lock, but only those from
10
- spec repos, not those from local/external sources or `:head' versions.}
11
- end
6
+ self.description = <<-DESC
7
+ Shows the outdated pods in the current Podfile.lock, but only those from
8
+ spec repos, not those from local/external sources or `:head` versions.
9
+ DESC
12
10
 
13
11
  def self.options
14
12
  [["--no-update", "Skip running `pod repo update` before install"]].concat(super)
15
13
  end
16
14
 
17
15
  def initialize(argv)
18
- config.skip_repo_update = argv.option('--no-update')
19
- super unless argv.empty?
16
+ config.skip_repo_update = argv.flag?('update', config.skip_repo_update)
17
+ super
20
18
  end
21
19
 
20
+ # @todo the command report new dependencies added to the Podfile as
21
+ # updates.
22
+ # @todo fix.
23
+ #
22
24
  def run
23
25
  verify_podfile_exists!
24
26
  verify_lockfile_exists!
25
27
 
26
- sandbox = Sandbox.new(config.project_pods_root)
27
- resolver = Resolver.new(config.podfile, config.lockfile, sandbox)
28
- resolver.update_mode = true
29
- resolver.update_external_specs = false
30
- resolver.resolve
31
-
32
- #TODO: the command report new dependencies (added to by updated ones)
33
- # as updates.
34
-
35
- names = resolver.pods_to_install - resolver.pods_from_external_sources
36
- specs = resolver.specs.select do |spec|
37
- names.include?(spec.name) && !spec.version.head?
28
+ lockfile = config.lockfile
29
+ pods = lockfile.pod_names
30
+ updates = []
31
+ pods.each do |pod_name|
32
+ set = SourcesManager.search(Dependency.new(pod_name))
33
+ source_version = set.versions.first
34
+ lockfile_version = lockfile.version(pod_name)
35
+ if source_version > lockfile_version
36
+ updates << [pod_name, lockfile_version, source_version]
37
+ end
38
38
  end
39
39
 
40
- if specs.empty?
41
- puts "No updates are available.".yellow
40
+ if updates.empty?
41
+ UI.puts "No updates are available.".yellow
42
42
  else
43
- puts "The following updates are available:".green
44
- puts " - " << specs.join("\n - ") << "\n"
43
+ UI.section "The following updates are available:" do
44
+ updates.each do |(name, from_version, to_version)|
45
+ UI.message "- #{name} #{from_version} -> #{to_version}"
46
+ end
47
+ end
45
48
  end
46
49
  end
47
50
  end
@@ -0,0 +1,90 @@
1
+ module Pod
2
+ class Command
3
+
4
+ # Provides support the common behaviour of the `install` and `update`
5
+ # commands.
6
+ #
7
+ module Project
8
+ module Options
9
+ def options
10
+ [
11
+ ["--no-clean", "Leave SCM dirs like `.git' and `.svn' intact after downloading"],
12
+ ["--no-doc", "Skip documentation generation with appledoc"],
13
+ ["--no-integrate", "Skip integration of the Pods libraries in the Xcode project(s)"],
14
+ ["--no-update", "Skip running `pod repo update` before install"],
15
+ ].concat(super)
16
+ end
17
+ end
18
+
19
+ def self.included(base)
20
+ base.extend Options
21
+ end
22
+
23
+ def initialize(argv)
24
+ config.clean = argv.flag?('clean', config.clean)
25
+ config.generate_docs = argv.flag?('doc', config.generate_docs)
26
+ config.integrate_targets = argv.flag?('integrate', config.integrate_targets)
27
+ config.skip_repo_update = !argv.flag?('update', !config.skip_repo_update)
28
+ super
29
+ end
30
+
31
+ # Runs the installer.
32
+ #
33
+ # @param [update] whether the installer should be run in update mode.
34
+ #
35
+ # @return [void]
36
+ #
37
+ def run_install_with_update(update)
38
+ installer = Installer.new(config.sandbox, config.podfile, config.lockfile)
39
+ installer.update_mode = update
40
+ installer.install!
41
+ end
42
+ end
43
+
44
+ #-------------------------------------------------------------------------#
45
+
46
+ class Install < Command
47
+ include Project
48
+
49
+ self.summary = 'Install project dependencies'
50
+
51
+ self.description = <<-DESC
52
+ Downloads all dependencies defined in `Podfile' and creates an Xcode
53
+ Pods library project in `./Pods'.
54
+
55
+ The Xcode project file should be specified in your `Podfile` like this:
56
+
57
+ xcodeproj 'path/to/XcodeProject'
58
+
59
+ If no xcodeproj is specified, then a search for an Xcode project will
60
+ be made. If more than one Xcode project is found, the command will
61
+ raise an error.
62
+
63
+ This will configure the project to reference the Pods static library,
64
+ add a build configuration file, and add a post build script to copy
65
+ Pod resources.
66
+ DESC
67
+
68
+ def run
69
+ verify_podfile_exists!
70
+ run_install_with_update(false)
71
+ end
72
+ end
73
+
74
+ #-------------------------------------------------------------------------#
75
+
76
+ class Update < Command
77
+ include Project
78
+
79
+ self.summary = 'Update outdated project dependencies'
80
+
81
+ def run
82
+ verify_podfile_exists!
83
+ verify_lockfile_exists!
84
+ run_install_with_update(true)
85
+ end
86
+ end
87
+
88
+ end
89
+ end
90
+
@@ -4,36 +4,40 @@ require 'active_support/core_ext/string/inflections'
4
4
  module Pod
5
5
  class Command
6
6
  class Push < Command
7
- def self.banner
8
- %{Pushing new specifications to a spec-repo:
7
+ self.summary = 'Push new specifications to a spec-repo'
9
8
 
10
- $ pod push REPO [NAME.podspec]
9
+ self.description = <<-DESC
10
+ Validates NAME.podspec or `*.podspec' in the current working dir, creates
11
+ a directory and version folder for the pod in the local copy of
12
+ REPO (~/.cocoapods/[REPO]), copies the podspec file into the version directory,
13
+ and finally it pushes REPO to its remote.
14
+ DESC
11
15
 
12
- Validates NAME.podspec or `*.podspec' in the current working dir, creates
13
- a directory and version folder for the pod in the local copy of
14
- REPO (~/.cocoapods/[REPO]), copies the podspec file into the version directory,
15
- and finally it pushes REPO to its remote.}
16
- end
16
+ self.arguments = 'REPO [NAME.podspec]'
17
17
 
18
18
  def self.options
19
19
  [ ["--allow-warnings", "Allows to push if warnings are not evitable"],
20
20
  ["--local-only", "Does not perform the step of pushing REPO to its remote"] ].concat(super)
21
21
  end
22
22
 
23
- extend Executable
24
- executable :git
25
-
26
23
  def initialize(argv)
27
- @allow_warnings = argv.option('--allow-warnings')
28
- @local_only = argv.option('--local-only')
24
+ @allow_warnings = argv.flag?('allow-warnings')
25
+ @local_only = argv.flag?('local-only')
29
26
  @repo = argv.shift_argument
30
- if @repo.end_with? ".podspec"
27
+ if @repo.nil?
28
+ @repo = "master"
29
+ elsif @repo.end_with? ".podspec"
31
30
  @podspec = @repo
32
31
  @repo = "master"
33
32
  else
34
33
  @podspec = argv.shift_argument
35
34
  end
36
- super unless argv.empty? && @repo
35
+ super
36
+ end
37
+
38
+ def validate!
39
+ super
40
+ help! "A spec-repo name is required." unless @repo
37
41
  end
38
42
 
39
43
  def run
@@ -44,35 +48,39 @@ module Pod
44
48
  push_repo unless @local_only
45
49
  end
46
50
 
51
+ #--------------------------------------#
52
+
47
53
  private
48
54
 
55
+ extend Executable
56
+ executable :git
57
+
49
58
  def update_repo
50
- UI.puts "Updating the `#{@repo}' repo\n".yellow unless config.silent
51
- # show the output of git even if not verbose
52
- # TODO: use the `git!' and find a way to show the output in realtime.
59
+ UI.puts "Updating the `#{@repo}' repo\n".yellow
53
60
  Dir.chdir(repo_dir) { UI.puts `git pull 2>&1` }
54
61
  end
55
62
 
56
63
  def push_repo
57
- UI.puts "\nPushing the `#{@repo}' repo\n".yellow unless config.silent
64
+ UI.puts "\nPushing the `#{@repo}' repo\n".yellow
58
65
  Dir.chdir(repo_dir) { UI.puts `git push 2>&1` }
59
66
  end
60
67
 
61
68
  def repo_dir
62
69
  dir = config.repos_dir + @repo
63
- raise Informative, "[!] `#{@repo}' repo not found".red unless dir.exist?
70
+ raise Informative, "`#{@repo}` repo not found" unless dir.exist?
64
71
  dir
65
72
  end
66
73
 
74
+ # @todo: Add specs for staged and unstaged files.
75
+ #
67
76
  def check_repo_status
68
- # TODO: add specs for staged and unstaged files (tested manually)
69
77
  clean = Dir.chdir(repo_dir) { `git status --porcelain 2>&1` } == ''
70
- raise Informative, "[!] `#{@repo}' repo not clean".red unless clean
78
+ raise Informative, "The repo `#{@repo}` is not clean" unless clean
71
79
  end
72
80
 
73
81
  def podspec_files
74
82
  files = Pathname.glob(@podspec || "*.podspec")
75
- raise Informative, "[!] Couldn't find any .podspec file in current directory".red if files.empty?
83
+ raise Informative, "Couldn't find any .podspec file in current directory" if files.empty?
76
84
  files
77
85
  end
78
86
 
@@ -83,18 +91,29 @@ module Pod
83
91
  end
84
92
 
85
93
  def validate_podspec_files
86
- UI.puts "\nValidating #{'spec'.pluralize(count)}".yellow unless config.silent
87
- lint_argv = ["lint"]
88
- lint_argv << "--only-errors" if @allow_warnings
89
- lint_argv << "--silent" if config.silent
90
- # all_valid = true
94
+ UI.puts "\nValidating #{'spec'.pluralize(count)}".yellow
91
95
  podspec_files.each do |podspec|
92
- Spec.new(ARGV.new(lint_argv + [podspec.to_s])).run
96
+ validator = Validator.new(podspec)
97
+ begin
98
+ validator.validate
99
+ rescue Exception => e
100
+ raise Informative, "The `#{podspec}` specification does not validate."
101
+ end
102
+ raise Informative, "The `#{podspec}` specification does not validate." unless validator.validated?
93
103
  end
94
104
  end
95
105
 
106
+ # Commits the podspecs to the source, which should be a git repo.
107
+ #
108
+ # @note The pre commit hook of the repo is skipped as the podspecs have
109
+ # already been linted.
110
+ #
111
+ # @todo Raise if the source is not under git source control.
112
+ #
113
+ # @return [void]
114
+ #
96
115
  def add_specs_to_repo
97
- UI.puts "\nAdding the #{'spec'.pluralize(count)} to the `#{@repo}' repo\n".yellow unless config.silent
116
+ UI.puts "\nAdding the #{'spec'.pluralize(count)} to the `#{@repo}' repo\n".yellow
98
117
  podspec_files.each do |spec_file|
99
118
  spec = Pod::Specification.from_file(spec_file)
100
119
  output_path = File.join(repo_dir, spec.name, spec.version.to_s)
@@ -105,13 +124,12 @@ module Pod
105
124
  else
106
125
  message = "[Add] #{spec}"
107
126
  end
108
- UI.puts " - #{message}" unless config.silent
127
+ UI.puts " - #{message}"
109
128
 
110
129
  FileUtils.mkdir_p(output_path)
111
130
  FileUtils.cp(Pathname.new(spec.name+'.podspec'), output_path)
112
131
  Dir.chdir(repo_dir) do
113
132
  git!("add #{spec.name}")
114
- # Bypass the pre-commit hook because we already performed validation
115
133
  git!("commit --no-verify -m '#{message}'")
116
134
  end
117
135
  end
@@ -3,194 +3,164 @@ require 'fileutils'
3
3
  module Pod
4
4
  class Command
5
5
  class Repo < Command
6
- def self.banner
7
- %{Managing spec-repos:
6
+ self.abstract_command = true
8
7
 
9
- $ pod repo add NAME URL [BRANCH]
8
+ # @todo should not show a usage banner!
9
+ #
10
+ self.summary = 'Manage spec-repositories'
10
11
 
11
- Clones `URL' in the local spec-repos directory at `~/.cocoapods'. The
12
- remote can later be referred to by `NAME'.
12
+ class Add < Repo
13
+ self.summary = 'Add a spec repo.'
13
14
 
14
- $ pod repo update [NAME]
15
+ self.description = <<-DESC
16
+ Clones `URL` in the local spec-repos directory at `~/.cocoapods`. The
17
+ remote can later be referred to by `NAME`.
18
+ DESC
15
19
 
16
- Updates the local clone of the spec-repo `NAME'. If `NAME' is omitted
17
- this will update all spec-repos in `~/.cocoapods'.
20
+ self.arguments = 'NAME URL [BRANCH]'
18
21
 
19
- $ pod repo lint [NAME | DIRECTORY]
20
-
21
- Lints the spec-repo `NAME'. If a directory is provided it is assumed
22
- to be the root of a repo. Finally, if NAME is not provided this will
23
- lint all the spec-repos known to CocoaPods.}
24
- end
25
-
26
- def self.options
27
- [["--only-errors", "Lint presents only the errors"]].concat(super)
28
- end
22
+ def initialize(argv)
23
+ @name, @url, @branch = argv.shift_argument, argv.shift_argument, argv.shift_argument
24
+ super
25
+ end
29
26
 
30
- extend Executable
31
- executable :git
27
+ def validate!
28
+ super
29
+ unless @name && @url
30
+ help! "Adding a repo needs a `NAME` and a `URL`."
31
+ end
32
+ end
32
33
 
33
- def initialize(argv)
34
- case @action = argv.arguments[0]
35
- when 'add'
36
- unless (@name = argv.arguments[1]) && (@url = argv.arguments[2])
37
- raise Informative, "#{@action == 'add' ? 'Adding' : 'Updating the remote of'} a repo needs a `name' and a `url'."
34
+ def run
35
+ UI.section("Cloning spec repo `#{@name}` from `#{@url}`#{" (branch `#{@branch}`)" if @branch}") do
36
+ config.repos_dir.mkpath
37
+ Dir.chdir(config.repos_dir) { git!("clone '#{@url}' #{@name}") }
38
+ Dir.chdir(dir) { git!("checkout #{@branch}") } if @branch
39
+ SourcesManager.check_version_information(dir)
38
40
  end
39
- @branch = argv.arguments[3]
40
- when 'update'
41
- @name = argv.arguments[1]
42
- when 'lint'
43
- @name = argv.arguments[1]
44
- @only_errors = argv.option('--only-errors')
45
- else
46
- super
47
41
  end
48
42
  end
49
43
 
50
- def dir
51
- config.repos_dir + @name
52
- end
44
+ #-----------------------------------------------------------------------#
53
45
 
54
- def run
55
- send @action.gsub('-', '_')
56
- end
46
+ class Update < Repo
47
+ self.summary = 'Update a spec repo.'
57
48
 
58
- def add
59
- UI.section("Cloning spec repo `#{@name}' from `#{@url}'#{" (branch `#{@branch}')" if @branch}") do
60
- config.repos_dir.mkpath
61
- Dir.chdir(config.repos_dir) { git!("clone '#{@url}' #{@name}") }
62
- Dir.chdir(dir) { git!("checkout #{@branch}") } if @branch
63
- check_versions(dir)
64
- end
65
- end
49
+ self.description = <<-DESC
50
+ Updates the local clone of the spec-repo `NAME`. If `NAME` is omitted
51
+ this will update all spec-repos in `~/.cocoapods`.
52
+ DESC
66
53
 
67
- def update
68
- dirs = @name ? [dir] : config.repos_dir.children.select {|c| c.directory?}
69
- dirs.each do |dir|
70
- UI.section "Updating spec repo `#{dir.basename}'" do
71
- Dir.chdir(dir) do
72
- `git rev-parse >/dev/null 2>&1`
73
- if $?.exitstatus.zero?
74
- git!("pull")
75
- else
76
- UI.message "Not a git repository"
77
- end
78
- end
79
- end
80
- check_versions(dir)
81
- end
82
- end
54
+ self.arguments = '[NAME]'
83
55
 
84
- def lint
85
- if @name
86
- dirs = File.exists?(@name) ? [ Pathname.new(@name) ] : [ dir ]
87
- else
88
- dirs = config.repos_dir.children.select {|c| c.directory?}
56
+ def initialize(argv)
57
+ @name = argv.shift_argument
58
+ super
89
59
  end
90
- dirs.each do |dir|
91
- check_versions(dir)
92
- UI.puts "\nLinting spec repo `#{dir.realpath.basename}'\n".yellow
93
- podspecs = Pathname.glob( dir + '**/*.podspec')
94
- invalid_count = 0
95
-
96
- podspecs.each do |podspec|
97
- linter = Linter.new(podspec)
98
- linter.quick = true
99
- linter.repo_path = dir
100
-
101
- linter.lint
102
-
103
- case linter.result_type
104
- when :error
105
- invalid_count += 1
106
- color = :red
107
- should_display = true
108
- when :warning
109
- color = :yellow
110
- should_display = !@only_errors
111
- end
112
-
113
- if should_display
114
- UI.puts " -> ".send(color) << linter.spec_name
115
- print_messages('ERROR', linter.errors)
116
- unless @only_errors
117
- print_messages('WARN', linter.warnings)
118
- print_messages('NOTE', linter.notes)
119
- end
120
- UI.puts unless config.silent?
121
- end
122
- end
123
- UI.puts "Analyzed #{podspecs.count} podspecs files.\n\n" unless config.silent?
124
60
 
125
- if invalid_count == 0
126
- UI.puts "All the specs passed validation.".green << "\n\n" unless config.silent?
127
- else
128
- raise Informative, "#{invalid_count} podspecs failed validation."
129
- end
61
+ def run
62
+ SourcesManager.update(@name, true)
130
63
  end
131
64
  end
132
65
 
133
- def print_messages(type, messages)
134
- return if config.silent?
135
- messages.each {|msg| UI.puts " - #{type.ljust(5)} | #{msg}"}
136
- end
66
+ #-----------------------------------------------------------------------#
137
67
 
138
- def check_versions(dir)
139
- versions = versions(dir)
140
- unless is_compatilbe(versions)
141
- min, max = versions['min'], versions['max']
142
- version_msg = ( min == max ) ? min : "#{min} - #{max}"
143
- raise Informative,
144
- "\n[!] The `#{dir.basename.to_s}' repo requires CocoaPods #{version_msg}\n".red +
145
- "Update Cocoapods, or checkout the appropriate tag in the repo.\n\n"
146
- end
147
- UI.puts "\nCocoapods #{versions['last']} is available.\n".green if has_update(versions) && config.new_version_message?
148
- end
68
+ class Lint < Repo
69
+ self.summary = 'Validates all specs in a repo.'
149
70
 
150
- def self.compatible?(name)
151
- dir = Config.instance.repos_dir + name
152
- versions = versions(dir)
153
- is_compatilbe(versions)
154
- end
71
+ self.description = <<-DESC
72
+ Lints the spec-repo `NAME`. If a directory is provided it is assumed
73
+ to be the root of a repo. Finally, if `NAME` is not provided this
74
+ will lint all the spec-repos known to CocoaPods.
75
+ DESC
155
76
 
156
- #--------------------------------------#
77
+ self.arguments = '[ NAME | DIRECTORY ]'
157
78
 
158
- private
79
+ def self.options
80
+ [["--only-errors", "Lint presents only the errors"]].concat(super)
81
+ end
159
82
 
160
- def versions(dir)
161
- self.class.versions(dir)
162
- end
83
+ def initialize(argv)
84
+ @name = argv.shift_argument
85
+ @only_errors = argv.flag?('only-errors')
86
+ super
87
+ end
163
88
 
164
- def self.versions(dir)
165
- require 'yaml'
166
- yaml_file = dir + 'CocoaPods-version.yml'
167
- yaml_file.exist? ? YAML.load_file(yaml_file) : {}
168
- end
89
+ # @todo Part of this logic needs to be ported to cocoapods-core so web
90
+ # services can validate the repo.
91
+ #
92
+ # @todo add UI.print and enable print statements again.
93
+ #
94
+ def run
95
+ if @name
96
+ dirs = File.exists?(@name) ? [ Pathname.new(@name) ] : [ dir ]
97
+ else
98
+ dirs = config.repos_dir.children.select {|c| c.directory?}
99
+ end
100
+ dirs.each do |dir|
101
+ SourcesManager.check_version_information(dir)
102
+ UI.puts "\nLinting spec repo `#{dir.realpath.basename}`\n".yellow
103
+ podspecs = Pathname.glob( dir + '**/*.podspec')
104
+ invalid_count = 0
105
+
106
+ messages_by_type = {}
107
+ podspecs.each do |podspec|
108
+ # print "\033[K -> #{podspec.relative_path_from(dir)}\r" unless config.silent?
109
+ validator = Validator.new(podspec)
110
+ validator.quick = true
111
+ validator.repo_path = dir
112
+ validator.only_errors = @only_errors
113
+ validator.disable_ui_output = true
114
+
115
+ validator.validate
116
+ invalid_count += 1 if validator.result_type == :error
117
+ unless validator.validated?
118
+ if @only_errors
119
+ results = validator.results.select { |r| r.type.to_s == "error" }
120
+ else
121
+ results = validator.results
122
+ end
123
+ sorted_results = results.sort_by { |r| [r.type.to_s, r.message] }
124
+ sorted_results.each do |result|
125
+ name = validator.spec ? validator.spec.name : podspec.relative_path_from(dir)
126
+ version = validator.spec ? validator.spec.version : 'unknown'
127
+ messages_by_type[result.type] ||= {}
128
+ messages_by_type[result.type][result.message] ||= {}
129
+ messages_by_type[result.type][result.message][name] ||= []
130
+ messages_by_type[result.type][result.message][name] << version
131
+ end
132
+ end
133
+ end
169
134
 
170
- def is_compatilbe(versions)
171
- self.class.is_compatilbe(versions)
172
- end
135
+ # print "\033[K" unless config.silent?
136
+ messages_by_type.each do |type, names_by_message|
137
+ names_by_message.each do |message, versions_by_names|
138
+ color = type == :error ? :red : :yellow
139
+ UI.puts "[#{type}] #{message}".send(color)
140
+ versions_by_names.each { |name, versions| UI.puts " - #{name} (#{versions * ', '})" }
141
+ UI.puts
142
+ end
143
+ end
173
144
 
174
- def self.is_compatilbe(versions)
175
- min, max = versions['min'], versions['max']
176
- supports_min = !min || bin_version >= Gem::Version.new(min)
177
- supports_max = !max || bin_version <= Gem::Version.new(max)
178
- supports_min && supports_max
179
- end
145
+ UI.puts "Analyzed #{podspecs.count} podspecs files.\n\n"
180
146
 
181
- def has_update(versions)
182
- self.class.has_update(versions)
147
+ if invalid_count == 0
148
+ UI.puts "All the specs passed validation.".green << "\n\n"
149
+ else
150
+ raise Informative, "#{invalid_count} podspecs failed validation."
151
+ end
152
+ end
153
+ end
183
154
  end
184
155
 
185
- def self.has_update(versions)
186
- last = versions['last']
187
- last && Gem::Version.new(last) > bin_version
188
- end
156
+ #-----------------------------------------------------------------------#
189
157
 
190
- def self.bin_version
191
- Gem::Version.new(VERSION)
192
- end
158
+ extend Executable
159
+ executable :git
193
160
 
161
+ def dir
162
+ config.repos_dir + @name
163
+ end
194
164
  end
195
165
  end
196
166
  end