cocoapods-core 0.30.0 → 1.15.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +7 -10
  3. data/lib/cocoapods-core/build_type.rb +121 -0
  4. data/lib/cocoapods-core/cdn_source.rb +501 -0
  5. data/lib/cocoapods-core/core_ui.rb +4 -3
  6. data/lib/cocoapods-core/dependency.rb +100 -73
  7. data/lib/cocoapods-core/gem_version.rb +1 -2
  8. data/lib/cocoapods-core/github.rb +32 -5
  9. data/lib/cocoapods-core/http.rb +86 -0
  10. data/lib/cocoapods-core/lockfile.rb +161 -56
  11. data/lib/cocoapods-core/metrics.rb +47 -0
  12. data/lib/cocoapods-core/platform.rb +99 -11
  13. data/lib/cocoapods-core/podfile/dsl.rb +623 -124
  14. data/lib/cocoapods-core/podfile/target_definition.rb +662 -109
  15. data/lib/cocoapods-core/podfile.rb +138 -65
  16. data/lib/cocoapods-core/requirement.rb +37 -8
  17. data/lib/cocoapods-core/source/acceptor.rb +16 -13
  18. data/lib/cocoapods-core/source/aggregate.rb +79 -103
  19. data/lib/cocoapods-core/source/health_reporter.rb +9 -18
  20. data/lib/cocoapods-core/source/manager.rb +488 -0
  21. data/lib/cocoapods-core/source/metadata.rb +79 -0
  22. data/lib/cocoapods-core/source.rb +241 -70
  23. data/lib/cocoapods-core/specification/consumer.rb +187 -47
  24. data/lib/cocoapods-core/specification/dsl/attribute.rb +49 -85
  25. data/lib/cocoapods-core/specification/dsl/attribute_support.rb +6 -8
  26. data/lib/cocoapods-core/specification/dsl/deprecations.rb +9 -126
  27. data/lib/cocoapods-core/specification/dsl/platform_proxy.rb +30 -20
  28. data/lib/cocoapods-core/specification/dsl.rb +943 -296
  29. data/lib/cocoapods-core/specification/json.rb +64 -23
  30. data/lib/cocoapods-core/specification/linter/analyzer.rb +218 -0
  31. data/lib/cocoapods-core/specification/linter/result.rb +128 -0
  32. data/lib/cocoapods-core/specification/linter.rb +310 -309
  33. data/lib/cocoapods-core/specification/root_attribute_accessors.rb +90 -39
  34. data/lib/cocoapods-core/specification/set/presenter.rb +35 -71
  35. data/lib/cocoapods-core/specification/set.rb +42 -96
  36. data/lib/cocoapods-core/specification.rb +368 -130
  37. data/lib/cocoapods-core/standard_error.rb +45 -24
  38. data/lib/cocoapods-core/trunk_source.rb +14 -0
  39. data/lib/cocoapods-core/vendor/requirement.rb +133 -53
  40. data/lib/cocoapods-core/vendor/version.rb +197 -156
  41. data/lib/cocoapods-core/vendor.rb +1 -5
  42. data/lib/cocoapods-core/version.rb +137 -42
  43. data/lib/cocoapods-core/yaml_helper.rb +334 -0
  44. data/lib/cocoapods-core.rb +10 -4
  45. metadata +100 -27
  46. data/lib/cocoapods-core/source/abstract_data_provider.rb +0 -71
  47. data/lib/cocoapods-core/source/file_system_data_provider.rb +0 -150
  48. data/lib/cocoapods-core/source/github_data_provider.rb +0 -143
  49. data/lib/cocoapods-core/specification/set/statistics.rb +0 -266
  50. data/lib/cocoapods-core/yaml_converter.rb +0 -192
@@ -1,12 +1,10 @@
1
1
  require 'cocoapods-core/source/acceptor'
2
2
  require 'cocoapods-core/source/aggregate'
3
3
  require 'cocoapods-core/source/health_reporter'
4
- require 'cocoapods-core/source/abstract_data_provider'
5
- require 'cocoapods-core/source/file_system_data_provider'
6
- require 'cocoapods-core/source/github_data_provider'
4
+ require 'cocoapods-core/source/manager'
5
+ require 'cocoapods-core/source/metadata'
7
6
 
8
7
  module Pod
9
-
10
8
  # The Source class is responsible to manage a collection of podspecs.
11
9
  #
12
10
  # The backing store of the podspecs collection is an implementation detail
@@ -18,32 +16,49 @@ module Pod
18
16
  # "#{SPEC_NAME}/#{VERSION}/#{SPEC_NAME}.podspec"
19
17
  #
20
18
  class Source
19
+ # The default branch in which the specs are stored
20
+ DEFAULT_SPECS_BRANCH = 'master'.freeze
21
21
 
22
- # @return [AbstractDataProvider] the data provider for this source.
22
+ # @return [Pod::Source::Metadata] The metadata for this source.
23
23
  #
24
- attr_accessor :data_provider
24
+ attr_reader :metadata
25
25
 
26
26
  # @param [Pathname, String] repo @see #repo.
27
27
  #
28
- def initialize(repo = nil)
29
- # TODO: Temporary solution to aide the transition
30
- if repo.is_a?(String) || repo.is_a?(Pathname)
31
- @data_provider = FileSystemDataProvider.new(repo)
32
- else
33
- @data_provider = repo
34
- end
28
+ def initialize(repo)
29
+ @repo = Pathname(repo).expand_path
30
+ @versions_by_name = {}
31
+ refresh_metadata
35
32
  end
36
33
 
37
34
  # @return [String] The name of the source.
38
35
  #
39
36
  def name
40
- data_provider.name
37
+ repo.basename.to_s
38
+ end
39
+
40
+ # @return [String] The URL of the source.
41
+ #
42
+ # @note In the past we had used `git ls-remote --get-url`, but this could
43
+ # lead to an issue when finding a source based on its URL when `git`
44
+ # is configured to rewrite URLs with the `url.<base>.insteadOf`
45
+ # option. See https://github.com/CocoaPods/CocoaPods/issues/2724.
46
+ #
47
+ def url
48
+ @url ||= begin
49
+ remote = repo_git(%w(config --get remote.origin.url))
50
+ if !remote.empty?
51
+ remote
52
+ elsif (repo + '.git').exist?
53
+ "file://#{repo}/.git"
54
+ end
55
+ end
41
56
  end
42
57
 
43
58
  # @return [String] The type of the source.
44
59
  #
45
60
  def type
46
- data_provider.type
61
+ git? ? 'git' : 'file system'
47
62
  end
48
63
 
49
64
  alias_method :to_s, :name
@@ -65,21 +80,79 @@ module Pod
65
80
  "#<#{self.class} name:#{name} type:#{type}>"
66
81
  end
67
82
 
83
+ # @!group Paths
84
+ #-------------------------------------------------------------------------#
85
+
86
+ # @return [Pathname] The path where the source is stored.
87
+ #
88
+ attr_reader :repo
89
+
90
+ # @return [Pathname] The directory where the specs are stored.
91
+ #
92
+ # @note In previous versions of CocoaPods they used to be stored in
93
+ # the root of the repo. This lead to issues, especially with
94
+ # the GitHub interface and now they are stored in a dedicated
95
+ # folder.
96
+ #
97
+ def specs_dir
98
+ @specs_dir ||= begin
99
+ specs_sub_dir = repo + 'Specs'
100
+ if specs_sub_dir.exist?
101
+ specs_sub_dir
102
+ elsif repo.exist?
103
+ repo
104
+ end
105
+ end
106
+ end
107
+
108
+ # @param [String] name The name of the pod.
109
+ #
110
+ # @return [Pathname] The path at which the specs for the given pod are
111
+ # stored.
112
+ #
113
+ def pod_path(name)
114
+ specs_dir.join(*metadata.path_fragment(name))
115
+ end
116
+
117
+ # @return [Pathname] The path at which source metadata is stored.
118
+ #
119
+ def metadata_path
120
+ repo + 'CocoaPods-version.yml'
121
+ end
122
+
68
123
  public
69
124
 
70
- # @!group Queering the source
125
+ # @!group Querying the source
71
126
  #-------------------------------------------------------------------------#
72
127
 
73
128
  # @return [Array<String>] the list of the name of all the Pods.
74
129
  #
75
130
  #
76
131
  def pods
77
- pods = data_provider.pods
78
- unless pods
79
- raise Informative, "Unable to find the #{data_provider.type} source " \
80
- "named: `#{data_provider.name}`"
132
+ unless specs_dir
133
+ raise Informative, "Unable to find a source named: `#{name}`"
134
+ end
135
+ glob = specs_dir.join('*/' * metadata.prefix_lengths.size, '*')
136
+ Pathname.glob(glob).reduce([]) do |pods, entry|
137
+ pods << entry.basename.to_s if entry.directory?
138
+ pods
139
+ end.sort
140
+ end
141
+
142
+ # Returns pod names for given array of specification paths.
143
+ #
144
+ # @param [Array<String>] spec_paths
145
+ # Array of file path names for specifications. Path strings should be relative to the source path.
146
+ #
147
+ # @return [Array<String>] the list of the name of Pods corresponding to specification paths.
148
+ #
149
+ def pods_for_specification_paths(spec_paths)
150
+ spec_paths.map do |path|
151
+ absolute_path = repo + path
152
+ relative_path = absolute_path.relative_path_from(specs_dir)
153
+ # The first file name returned by 'each_filename' is the pod name
154
+ relative_path.each_filename.first
81
155
  end
82
- pods
83
156
  end
84
157
 
85
158
  # @return [Array<Version>] all the available versions for the Pod, sorted
@@ -89,8 +162,22 @@ module Pod
89
162
  # the name of the Pod.
90
163
  #
91
164
  def versions(name)
92
- versions = data_provider.versions(name)
93
- versions.map { |version| Version.new(version) } if versions
165
+ return nil unless specs_dir
166
+ raise ArgumentError, 'No name' unless name
167
+ pod_dir = pod_path(name)
168
+ return unless pod_dir.exist?
169
+ @versions_by_name[name] ||= pod_dir.children.map do |v|
170
+ next nil unless v.directory?
171
+ basename = v.basename.to_s
172
+ next unless basename[0, 1] != '.'
173
+ begin
174
+ Version.new(basename)
175
+ rescue ArgumentError
176
+ raise Informative, 'An unexpected version directory ' \
177
+ "`#{basename}` was encountered for the " \
178
+ "`#{pod_dir}` Pod in the `#{name}` repository."
179
+ end
180
+ end.compact.sort.reverse
94
181
  end
95
182
 
96
183
  # @return [Specification] the specification for a given version of Pod.
@@ -112,31 +199,35 @@ module Pod
112
199
  # @return [Pathname] The path of the specification.
113
200
  #
114
201
  def specification_path(name, version)
115
- path = specs_dir + name + version.to_s
202
+ raise ArgumentError, 'No name' unless name
203
+ raise ArgumentError, 'No version' unless version
204
+ path = pod_path(name) + version.to_s
116
205
  specification_path = path + "#{name}.podspec.json"
117
206
  unless specification_path.exist?
118
207
  specification_path = path + "#{name}.podspec"
119
208
  end
120
209
  unless specification_path.exist?
121
210
  raise StandardError, "Unable to find the specification #{name} " \
122
- "(#{version}) in the #{name} source."
211
+ "(#{version}) in the #{self.name} source."
123
212
  end
124
- spec
213
+ specification_path
125
214
  end
126
215
 
127
216
  # @return [Array<Specification>] all the specifications contained by the
128
217
  # source.
129
218
  #
130
219
  def all_specs
131
- specs = pods.map do |name|
220
+ glob = specs_dir.join('*/' * metadata.prefix_lengths.size, '*', '*', '*.podspec{.json,}')
221
+ specs = Pathname.glob(glob).map do |path|
132
222
  begin
133
- versions(name).map { |version| specification(name, version) }
223
+ Specification.from_file(path)
134
224
  rescue
135
- CoreUI.warn "Skipping `#{name}` because the podspec contains errors."
225
+ CoreUI.warn "Skipping `#{path.relative_path_from(repo)}` because the " \
226
+ 'podspec contains errors.'
136
227
  next
137
228
  end
138
229
  end
139
- specs.flatten.compact
230
+ specs.compact
140
231
  end
141
232
 
142
233
  # Returns the set for the Pod with the given name.
@@ -156,22 +247,6 @@ module Pod
156
247
  pods.map { |pod_name| set(pod_name) }
157
248
  end
158
249
 
159
- # Returns the path of the specification with the given name and version.
160
- #
161
- # @param [String] name
162
- # the name of the Pod.
163
- #
164
- # @param [Version,String] version
165
- # the version for the specification.
166
- #
167
- # @return [Pathname] The path of the specification.
168
- #
169
- # @todo Remove.
170
- #
171
- def specification_path(name, version)
172
- data_provider.specification_path(name, version)
173
- end
174
-
175
250
  public
176
251
 
177
252
  # @!group Searching the source
@@ -180,16 +255,33 @@ module Pod
180
255
  # @return [Set] a set for a given dependency. The set is identified by the
181
256
  # name of the dependency and takes into account subspecs.
182
257
  #
258
+ # @note This method is optimized for fast lookups by name, i.e. it does
259
+ # *not* require iterating through {#pod_sets}
260
+ #
183
261
  # @todo Rename to #load_set
184
262
  #
185
263
  def search(query)
264
+ unless specs_dir
265
+ raise Informative, "Unable to find a source named: `#{name}`"
266
+ end
186
267
  if query.is_a?(Dependency)
187
- name = query.root_name
188
- else
189
- name = query
268
+ query = query.root_name
269
+ end
270
+
271
+ if (versions = @versions_by_name[query]) && !versions.empty?
272
+ set = set(query)
273
+ return set if set.specification_name == query
274
+ end
275
+
276
+ found = []
277
+ Pathname.glob(pod_path(query)) do |path|
278
+ next unless Dir.foreach(path).any? { |child| child != '.' && child != '..' }
279
+ found << path.basename.to_s
190
280
  end
191
- pod_sets.find do |set|
192
- set.name == name
281
+
282
+ if [query] == found
283
+ set = set(query)
284
+ set if set.specification_name == query
193
285
  end
194
286
  end
195
287
 
@@ -198,7 +290,7 @@ module Pod
198
290
  # @param [String] query
199
291
  # the search term. Can be a regular expression.
200
292
  #
201
- # @param [Bool] full_text_search
293
+ # @param [Boolean] full_text_search
202
294
  # whether the search should be limited to the name of the Pod or
203
295
  # should include also the author, the summary, and the description.
204
296
  #
@@ -210,23 +302,19 @@ module Pod
210
302
  def search_by_name(query, full_text_search = false)
211
303
  regexp_query = /#{query}/i
212
304
  if full_text_search
213
- if data_provider.is_a?(FileSystemDataProvider)
214
- pod_sets.reject do |set|
215
- texts = []
216
- begin
217
- s = set.specification
218
- texts << s.name
219
- texts += s.authors.keys
220
- texts << s.summary
221
- texts << s.description
222
- rescue
223
- CoreUI.warn "Skipping `#{set.name}` because the podspec " \
224
- "contains errors."
225
- end
226
- texts.grep(regexp_query).empty?
305
+ pod_sets.reject do |set|
306
+ texts = []
307
+ begin
308
+ s = set.specification
309
+ texts << s.name
310
+ texts += s.authors.keys
311
+ texts << s.summary
312
+ texts << s.description
313
+ rescue
314
+ CoreUI.warn "Skipping `#{set.name}` because the podspec " \
315
+ 'contains errors.'
227
316
  end
228
- else
229
- []
317
+ texts.grep(regexp_query).empty?
230
318
  end
231
319
  else
232
320
  names = pods.grep(regexp_query)
@@ -250,6 +338,56 @@ module Pod
250
338
  end
251
339
  end
252
340
 
341
+ # @!group Updating the source
342
+ #-------------------------------------------------------------------------#
343
+
344
+ # Updates the local clone of the source repo.
345
+ #
346
+ # @param [Boolean] show_output
347
+ #
348
+ # @return [Array<String>] changed_spec_paths
349
+ # Returns the list of changed spec paths.
350
+ #
351
+ def update(show_output)
352
+ return [] if unchanged_github_repo?
353
+ prev_commit_hash = git_commit_hash
354
+ update_git_repo(show_output)
355
+ @versions_by_name.clear
356
+ refresh_metadata
357
+ if version = metadata.last_compatible_version(Version.new(CORE_VERSION))
358
+ tag = "v#{version}"
359
+ CoreUI.warn "Using the `#{tag}` tag of the `#{name}` source because " \
360
+ "it is the last version compatible with CocoaPods #{CORE_VERSION}."
361
+ repo_git(['checkout', tag])
362
+ end
363
+ diff_until_commit_hash(prev_commit_hash)
364
+ end
365
+
366
+ def updateable?
367
+ git?
368
+ end
369
+
370
+ def git?
371
+ repo.join('.git').exist? && !repo_git(%w(rev-parse HEAD)).empty?
372
+ end
373
+
374
+ def indexable?
375
+ true
376
+ end
377
+
378
+ def verify_compatibility!
379
+ return if metadata.compatible?(CORE_VERSION)
380
+
381
+ version_msg = if metadata.minimum_cocoapods_version == metadata.maximum_cocoapods_version
382
+ metadata.minimum_cocoapods_version
383
+ else
384
+ "#{metadata.minimum_cocoapods_version} - #{metadata.maximum_cocoapods_version}"
385
+ end
386
+ raise Informative, "The `#{name}` repo requires " \
387
+ "CocoaPods #{version_msg} (currently using #{CORE_VERSION})\n" \
388
+ 'Update CocoaPods, or checkout the appropriate tag in the repo.'
389
+ end
390
+
253
391
  public
254
392
 
255
393
  # @!group Representations
@@ -295,11 +433,44 @@ module Pod
295
433
  specification(name, version) if version
296
434
  rescue Informative
297
435
  Pod::CoreUI.warn "Skipping `#{name}` because the podspec " \
298
- "contains errors."
436
+ 'contains errors.'
299
437
  nil
300
438
  end
301
439
 
302
- #-------------------------------------------------------------------------#
440
+ def refresh_metadata
441
+ @metadata = Metadata.from_file(metadata_path)
442
+ end
303
443
 
444
+ def git_commit_hash
445
+ repo_git(%w(rev-parse HEAD))
446
+ end
447
+
448
+ def update_git_repo(show_output = false)
449
+ repo_git(['checkout', git_tracking_branch])
450
+ output = repo_git(%w(pull --ff-only), :include_error => true)
451
+ CoreUI.puts output if show_output
452
+ end
453
+
454
+ def git_tracking_branch
455
+ path = repo.join('.git', 'cocoapods_branch')
456
+ path.file? ? path.read.strip : DEFAULT_SPECS_BRANCH
457
+ end
458
+
459
+ def diff_until_commit_hash(commit_hash)
460
+ repo_git(%W(diff --name-only #{commit_hash}..HEAD)).split("\n")
461
+ end
462
+
463
+ def repo_git(args, include_error: false)
464
+ command = "env -u GIT_CONFIG git -C \"#{repo}\" " << args.join(' ')
465
+ command << ' 2>&1' if include_error
466
+ (`#{command}` || '').strip
467
+ end
468
+
469
+ def unchanged_github_repo?
470
+ return unless url =~ /github.com/
471
+ !GitHub.modified_since_commit(url, git_commit_hash)
472
+ end
473
+
474
+ #-------------------------------------------------------------------------#
304
475
  end
305
476
  end