cocoapods 0.16.4 → 0.17.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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