cocoapods-core 1.0.0.beta.6 → 1.0.0.beta.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b116f2a8b2c3c090b6aada3e046db9d3515e538a
4
- data.tar.gz: e4f97aed43c5b3ddc85381673e863a414796def1
3
+ metadata.gz: 4c385f0112647c0300a73d55e8c6d160f98fec9d
4
+ data.tar.gz: 1d87fa015bcf769c785e181c394aa1fdc7584ead
5
5
  SHA512:
6
- metadata.gz: b06b2f5053a53c500861559f776f65864128c2144b1e5a7bff34d40c78811fa952b70ee112e3a2f18475962049a1e3258da1b0d32c78aa0f62ab961d4a24fe3c
7
- data.tar.gz: 123a0a4674f478085c4abc1ba8d63b155c20b3fbc16d93b399bac5ebd0ff2d6914bb49baf8c730093a086881c61868f0829010737a5226c7a5a61ee5afbf5344
6
+ metadata.gz: 51e7cb1f3134fdd9d08f15427489033a43b76616429d7158d2534ca6fe461fde55da4c459ef98030d9f1e4bd53259b6d64c171edebc4ba4428725f1073259e6d
7
+ data.tar.gz: 7c0aaacccb38fbd1f89fdb950731eb72c58ecf3462258468a9b6703746fe2d0fb9d1ad5e98b652a317ab1bae6643c114f19748c097da1342052c8b56fa81512f
@@ -6,6 +6,10 @@ module Pod
6
6
  STDOUT.puts message
7
7
  end
8
8
 
9
+ def self.print(message)
10
+ STDOUT.print(message)
11
+ end
12
+
9
13
  def self.warn(message)
10
14
  STDERR.puts message
11
15
  end
@@ -1,5 +1,5 @@
1
1
  module Pod
2
2
  # The version of the cocoapods-core.
3
3
  #
4
- CORE_VERSION = '1.0.0.beta.6'.freeze unless defined? Pod::CORE_VERSION
4
+ CORE_VERSION = '1.0.0.beta.7'.freeze unless defined? Pod::CORE_VERSION
5
5
  end
@@ -123,21 +123,22 @@ module Pod
123
123
  # your Podfile to use future versions of CocoaPods. Anyway a clear and
124
124
  # simple upgrade path will be provided.
125
125
  #
126
- # By default dependencies are installed on all the build configurations
126
+ # By default dependencies are installed in all the build configurations
127
127
  # of the target. For debug purposes or for other reasons, they can be
128
- # enabled only on a given list of build configuration names.
128
+ # only enabled on a list of build configurations.
129
129
  #
130
- # pod 'PonyDebugger', :configurations => ['Release', 'App Store']
130
+ # pod 'PonyDebugger', :configurations => ['Debug', 'Beta']
131
131
  #
132
- # Alternatively you can white-list only a single build configuration.
132
+ # Alternatively, you specify to have it included on a single build
133
+ # configuration.
133
134
  #
134
- # pod 'PonyDebugger', :configuration => ['Release']
135
+ # pod 'PonyDebugger', :configuration => 'Debug'
135
136
  #
136
137
  # ------
137
138
  #
138
139
  # ### Subspecs
139
140
  #
140
- # When installing a Pod via it's name, it will install all of the
141
+ # When installing a Pod via its name, it will install all of the
141
142
  # default subspecs defined in the podspec.
142
143
  #
143
144
  # You may install a specific subspec using the following:
@@ -295,12 +296,12 @@ module Pod
295
296
  # target 'ShowsApp' do
296
297
  # pod 'ShowsKit'
297
298
  #
298
- # # Has it's own copy of ShowsKit + ShowTVAuth
299
+ # # Has its own copy of ShowsKit + ShowTVAuth
299
300
  # target 'ShowsTV' do
300
301
  # pod 'ShowTVAuth'
301
302
  # end
302
303
  #
303
- # # Has it's own copy of Specta + Expecta
304
+ # # Has its own copy of Specta + Expecta
304
305
  # # and has access to ShowsKit via the app
305
306
  # # that the test target is bundled into
306
307
  #
@@ -344,23 +345,24 @@ module Pod
344
345
  #
345
346
  # @example Defining an abstract_target wrapping Pods to multiple targets
346
347
  #
347
- # # There are no targets called "Shows" in any Xcode projects
348
+ # # Note: There are no targets called "Shows" in any of this workspace's Xcode projects
348
349
  # abstract_target 'Shows' do
349
350
  # pod 'ShowsKit'
350
351
  #
351
- # # Has it's own copy of ShowsKit + ShowWebAuth
352
+ # # The target ShowsiOS has its own copy of ShowsKit (inherited) + ShowWebAuth (added here)
352
353
  # target 'ShowsiOS' do
353
354
  # pod 'ShowWebAuth'
354
355
  # end
355
356
  #
356
- # # Has it's own copy of ShowsKit + ShowTVAuth
357
+ # # The target ShowsTV has its own copy of ShowsKit (inherited) + ShowTVAuth (added here)
357
358
  # target 'ShowsTV' do
358
359
  # pod 'ShowTVAuth'
359
360
  # end
360
361
  #
361
- # # Has it's own copy of Specta + Expecta
362
- # # and has access to ShowsKit via the app
363
- # # that the test target is bundled into
362
+ # # Our tests target has its own copy of
363
+ # # our testing frameworks, and has access
364
+ # # to ShowsKit as well because it is
365
+ # # a child of the abstract target 'Shows'
364
366
  #
365
367
  # target 'ShowsTests' do
366
368
  # inherit! :search_paths
@@ -10,6 +10,7 @@ module Pod
10
10
  # @param [Array<Source>] repos_dirs @see Sources
11
11
  #
12
12
  def initialize(sources)
13
+ raise "Cannot initialize an aggregate with a nil source: (#{sources})" if sources.include?(nil)
13
14
  @sources = sources
14
15
  end
15
16
 
@@ -164,7 +165,7 @@ module Pod
164
165
  result = {}
165
166
  sets.each do |set|
166
167
  word_list_from_set(set).each do |w|
167
- (result[w] ||= []).push(set.name.to_sym)
168
+ (result[w] ||= []).push(set.name)
168
169
  end
169
170
  end
170
171
  result
@@ -0,0 +1,412 @@
1
+ module Pod
2
+ class Source
3
+ class Manager
4
+ # @return [Pathname] The directory that contains the source repo
5
+ # directories.
6
+ #
7
+ attr_reader :repos_dir
8
+
9
+ def initialize(repos_dir)
10
+ @repos_dir = Pathname(repos_dir).expand_path
11
+ end
12
+
13
+ # @return [Array<Pathname>] The source repo directories.
14
+ #
15
+ def source_repos
16
+ return [] unless repos_dir.exist?
17
+ repos_dir.children.select(&:directory?).sort_by { |d| d.basename.to_s.downcase }
18
+ end
19
+
20
+ # @return [Source::Aggregate] The aggregate of all the sources with the
21
+ # known Pods.
22
+ #
23
+ def aggregate
24
+ aggregate_with_repos(source_repos)
25
+ end
26
+
27
+ # @return [Source::Aggregate] The aggregate of the sources from repos.
28
+ #
29
+ # @param [Dependency] dependency
30
+ # The dependency for which to find or build the appropriate.
31
+ # aggregate. If the dependency specifies a source podspec repo
32
+ # then only that source will be used, otherwise all sources
33
+ # will be used.
34
+ #
35
+ def aggregate_for_dependency(dependency)
36
+ if dependency.podspec_repo
37
+ source = source_with_url(dependency.podspec_repo)
38
+ raise StandardError, '[Bug] Failed to find known source with the URL ' \
39
+ "#{dependency.podspec_repo.inspect}" if source.nil?
40
+
41
+ aggregate_with_repos([source.repo])
42
+ else
43
+ aggregate
44
+ end
45
+ end
46
+
47
+ # @return [Array<Source>] The list of the sources with the given names.
48
+ #
49
+ # @param [Array<#to_s>] names
50
+ # The names of the sources.
51
+ #
52
+ def sources(names)
53
+ dirs = names.map { |name| source_dir(name) }
54
+ dirs.map { |repo| source_from_path(repo) }
55
+ end
56
+
57
+ # @return [Array<Source>] The list of all the sources known to this
58
+ # installation of CocoaPods.
59
+ #
60
+ def all
61
+ aggregate.sources
62
+ end
63
+
64
+ # @return [Array<Source>] The CocoaPods Master Repo source.
65
+ #
66
+ def master
67
+ sources(['master']).select { |s| s.repo.directory? }
68
+ end
69
+
70
+ # @!group Master repo
71
+
72
+ # @return [Pathname] The path of the master repo.
73
+ #
74
+ def master_repo_dir
75
+ source_dir('master')
76
+ end
77
+
78
+ # @return [Bool] Checks if the master repo is usable.
79
+ #
80
+ # @note Note this is used to automatically setup the master repo if
81
+ # needed.
82
+ #
83
+ def master_repo_functional?
84
+ return false unless master_repo = master.first
85
+ master_repo.metadata.compatible?(CORE_VERSION)
86
+ end
87
+
88
+ # Search the appropriate sources to match the set for the given dependency.
89
+ #
90
+ # @return [Set, nil] a set for a given dependency including all the
91
+ # {Source} that contain the Pod. If no sources containing the
92
+ # Pod where found it returns nil.
93
+ #
94
+ # @raise If no source can be found that includes the dependency.
95
+ #
96
+ def search(dependency)
97
+ aggregate_for_dependency(dependency).search(dependency)
98
+ end
99
+
100
+ # Search all the sources with the given search term.
101
+ #
102
+ # @param [String] query
103
+ # The search term.
104
+ #
105
+ # @param [Bool] full_text_search
106
+ # Whether the search should be limited to the name of the Pod or
107
+ # should include also the author, the summary, and the
108
+ # description.
109
+ #
110
+ # @raise If no source including the set can be found.
111
+ #
112
+ # @return [Array<Set>] The sets that contain the search term.
113
+ #
114
+ def search_by_name(query, full_text_search = false)
115
+ query_word_regexps = query.split.map { |word| /#{word}/i }
116
+ if full_text_search
117
+ query_word_results_hash = {}
118
+ updated_search_index.each_value do |word_spec_hash|
119
+ word_spec_hash.each_pair do |word, spec_names|
120
+ query_word_regexps.each do |query_word_regexp|
121
+ set = (query_word_results_hash[query_word_regexp] ||= Set.new)
122
+ set.merge(spec_names) if word =~ query_word_regexp
123
+ end
124
+ end
125
+ end
126
+ found_set_names = query_word_results_hash.values.reduce(:&)
127
+ found_set_names ||= []
128
+ sets = found_set_names.map do |name|
129
+ aggregate.representative_set(name)
130
+ end
131
+ # Remove nil values because representative_set return nil if no pod is found in any of the sources.
132
+ sets.compact!
133
+ else
134
+ sets = aggregate.search_by_name(query, false)
135
+ end
136
+ if sets.empty?
137
+ extra = ', author, summary, or description' if full_text_search
138
+ raise Informative, "Unable to find a pod with name#{extra}" \
139
+ "matching `#{query}`"
140
+ end
141
+ sorted_sets(sets, query_word_regexps)
142
+ end
143
+
144
+ # Returns given set array by sorting it in-place.
145
+ #
146
+ # @param [Array<Set>] sets
147
+ # Array of sets to be sorted.
148
+ #
149
+ # @param [Array<Regexp>] query_word_regexps
150
+ # Array of regexp objects for user query.
151
+ #
152
+ # @return [Array<Set>] Given sets parameter itself after sorting it in-place.
153
+ #
154
+ def sorted_sets(sets, query_word_regexps)
155
+ sets.sort_by! do |set|
156
+ pre_match_length = nil
157
+ found_query_index = nil
158
+ found_query_count = 0
159
+ query_word_regexps.each_with_index do |q, idx|
160
+ if (m = set.name.match(/#{q}/i))
161
+ pre_match_length ||= m.pre_match.length
162
+ found_query_index ||= idx
163
+ found_query_count += 1
164
+ end
165
+ end
166
+ pre_match_length ||= 1000
167
+ found_query_index ||= 1000
168
+ [-found_query_count, pre_match_length, found_query_index, set.name.downcase]
169
+ end
170
+ sets
171
+ end
172
+
173
+ # Returns the search data. If a saved search data exists, retrieves it from file and returns it.
174
+ # Else, creates the search data from scratch, saves it to file system, and returns it.
175
+ # Search data is grouped by source repos. For each source, it contains a hash where keys are words
176
+ # and values are the pod names containing corresponding word.
177
+ #
178
+ # For each source, list of unique words are generated from the following spec information.
179
+ # - version
180
+ # - summary
181
+ # - description
182
+ # - authors
183
+ #
184
+ # @return [Hash{String => Hash{String => Array<String>}}] The up to date search data.
185
+ #
186
+ def updated_search_index
187
+ index = stored_search_index || {}
188
+ all.each do |source|
189
+ source_name = source.name
190
+ unless index[source_name]
191
+ CoreUI.print "Creating search index for spec repo '#{source_name}'.."
192
+ index[source_name] = aggregate.generate_search_index_for_source(source)
193
+ CoreUI.puts ' Done!'
194
+ end
195
+ end
196
+ save_search_index(index)
197
+ index
198
+ end
199
+
200
+ # Updates the stored search index if there are changes in spec repos while updating them.
201
+ # Update is performed incrementally. Only the changed pods' search data is re-generated and updated.
202
+ # @param [Hash{Source => Array<String>}] changed_spec_paths
203
+ # A hash containing changed specification paths for each source.
204
+ #
205
+ def update_search_index_if_needed(changed_spec_paths)
206
+ search_index = stored_search_index
207
+ return unless search_index
208
+ changed_spec_paths.each_pair do |source, spec_paths|
209
+ index_for_source = search_index[source.name]
210
+ next unless index_for_source && !spec_paths.empty?
211
+ updated_pods = source.pods_for_specification_paths(spec_paths)
212
+
213
+ new_index = aggregate.generate_search_index_for_changes_in_source(source, spec_paths)
214
+ # First traverse search_index and update existing words
215
+ # Remove traversed words from new_index after adding to search_index,
216
+ # so that only non existing words will remain in new_index after enumeration completes.
217
+ index_for_source.each_pair do |word, _|
218
+ if new_index[word]
219
+ index_for_source[word] |= new_index[word]
220
+ new_index.delete(word)
221
+ else
222
+ index_for_source[word] -= updated_pods
223
+ end
224
+ end
225
+
226
+ # Now add non existing words remained in new_index to search_index
227
+ index_for_source.merge!(new_index)
228
+ end
229
+ save_search_index(search_index)
230
+ end
231
+
232
+ # Updates search index for changed pods in background
233
+ # @param [Hash{Source => Array<String>}] changed_spec_paths
234
+ # A hash containing changed specification paths for each source.
235
+ #
236
+ def update_search_index_if_needed_in_background(changed_spec_paths)
237
+ Process.fork do
238
+ Process.daemon
239
+ update_search_index_if_needed(changed_spec_paths)
240
+ exit
241
+ end
242
+ end
243
+
244
+ # Returns the search data stored in the file system.
245
+ # If existing data in the file system is not valid, returns nil.
246
+ #
247
+ def stored_search_index
248
+ @updated_search_index ||= begin
249
+ if search_index_path.exist?
250
+ require 'json'
251
+ index = JSON.parse(search_index_path.read)
252
+ index if index.is_a?(Hash) # TODO: should we also check if hash has correct hierarchy?
253
+ end
254
+ end
255
+ end
256
+
257
+ # Stores given search data in the file system.
258
+ # @param [Hash] index
259
+ # Index to be saved in file system
260
+ #
261
+ def save_search_index(index)
262
+ require 'json'
263
+ @updated_search_index = index
264
+ search_index_path.open('w') do |io|
265
+ io.write(@updated_search_index.to_json)
266
+ end
267
+ end
268
+
269
+ # Allows to clear the search index.
270
+ #
271
+ attr_writer :updated_search_index
272
+
273
+ # @return [Pathname] The path where the search index should be stored.
274
+ #
275
+ attr_accessor :search_index_path
276
+
277
+ private
278
+
279
+ # @return [Source] The Source at a given path.
280
+ #
281
+ # @param [Pathname] path
282
+ # The local file path to one podspec repo.
283
+ #
284
+ def source_from_path(path)
285
+ @sources_by_path ||= Hash.new do |hash, key|
286
+ hash[key] = if key.basename.to_s == 'master'
287
+ MasterSource.new(key)
288
+ else
289
+ Source.new(key)
290
+ end
291
+ end
292
+ @sources_by_path[path]
293
+ end
294
+
295
+ # @return [Source::Aggregate] The aggregate of the sources from repos.
296
+ #
297
+ # @param [Array<Pathname>] repos
298
+ # The local file paths to one or more podspec repo caches.
299
+ #
300
+ def aggregate_with_repos(repos)
301
+ sources = repos.map { |path| source_from_path(path) }
302
+ @aggregates_by_repos ||= {}
303
+ @aggregates_by_repos[repos] ||= Source::Aggregate.new(sources)
304
+ end
305
+
306
+ # @return [Source] The git source with the given name. If no git source
307
+ # with given name is found it raises.
308
+ #
309
+ # @param [String] name
310
+ # The name of the source.
311
+ #
312
+ def git_source_named(name)
313
+ specified_source = sources([name]).first
314
+ unless specified_source
315
+ raise Informative, "Unable to find the `#{name}` repo."
316
+ end
317
+ unless specified_source.git?
318
+ raise Informative, "The `#{name}` repo is not a git repo."
319
+ end
320
+ specified_source
321
+ end
322
+
323
+ # @return [Source] The list of the git sources.
324
+ #
325
+ def git_sources
326
+ all.select(&:git?)
327
+ end
328
+
329
+ # @return [Pathname] The path of the source with the given name.
330
+ #
331
+ # @param [String] name
332
+ # The name of the source.
333
+ #
334
+ def source_dir(name)
335
+ repos_dir + name
336
+ end
337
+
338
+ # @return [Source] The source whose {Source#url} is equal to `url`.
339
+ #
340
+ # @param [String] url
341
+ # The URL of the source.
342
+ #
343
+ def source_with_url(url)
344
+ url = url.downcase.gsub(/.git$/, '')
345
+ url = 'https://github.com/cocoapods/specs' if url =~ %r{github.com[:/]+cocoapods/specs}
346
+ all.find do |source|
347
+ source.url && source.url.downcase.gsub(/.git$/, '') == url
348
+ end
349
+ end
350
+
351
+ # Returns a suitable repository name for `url`.
352
+ #
353
+ # @example A GitHub.com URL
354
+ #
355
+ # name_for_url('https://github.com/Artsy/Specs.git')
356
+ # # "artsy"
357
+ # name_for_url('https://github.com/Artsy/Specs.git')
358
+ # # "artsy-1"
359
+ #
360
+ # @example A non-Github.com URL
361
+ #
362
+ # name_for_url('https://sourceforge.org/Artsy/Specs.git')
363
+ # # sourceforge-artsy-specs
364
+ #
365
+ # @example A file URL
366
+ #
367
+ # name_for_url('file:///Artsy/Specs.git')
368
+ # # artsy-specs
369
+ #
370
+ # @param [#to_s] url
371
+ # The URL of the source.
372
+ #
373
+ # @return [String] A suitable repository name for `url`.
374
+ #
375
+ def name_for_url(url)
376
+ base_from_host_and_path = lambda do |host, path|
377
+ if host
378
+ base = host.split('.')[-2] || host
379
+ base += '-'
380
+ else
381
+ base = ''
382
+ end
383
+
384
+ base + path.gsub(/.git$/, '').gsub(%r{^/}, '').split('/').join('-')
385
+ end
386
+
387
+ case url.to_s.downcase
388
+ when %r{github.com[:/]+cocoapods/specs}
389
+ base = 'master'
390
+ when %r{github.com[:/]+(.+)/(.+)}
391
+ base = Regexp.last_match[1]
392
+ when %r{^\S+@(\S+)[:/]+(.+)$}
393
+ host, path = Regexp.last_match.captures
394
+ base = base_from_host_and_path[host, path]
395
+ when URI.regexp
396
+ url = URI(url.downcase)
397
+ base = base_from_host_and_path[url.host, url.path]
398
+ else
399
+ base = url.to_s.downcase
400
+ end
401
+
402
+ name = base
403
+ infinity = 1.0 / 0
404
+ (1..infinity).each do |i|
405
+ break unless source_dir(name).exist?
406
+ name = "#{base}-#{i}"
407
+ end
408
+ name
409
+ end
410
+ end
411
+ end
412
+ end
@@ -0,0 +1,79 @@
1
+ autoload :Digest, 'digest/md5'
2
+ require 'active_support/hash_with_indifferent_access'
3
+ require 'active_support/core_ext/hash/indifferent_access'
4
+
5
+ module Pod
6
+ class Source
7
+ class Metadata
8
+ attr_reader :minimum_cocoapods_version
9
+ attr_reader :maximum_cocoapods_version
10
+ attr_reader :latest_cocoapods_version
11
+ attr_reader :prefix_lengths
12
+ attr_reader :last_compatible_versions
13
+
14
+ def initialize(hash = {})
15
+ hash = hash.with_indifferent_access
16
+ @minimum_cocoapods_version = hash['min']
17
+ @minimum_cocoapods_version &&= Pod::Version.new(@minimum_cocoapods_version)
18
+ @maximum_cocoapods_version = hash['max']
19
+ @maximum_cocoapods_version &&= Pod::Version.new(@maximum_cocoapods_version)
20
+ @latest_cocoapods_version = hash['last']
21
+ @latest_cocoapods_version &&= Pod::Version.new(@latest_cocoapods_version)
22
+ @prefix_lengths = Array(hash['prefix_lengths']).map!(&:to_i)
23
+ @last_compatible_versions = Array(hash['last_compatible_versions']).map(&Pod::Version.method(:new)).sort
24
+ end
25
+
26
+ def self.from_file(file)
27
+ hash = file.file? ? YAMLHelper.load_file(file) : {}
28
+ new(hash)
29
+ end
30
+
31
+ def to_hash
32
+ hash = ActiveSupport::HashWithIndifferentAccess.new
33
+ hash['min'] = @minimum_cocoapods_version.to_s if @minimum_cocoapods_version
34
+ hash['max'] = @maximum_cocoapods_version.to_s if @maximum_cocoapods_version
35
+ hash['last'] = @latest_cocoapods_version.to_s if @latest_cocoapods_version
36
+ hash['prefix_lengths'] = @prefix_lengths unless @prefix_lengths.empty?
37
+ hash['last_compatible_versions'] = @last_compatible_versions.map(&:to_s) unless @last_compatible_versions.empty?
38
+ hash
39
+ end
40
+
41
+ def path_fragment(pod_name, version = nil)
42
+ prefixes = if prefix_lengths.empty?
43
+ []
44
+ else
45
+ hashed = Digest::MD5.hexdigest(pod_name)
46
+ prefix_lengths.map do |length|
47
+ hashed.slice!(0, length)
48
+ end
49
+ end
50
+ prefixes.concat([pod_name, version]).compact.join(File::SEPARATOR)
51
+ end
52
+
53
+ def last_compatible_version(target_version)
54
+ return unless minimum_cocoapods_version
55
+ return if minimum_cocoapods_version <= target_version
56
+ @last_compatible_versions.reverse_each.bsearch { |v| v <= target_version }.tap do |version|
57
+ raise Informative, 'Unable to find compatible version' unless version
58
+ end
59
+ end
60
+
61
+ # Returns whether a source is compatible with the current version of
62
+ # CocoaPods.
63
+ #
64
+ # @param [Pathname] dir
65
+ # The directory where the source is stored.
66
+ #
67
+ # @return [Bool] whether the source is compatible.
68
+ #
69
+ def compatible?(version)
70
+ bin_version = Gem::Version.new(version)
71
+ supports_min = !minimum_cocoapods_version ||
72
+ (bin_version >= Gem::Version.new(minimum_cocoapods_version))
73
+ supports_max = !maximum_cocoapods_version ||
74
+ (bin_version <= Gem::Version.new(maximum_cocoapods_version))
75
+ supports_min && supports_max
76
+ end
77
+ end
78
+ end
79
+ end
@@ -1,6 +1,8 @@
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/manager'
5
+ require 'cocoapods-core/source/metadata'
4
6
 
5
7
  module Pod
6
8
  # The Source class is responsible to manage a collection of podspecs.
@@ -14,14 +16,15 @@ module Pod
14
16
  # "#{SPEC_NAME}/#{VERSION}/#{SPEC_NAME}.podspec"
15
17
  #
16
18
  class Source
17
- # @return [Pathname] The path where the source is stored.
19
+ # @return [Pod::Source::Metadata] The metadata for this source.
18
20
  #
19
- attr_reader :repo
21
+ attr_reader :metadata
20
22
 
21
23
  # @param [Pathname, String] repo @see #repo.
22
24
  #
23
25
  def initialize(repo)
24
26
  @repo = Pathname(repo).expand_path
27
+ refresh_metadata
25
28
  end
26
29
 
27
30
  # @return [String] The name of the source.
@@ -74,9 +77,49 @@ module Pod
74
77
  "#<#{self.class} name:#{name} type:#{type}>"
75
78
  end
76
79
 
80
+ # @!group Paths
81
+ #-------------------------------------------------------------------------#
82
+
83
+ # @return [Pathname] The path where the source is stored.
84
+ #
85
+ attr_reader :repo
86
+
87
+ # @return [Pathname] The directory where the specs are stored.
88
+ #
89
+ # @note In previous versions of CocoaPods they used to be stored in
90
+ # the root of the repo. This lead to issues, especially with
91
+ # the GitHub interface and now they are stored in a dedicated
92
+ # folder.
93
+ #
94
+ def specs_dir
95
+ @specs_dir ||= begin
96
+ specs_sub_dir = repo + 'Specs'
97
+ if specs_sub_dir.exist?
98
+ specs_sub_dir
99
+ elsif repo.exist?
100
+ repo
101
+ end
102
+ end
103
+ end
104
+
105
+ # @param [String] name The name of the pod.
106
+ #
107
+ # @return [Pathname] The path at which the specs for the given pod are
108
+ # stored.
109
+ #
110
+ def pod_path(name)
111
+ specs_dir.join(*metadata.path_fragment(name))
112
+ end
113
+
114
+ # @return [Pathname] The path at which source metadata is stored.
115
+ #
116
+ def metadata_path
117
+ repo + 'CocoaPods-version.yml'
118
+ end
119
+
77
120
  public
78
121
 
79
- # @!group Queering the source
122
+ # @!group Querying the source
80
123
  #-------------------------------------------------------------------------#
81
124
 
82
125
  # @return [Array<String>] the list of the name of all the Pods.
@@ -86,10 +129,10 @@ module Pod
86
129
  unless specs_dir
87
130
  raise Informative, "Unable to find a source named: `#{name}`"
88
131
  end
89
- specs_dir_as_string = specs_dir.to_s
90
- Dir.entries(specs_dir).select do |entry|
91
- valid_name = entry[0, 1] != '.'
92
- valid_name && File.directory?(File.join(specs_dir_as_string, entry))
132
+ glob = specs_dir.join('*/' * metadata.prefix_lengths.size, '*')
133
+ Pathname.glob(glob).reduce([]) do |pods, entry|
134
+ pods << entry.basename.to_s if entry.directory?
135
+ pods
93
136
  end.sort
94
137
  end
95
138
 
@@ -118,7 +161,7 @@ module Pod
118
161
  def versions(name)
119
162
  return nil unless specs_dir
120
163
  raise ArgumentError, 'No name' unless name
121
- pod_dir = specs_dir + name
164
+ pod_dir = pod_path(name)
122
165
  return unless pod_dir.exist?
123
166
  pod_dir.children.map do |v|
124
167
  basename = v.basename.to_s
@@ -153,7 +196,7 @@ module Pod
153
196
  def specification_path(name, version)
154
197
  raise ArgumentError, 'No name' unless name
155
198
  raise ArgumentError, 'No version' unless version
156
- path = specs_dir + name + version.to_s
199
+ path = pod_path(name) + version.to_s
157
200
  specification_path = path + "#{name}.podspec.json"
158
201
  unless specification_path.exist?
159
202
  specification_path = path + "#{name}.podspec"
@@ -169,15 +212,17 @@ module Pod
169
212
  # source.
170
213
  #
171
214
  def all_specs
172
- specs = pods.map do |name|
215
+ glob = specs_dir.join('*/' * metadata.prefix_lengths.size, '*', '*', '*.podspec{.json,}')
216
+ specs = Pathname.glob(glob).map do |path|
173
217
  begin
174
- versions(name).map { |version| specification(name, version) }
218
+ Specification.from_file(path)
175
219
  rescue
176
- CoreUI.warn "Skipping `#{name}` because the podspec contains errors."
220
+ CoreUI.warn "Skipping `#{path.relative_path_from(repo)}` because the " \
221
+ 'podspec contains errors.'
177
222
  next
178
223
  end
179
224
  end
180
- specs.flatten.compact
225
+ specs.compact
181
226
  end
182
227
 
183
228
  # Returns the set for the Pod with the given name.
@@ -217,8 +262,10 @@ module Pod
217
262
  if query.is_a?(Dependency)
218
263
  query = query.root_name
219
264
  end
220
- if specs_dir.children.select(&:directory?).map(&:basename).map(&:to_s).include?(query.to_s)
221
- set(query)
265
+ found = Pathname.glob(pod_path(query)).map { |path| path.basename.to_s }
266
+ if [query] == found
267
+ set = set(query)
268
+ set if set.specification.name == query
222
269
  end
223
270
  end
224
271
 
@@ -290,11 +337,37 @@ module Pod
290
337
  Dir.chdir(repo) do
291
338
  prev_commit_hash = git_commit_hash
292
339
  update_git_repo(show_output)
340
+ refresh_metadata
341
+ if version = metadata.last_compatible_version(Version.new(CORE_VERSION))
342
+ tag = "v#{version}"
343
+ CoreUI.warn "Using the `#{tag}` tag of the `#{name}` source because " \
344
+ "it is the last version compatible with CocoaPods #{CORE_VERSION}."
345
+ git(['checkout', tag])
346
+ end
293
347
  changed_spec_paths = diff_until_commit_hash(prev_commit_hash)
294
348
  end
295
349
  changed_spec_paths
296
350
  end
297
351
 
352
+ def git?
353
+ Dir.chdir(repo) do
354
+ !git(%w(rev-parse HEAD)).empty?
355
+ end
356
+ end
357
+
358
+ def verify_compatibility!
359
+ return if metadata.compatible?(CORE_VERSION)
360
+
361
+ version_msg = if metadata.minimum_cocoapods_version == metadata.maximum_cocoapods_version
362
+ metadata.minimum_cocoapods_version
363
+ else
364
+ "#{metadata.minimum_cocoapods_version} - #{metadata.maximum_cocoapods_version}"
365
+ end
366
+ raise Informative, "The `#{name}` repo requires " \
367
+ "CocoaPods #{version_msg} (currently using #{CORE_VERSION})\n" \
368
+ 'Update CocoaPods, or checkout the appropriate tag in the repo.'
369
+ end
370
+
298
371
  public
299
372
 
300
373
  # @!group Representations
@@ -326,8 +399,9 @@ module Pod
326
399
  #-------------------------------------------------------------------------#
327
400
 
328
401
  def ensure_in_repo!
329
- return if Pathname.pwd == repo
330
- raise 'Must be in the root of the repo'
402
+ return if Pathname.pwd.realpath == repo.realpath
403
+ raise StandardError, "Must be in the root of the repo (#{repo}), " \
404
+ "instead in #{Pathname.pwd}."
331
405
  end
332
406
 
333
407
  # Loads the specification for the given Pod gracefully.
@@ -349,22 +423,8 @@ module Pod
349
423
  nil
350
424
  end
351
425
 
352
- # @return [Pathname] The directory where the specs are stored.
353
- #
354
- # @note In previous versions of CocoaPods they used to be stored in
355
- # the root of the repo. This lead to issues, especially with
356
- # the GitHub interface and now they are stored in a dedicated
357
- # folder.
358
- #
359
- def specs_dir
360
- @specs_dir ||= begin
361
- specs_sub_dir = repo + 'Specs'
362
- if specs_sub_dir.exist?
363
- specs_sub_dir
364
- elsif repo.exist?
365
- repo
366
- end
367
- end
426
+ def refresh_metadata
427
+ @metadata = Metadata.from_file(metadata_path)
368
428
  end
369
429
 
370
430
  def git_commit_hash
@@ -374,10 +434,16 @@ module Pod
374
434
 
375
435
  def update_git_repo(show_output = false)
376
436
  ensure_in_repo!
437
+ git(['checkout', git_tracking_branch])
377
438
  output = git(%w(pull --ff-only), :include_error => true)
378
439
  CoreUI.puts output if show_output
379
440
  end
380
441
 
442
+ def git_tracking_branch
443
+ path = repo.join('.git', 'cocoapods_branch')
444
+ path.file? ? path.read.strip : 'master'
445
+ end
446
+
381
447
  def diff_until_commit_hash(commit_hash)
382
448
  ensure_in_repo!
383
449
  git(%W(diff --name-only #{commit_hash}..HEAD)).split("\n")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cocoapods-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.beta.6
4
+ version: 1.0.0.beta.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eloy Duran
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-03-14 00:00:00.000000000 Z
12
+ date: 2016-04-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -98,6 +98,8 @@ files:
98
98
  - lib/cocoapods-core/source/acceptor.rb
99
99
  - lib/cocoapods-core/source/aggregate.rb
100
100
  - lib/cocoapods-core/source/health_reporter.rb
101
+ - lib/cocoapods-core/source/manager.rb
102
+ - lib/cocoapods-core/source/metadata.rb
101
103
  - lib/cocoapods-core/specification.rb
102
104
  - lib/cocoapods-core/specification/consumer.rb
103
105
  - lib/cocoapods-core/specification/dsl.rb
@@ -138,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
140
  version: '0'
139
141
  requirements: []
140
142
  rubyforge_project:
141
- rubygems_version: 2.6.2
143
+ rubygems_version: 2.6.3
142
144
  signing_key:
143
145
  specification_version: 3
144
146
  summary: The models of CocoaPods