cocoapods-nexus 0.0.6 → 0.0.7

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