cocoapods-nexus 0.0.6 → 0.0.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
  SHA256:
3
- metadata.gz: f26e201f54fc7fa7527ced7f39be79211ea312640a2080b1cf9dad3f27d32b8e
4
- data.tar.gz: eea1a06b6eb1eb14f80b63961c37d2ed1abfcdc1e83e126a8659cf0e19b76095
3
+ metadata.gz: 96b8c8d75508a36b5121b69dadb1c71acec0edae13eff2b779d97793fcfbd33b
4
+ data.tar.gz: 05db6f85c0611aae05420f65e487221692e0c08cf50c5bab656584e7a14fe78a
5
5
  SHA512:
6
- metadata.gz: 472e4c8da17a58c0f3dc9067a968308727b24f97da71f31da0bd276a1bdc86574bf5a6dcc659cc5122e5125ccd0d0efc2ee665f85f3a1f4f9d691b752964d159
7
- data.tar.gz: 525daa7f70e4fe069c0d83ffa6ac68bf20ebaa8bea43c7dd9762d0245a59f0e79d2fff83b29bf950eb07cfdc0e3670520050a7ce97c19751485883e3efe5c0d5
6
+ metadata.gz: 32e7ac7b9fd22c4f41f7f30b25d96c2178b4122447c69fb7e58023c85f8d9286bb2795364e0e2b00ccf76d68f621b0f643e9d7b98cd71dfef551ab364ef5c066
7
+ data.tar.gz: 269c7f2054d40ec71345698aab553e5aa421d8738d21f2227f7e7ca773b522dfac13ad23e5605291d7085e8302888bf83eb64ec4930c7ecf71f8a2414790fe40
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'cocoapods-nexus'
7
- spec.version = "0.0.6"
7
+ spec.version = '0.0.7'
8
8
  spec.authors = ['mrdaios']
9
9
  spec.email = ['mrdaios@gmail.com']
10
10
  spec.description = 'a cocoapods plugin for nexus.'
@@ -19,8 +19,9 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.add_runtime_dependency 'cocoapods', '>= 1.9.3'
22
+ spec.add_runtime_dependency 'concurrent-ruby', '~> 1.1'
22
23
  spec.add_runtime_dependency 'rest-client', '~> 2.1.0'
23
- spec.add_runtime_dependency 'versionomy', '~> 0.5.0'
24
+ spec.add_runtime_dependency 'typhoeus', '~> 1.0'
24
25
 
25
26
  spec.add_development_dependency 'bundler', '~> 1.3'
26
27
  spec.add_development_dependency 'rake', '~> 13.0'
@@ -112,7 +112,7 @@ module CocoapodsNexus
112
112
 
113
113
  def send_request(connection_method, endpoint, parameters: '', headers: {}, api_version: 'v1')
114
114
  parameters = parameters.to_json if headers['Content-Type'] == 'application/json'
115
- url = "#{@hostname}/nexus/service/rest/#{api_version}/#{endpoint}"
115
+ url = File.join(@hostname,"/nexus/service/rest/#{api_version}/#{endpoint}")
116
116
  catch_connection_error do
117
117
  RestClient::Request.execute(
118
118
  method: connection_method,
@@ -1,10 +1,14 @@
1
+ require 'concurrent'
2
+ require 'typhoeus'
1
3
  require 'cocoapods-nexus/api'
2
4
  require 'cocoapods-nexus/downloader'
3
- require 'versionomy'
4
5
  require 'cocoapods-nexus/hook/specification'
5
6
 
6
7
  module Pod
7
8
  class NexusSource < Source
9
+ include Concurrent
10
+ HYDRA_EXECUTOR = Concurrent::SingleThreadExecutor.new
11
+
8
12
  def initialize(repo, url)
9
13
  @source_url = url
10
14
  super(repo)
@@ -27,34 +31,87 @@ module Pod
27
31
  'nexus'
28
32
  end
29
33
 
34
+ # @return [Array<Version>] all the available versions for the Pod, sorted
35
+ # from highest to lowest.
36
+ #
37
+ # @param [String] name
38
+ # the name of the Pod.
39
+ #
40
+ def versions(name)
41
+ return nil unless specs_dir
42
+ raise ArgumentError, 'No name' unless name
43
+
44
+ return @versions_by_name[name] unless @versions_by_name[name].nil?
45
+
46
+ pod_path_actual = pod_path(name)
47
+ pod_path_relative = pod_path(name).relative_path_from(repo)
48
+
49
+ concurrent_requests_catching_errors do
50
+ loaders = []
51
+ components = nexus_api.search_maven_component(artifact_id: name)
52
+ if !components.nil? && components.count > 0
53
+ # 从服务器获取version
54
+ @versions_by_name[name] ||= components.map do |component|
55
+ # Optimization: ensure all the podspec files at least exist. The correct one will get refreshed
56
+ # in #specification_path regardless.
57
+ podspec_version_path_relative = Pathname.new(component["version"]).join("#{name}.podspec")
58
+
59
+ unless pod_path_actual.join(podspec_version_path_relative).exist?
60
+ remote_url = parse_artifacte_asset_url(component, 'podspec')
61
+ # Queue all podspec download tasks first
62
+ loaders << download_file_async(pod_path_relative.join(podspec_version_path_relative).to_s, remote_url)
63
+ end
64
+
65
+ begin
66
+ Version.new(component["version"]) if component["version"][0, 1] != '.'
67
+ rescue ArgumentError
68
+ raise Informative, 'An unexpected version directory ' \
69
+ "`#{component["version"]}` was encountered for the " \
70
+ "`#{pod_path_actual}` Pod in the `#{name}` repository."
71
+ end
72
+ end.compact.sort.reverse
73
+ end
74
+ # Block and wait for all to complete running on Hydra
75
+ Promises.zip_futures_on(HYDRA_EXECUTOR, *loaders).wait!
76
+ end
77
+ @versions_by_name[name]
78
+ end
79
+
30
80
  # 从nexus查询依赖
31
81
  # @param [Object] query
32
82
  def search(query)
33
83
  unless File.exist?("#{repo}/.nexus")
34
84
  raise Informative, "Unable to find a source named: `#{name}`"
35
85
  end
36
-
37
- found = find_local_podspec(query)
38
- # 本地没查询到,则从nexus服务查询
39
- if found == []
40
- # 暂时这样处理
41
- spec_version = query.requirement.requirements.last.last.to_s
42
- artifacte = nexus_find_artifacte(spec_name: query.root_name, spec_version: spec_version)
43
- if artifacte
44
- download_url = parse_artifacte_asset_url(artifacte, 'podspec')
45
- if download_url
46
- target_path = "#{@repo}/#{query.root_name}/#{spec_version}"
47
- downloader = Pod::Downloader::NexusHttp.new(target_path, download_url, {:type => 'podspec', :name => query.root_name})
48
- downloader.download
49
-
50
- found = find_local_podspec(query)
51
- end
52
- end
86
+ if query.is_a?(Dependency)
87
+ query = query.root_name
53
88
  end
54
89
 
55
- if found == [query.root_name]
56
- set = set(query.root_name)
57
- set if set.specification_name == query.root_name
90
+ # found = find_local_podspec(query)
91
+ # # 本地没查询到,则从nexus服务查询
92
+ # if found == []
93
+ # # 暂时这样处理
94
+ # spec_version = query.requirement.requirements.last.last.to_s
95
+ # artifacte = nexus_find_artifacte(spec_name: query.root_name, spec_version: spec_version)
96
+ # if artifacte
97
+ # download_url = parse_artifacte_asset_url(artifacte, 'podspec')
98
+ # if download_url
99
+ # target_path = "#{@repo}/#{query.root_name}/#{spec_version}"
100
+ # downloader = Pod::Downloader::NexusHttp.new(target_path, download_url, {:type => 'podspec', :name => query.root_name})
101
+ # downloader.download
102
+ #
103
+ # found = find_local_podspec(query)
104
+ # end
105
+ # end
106
+ # end
107
+
108
+ # version信息暂时不缓存到本地
109
+ components = nexus_api.search_maven_component(artifact_id: query)
110
+ found = !components.nil? && components.count > 0 ? query : nil
111
+
112
+ if found
113
+ set = set(query)
114
+ set if set.specification_name == query
58
115
  end
59
116
  end
60
117
 
@@ -120,5 +177,156 @@ module Pod
120
177
  repo_name = File.basename(@repo).gsub('nexus_', '')
121
178
  @nexus_api ||= CocoapodsNexus::API.new(hostname: url, repo: repo_name)
122
179
  end
180
+
181
+ def concurrent_requests_catching_errors
182
+ yield
183
+ rescue MultipleErrors => e
184
+ # aggregated error message from `Concurrent`
185
+ errors = e.errors
186
+ raise Informative, "CDN: #{name} Repo update failed - #{e.errors.size} error(s):\n#{errors.join("\n")}"
187
+ end
188
+
189
+ def download_file_async(partial_url, remote_url)
190
+ file_remote_url = URI.encode(remote_url)
191
+ path = repo + partial_url
192
+
193
+ # file_okay = local_file_okay?(partial_url)
194
+ # if file_okay
195
+ # if @startup_time < File.mtime(path)
196
+ # debug "CDN: #{name} Relative path: #{partial_url} modified during this run! Returning local"
197
+ # return Promises.fulfilled_future(partial_url, HYDRA_EXECUTOR)
198
+ # end
199
+ #
200
+ # unless @check_existing_files_for_update
201
+ # debug "CDN: #{name} Relative path: #{partial_url} exists! Returning local because checking is only perfomed in repo update"
202
+ # return Promises.fulfilled_future(partial_url, HYDRA_EXECUTOR)
203
+ # end
204
+ # end
205
+
206
+ path.dirname.mkpath
207
+
208
+ # etag_path = path.sub_ext(path.extname + '.etag')
209
+
210
+ # etag = File.read(etag_path) if file_okay && File.exist?(etag_path)
211
+ # debug "CDN: #{name} Relative path: #{partial_url}, has ETag? #{etag}" unless etag.nil?
212
+
213
+ download_and_save_with_retries_async(partial_url, file_remote_url)
214
+ end
215
+
216
+ def download_and_save_with_retries_async(partial_url, file_remote_url, retries = 5)
217
+ path = repo + partial_url
218
+ # etag_path = path.sub_ext(path.extname + '.etag')
219
+
220
+ download_task = download_typhoeus_impl_async(file_remote_url, nil).then do |response|
221
+ case response.response_code
222
+ when 301
223
+ redirect_location = response.headers['location']
224
+ # debug "CDN: #{name} Redirecting from #{file_remote_url} to #{redirect_location}"
225
+ download_and_save_with_retries_async(partial_url, redirect_location, nil )
226
+ when 304
227
+ # debug "CDN: #{name} Relative path not modified: #{partial_url}"
228
+ # We need to update the file modification date, as it is later used for freshness
229
+ # optimization. See #initialize for more information.
230
+ FileUtils.touch path
231
+ partial_url
232
+ when 200
233
+ File.open(path, 'w') { |f| f.write(response.response_body.force_encoding('UTF-8')) }
234
+
235
+ # etag_new = response.headers['etag'] unless response.headers.nil?
236
+ # debug "CDN: #{name} Relative path downloaded: #{partial_url}, save ETag: #{etag_new}"
237
+ # File.open(etag_path, 'w') { |f| f.write(etag_new) } unless etag_new.nil?
238
+ partial_url
239
+ when 404
240
+ # debug "CDN: #{name} Relative path couldn't be downloaded: #{partial_url} Response: #{response.response_code}"
241
+ nil
242
+ when 502, 503, 504
243
+ # Retryable HTTP errors, usually related to server overloading
244
+ if retries <= 1
245
+ # raise Informative, "CDN: #{name} URL couldn't be downloaded: #{file_remote_url} Response: #{response.response_code} #{response.response_body}"
246
+ else
247
+ # debug "CDN: #{name} URL couldn't be downloaded: #{file_remote_url} Response: #{response.response_code} #{response.response_body}, retries: #{retries - 1}"
248
+ exponential_backoff_async(retries).then do
249
+ download_and_save_with_retries_async(partial_url, file_remote_url, nil , retries - 1)
250
+ end
251
+ end
252
+ when 0
253
+ # Non-HTTP errors, usually network layer
254
+ if retries <= 1
255
+ raise Informative, "CDN: #{name} URL couldn't be downloaded: #{file_remote_url} Response: #{response.return_message}"
256
+ else
257
+ debug "CDN: #{name} URL couldn't be downloaded: #{file_remote_url} Response: #{response.return_message}, retries: #{retries - 1}"
258
+ exponential_backoff_async(retries).then do
259
+ download_and_save_with_retries_async(partial_url, file_remote_url, etag, retries - 1)
260
+ end
261
+ end
262
+ else
263
+ raise Informative, "CDN: #{name} URL couldn't be downloaded: #{file_remote_url} Response: #{response.response_code} #{response.response_body}"
264
+ end
265
+ end
266
+
267
+ # Calling `Future#run` flattens the chained futures created by retries or redirects
268
+ #
269
+ # Does not, in fact, run the task - that is already happening in Hydra at this point
270
+ download_task.run
271
+ end
272
+
273
+ def exponential_backoff_async(retries)
274
+ sleep_async(backoff_time(retries))
275
+ end
276
+
277
+ def backoff_time(retries)
278
+ current_retry = MAX_NUMBER_OF_RETRIES - retries
279
+ 4 * 2**current_retry
280
+ end
281
+
282
+ def sleep_async(seconds)
283
+ # Async sleep to avoid blocking either the main or the Hydra thread
284
+ Promises.schedule_on(HYDRA_EXECUTOR, seconds)
285
+ end
286
+
287
+ def download_typhoeus_impl_async(file_remote_url, etag)
288
+ # Create a prefereably HTTP/2 request - the protocol is ultimately responsible for picking
289
+ # the maximum supported protocol
290
+ # When debugging with proxy, use the following extra options:
291
+ # :proxy => 'http://localhost:8888',
292
+ # :ssl_verifypeer => false,
293
+ # :ssl_verifyhost => 0,
294
+ request = Typhoeus::Request.new(
295
+ file_remote_url,
296
+ :method => :get,
297
+ :http_version => :httpv2_0,
298
+ :timeout => 10,
299
+ :connecttimeout => 10,
300
+ :accept_encoding => 'gzip',
301
+ :netrc => :optional,
302
+ :netrc_file => Netrc.default_path,
303
+ :headers => etag.nil? ? {} : { 'If-None-Match' => etag },
304
+ )
305
+
306
+ future = Promises.resolvable_future_on(Concurrent::SingleThreadExecutor.new)
307
+ queue_request(request)
308
+ request.on_complete do |response|
309
+ future.fulfill(response)
310
+ end
311
+
312
+ # This `Future` should never reject, network errors are exposed on `Typhoeus::Response`
313
+ future
314
+ end
315
+
316
+ def queue_request(request)
317
+ @hydra ||= Typhoeus::Hydra.new
318
+
319
+ # Queue the request into the Hydra (libcurl reactor).
320
+ @hydra.queue(request)
321
+
322
+ # Cycle the reactor on a separate thread
323
+ #
324
+ # The way it works is that if more requests are queued while Hydra is in the `#run`
325
+ # method, it will keep executing them
326
+ #
327
+ # The upcoming calls to `#run` will simply run empty.
328
+ HYDRA_EXECUTOR.post(@hydra, &:run)
329
+ end
330
+
123
331
  end
124
332
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cocoapods-nexus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - mrdaios
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-08 00:00:00.000000000 Z
11
+ date: 2020-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cocoapods
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.9.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: concurrent-ruby
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.1'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rest-client
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -39,19 +53,19 @@ dependencies:
39
53
  - !ruby/object:Gem::Version
40
54
  version: 2.1.0
41
55
  - !ruby/object:Gem::Dependency
42
- name: versionomy
56
+ name: typhoeus
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: 0.5.0
61
+ version: '1.0'
48
62
  type: :runtime
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: 0.5.0
68
+ version: '1.0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: bundler
57
71
  requirement: !ruby/object:Gem::Requirement