cocoapods-downloader 0.1.0
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.
Potentially problematic release.
This version of cocoapods-downloader might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.markdown +72 -0
- data/lib/cocoapods-downloader.rb +72 -0
- data/lib/cocoapods-downloader/api.rb +73 -0
- data/lib/cocoapods-downloader/api_exposable.rb +21 -0
- data/lib/cocoapods-downloader/base.rb +229 -0
- data/lib/cocoapods-downloader/gem_version.rb +10 -0
- data/lib/cocoapods-downloader/git.rb +273 -0
- data/lib/cocoapods-downloader/http.rb +113 -0
- data/lib/cocoapods-downloader/mercurial.rb +45 -0
- data/lib/cocoapods-downloader/subversion.rb +60 -0
- metadata +58 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 423ed46eabbb91ff618c8848326e3b66f006588f
|
4
|
+
data.tar.gz: d88c23a94aa7f69e33d8cd3ce429086fc072a858
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 15f102043735c5a60b661d42485b2f286ab586210961d7f68f91cf306310ef679112412f7b1533d086556a60b0b5c2fa8b9efb825ad6daf1ad784845a5d06864
|
7
|
+
data.tar.gz: 375648ab909ee38343e34c240ec598e7316cb6fbb13aaaed321ecea90a4cc5a0fb0d260beb7bd99e1f516b46ceb8ac85d2cc3c83c59a770d6572c4bf16151ac5
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2011 - 2012 Eloy Durán <eloy.de.enige@gmail.com>
|
2
|
+
Copyright (c) 2012 Fabio Pelosin <fabiopelosin@gmail.com>
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
6
|
+
in the Software without restriction, including without limitation the rights
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
9
|
+
furnished to do so, subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be included in
|
12
|
+
all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
THE SOFTWARE.
|
21
|
+
|
data/README.markdown
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# Downloader
|
2
|
+
|
3
|
+
A small library for downloading files from remotes in a folder.
|
4
|
+
|
5
|
+
[](https://travis-ci.org/CocoaPods/cocoapods-downloader)
|
6
|
+
|
7
|
+
## Install
|
8
|
+
|
9
|
+
```
|
10
|
+
$ [sudo] gem install claide
|
11
|
+
```
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
require 'cocoapods-downloaders'
|
17
|
+
|
18
|
+
target_path = './Downloads/MyDownload'
|
19
|
+
options = { :git => 'example.com' }
|
20
|
+
downloader = Pod::Downloaders.for_target(target_path, options)
|
21
|
+
downloader.cache_root = '~/Library/Caches/APPNAME'
|
22
|
+
downloader.max_cache_size = 500
|
23
|
+
downloader.download
|
24
|
+
downloader.checkout_options #=> { :git => 'example.com', :commit => 'd7f410490dabf7a6bde665ba22da102c3acf1bd9' }
|
25
|
+
```
|
26
|
+
|
27
|
+
The downloader class supports the following option keys:
|
28
|
+
|
29
|
+
- git: commit, tag, branch, submodules
|
30
|
+
- hg: revision
|
31
|
+
- svn: revision, tag, folder
|
32
|
+
- http: type
|
33
|
+
|
34
|
+
The downloader also provides hooks which allow to customize its output or the way in which the commands are executed
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
require 'cocoapods-downloader'
|
38
|
+
|
39
|
+
module Pod
|
40
|
+
module Downloader
|
41
|
+
class Base
|
42
|
+
|
43
|
+
override_api do
|
44
|
+
def self.execute_command(executable, command, raise_on_failure = false)
|
45
|
+
puts "Will download"
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.ui_action(ui_message)
|
50
|
+
puts ui_message.green
|
51
|
+
yield
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
## Extraction
|
61
|
+
|
62
|
+
This gem was extracted from [CocoaPods](https://github.com/CocoaPods/CocoaPods). Refer to also that repository for the history and the contributors.
|
63
|
+
|
64
|
+
## Collaborate
|
65
|
+
|
66
|
+
All CocoaPods development happens on GitHub, there is a repository for [CocoaPods](https://github.com/CocoaPods/CocoaPods) and one for the [CocoaPods specs](https://github.com/CocoaPods/Specs). Contributing patches or Pods is really easy and gratifying. You even get push access when one of your specs or patches is accepted.
|
67
|
+
|
68
|
+
Follow [@CocoaPodsOrg](http://twitter.com/CocoaPodsOrg) to get up to date information about what's going on in the CocoaPods world.
|
69
|
+
|
70
|
+
## License
|
71
|
+
|
72
|
+
This gem and CocoaPods are available under the MIT license.
|
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
module Pod
|
3
|
+
module Downloader
|
4
|
+
require 'cocoapods-downloader/gem_version'
|
5
|
+
|
6
|
+
require 'cocoapods-downloader/api'
|
7
|
+
require 'cocoapods-downloader/api_exposable'
|
8
|
+
require 'cocoapods-downloader/base'
|
9
|
+
|
10
|
+
|
11
|
+
# @return [Hash{Symbol=>Class}] The symbol of the options array associated
|
12
|
+
# with each class.
|
13
|
+
#
|
14
|
+
def self.downloader_class_by_key
|
15
|
+
require 'cocoapods-downloader/git'
|
16
|
+
require 'cocoapods-downloader/mercurial'
|
17
|
+
require 'cocoapods-downloader/subversion'
|
18
|
+
require 'cocoapods-downloader/http'
|
19
|
+
|
20
|
+
{
|
21
|
+
:git => Git,
|
22
|
+
:hg => Mercurial,
|
23
|
+
:svn => Subversion,
|
24
|
+
:http => Http,
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Downloader::Base] A concrete downloader according to the
|
29
|
+
# options.
|
30
|
+
#
|
31
|
+
# @todo Improve the common support for the cache in Base and add specs.
|
32
|
+
# @todo Find a way to switch to GitHub tarballs if no cache is used. Have
|
33
|
+
# global options for the Downloader cache?
|
34
|
+
#
|
35
|
+
def self.for_target(target_path, options)
|
36
|
+
|
37
|
+
if target_path.nil?
|
38
|
+
raise DownloaderError, "No target path provided."
|
39
|
+
end
|
40
|
+
|
41
|
+
if options.nil? || options.empty?
|
42
|
+
raise DownloaderError, "No source url provided."
|
43
|
+
end
|
44
|
+
|
45
|
+
options = options.dup
|
46
|
+
klass = nil
|
47
|
+
url = nil
|
48
|
+
downloader_class_by_key.each do |key, key_klass|
|
49
|
+
url = options.delete(key)
|
50
|
+
if url
|
51
|
+
klass = key_klass
|
52
|
+
break
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
unless klass
|
57
|
+
raise DownloaderError, "Unsupported download strategy `#{options.inspect}`."
|
58
|
+
end
|
59
|
+
|
60
|
+
if klass == Git && url.to_s =~ /github.com/
|
61
|
+
klass = GitHub
|
62
|
+
end
|
63
|
+
|
64
|
+
klass.new(target_path, url, options)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Denotes the error generated by a Downloader
|
68
|
+
#
|
69
|
+
class DownloaderError < StandardError; end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Pod
|
2
|
+
module Downloader
|
3
|
+
|
4
|
+
# The Downloader::Hooks module allows to adapt the Downloader to
|
5
|
+
# the UI of other gems.
|
6
|
+
#
|
7
|
+
module API
|
8
|
+
|
9
|
+
# Executes
|
10
|
+
# @return [String] the ouptu of the command.
|
11
|
+
#
|
12
|
+
def execute_command(executable, command, raise_on_failure = false)
|
13
|
+
output = `\n#{executable} #{command} 2>&1`
|
14
|
+
check_exit_code!(executable, command, output) if raise_on_failure
|
15
|
+
puts output
|
16
|
+
output
|
17
|
+
end
|
18
|
+
|
19
|
+
# Cheks if the just executed command completed sucessfully.
|
20
|
+
#
|
21
|
+
# @raise If the command failed.
|
22
|
+
#
|
23
|
+
# @return [void]
|
24
|
+
#
|
25
|
+
def check_exit_code!(executable, command, output)
|
26
|
+
if $?.to_i != 0
|
27
|
+
raise DownloaderError, "Error on `#{executable} #{command}`.\n#{output}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Indicates that an action will be perfomed. The action is passed as a
|
32
|
+
# block.
|
33
|
+
#
|
34
|
+
# @param [String] message
|
35
|
+
# The message associated with the action.
|
36
|
+
#
|
37
|
+
# @yield The action, this block is always exectued.
|
38
|
+
#
|
39
|
+
# @retur [void]
|
40
|
+
#
|
41
|
+
def ui_action(message)
|
42
|
+
puts message
|
43
|
+
yield
|
44
|
+
end
|
45
|
+
|
46
|
+
# Indicates that a minor action will be perfomed. The action is passed as
|
47
|
+
# a block.
|
48
|
+
#
|
49
|
+
# @param [String] message
|
50
|
+
# The message associated with the action.
|
51
|
+
#
|
52
|
+
# @yield The action, this block is always exectued.
|
53
|
+
#
|
54
|
+
# @retur [void]
|
55
|
+
#
|
56
|
+
def ui_sub_action(message)
|
57
|
+
puts message
|
58
|
+
yield
|
59
|
+
end
|
60
|
+
|
61
|
+
# Prints an UI message.
|
62
|
+
#
|
63
|
+
# @param [String] message
|
64
|
+
# The message associated with the action.
|
65
|
+
#
|
66
|
+
# @retur [void]
|
67
|
+
#
|
68
|
+
def ui_message(message)
|
69
|
+
puts message
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Pod
|
2
|
+
module Downloader
|
3
|
+
module APIExposable
|
4
|
+
|
5
|
+
def expose_api(mod = nil, &block)
|
6
|
+
if mod.nil?
|
7
|
+
if block.nil?
|
8
|
+
raise "Either a module or a block that's used to create a module is required."
|
9
|
+
else
|
10
|
+
mod = Module.new(&block)
|
11
|
+
end
|
12
|
+
elsif mod && block
|
13
|
+
raise "Only a module *or* is required, not both."
|
14
|
+
end
|
15
|
+
include mod
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :override_api, :expose_api
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
module Pod
|
2
|
+
module Downloader
|
3
|
+
|
4
|
+
# The base class defines the common behaviour of the downloaders.
|
5
|
+
#
|
6
|
+
# @abstract Subclass and implement {#download}.
|
7
|
+
#
|
8
|
+
# @private
|
9
|
+
#
|
10
|
+
class Base
|
11
|
+
|
12
|
+
extend APIExposable
|
13
|
+
expose_api API
|
14
|
+
|
15
|
+
# @abstract Override in subclasses.
|
16
|
+
#
|
17
|
+
# @return [Array<Symbol>] the options accepted by the concrete class.
|
18
|
+
#
|
19
|
+
def self.options
|
20
|
+
[]
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Pathname] the destination folder for the download.
|
24
|
+
#
|
25
|
+
attr_reader :target_path
|
26
|
+
|
27
|
+
# @return [String] the url of the remote source.
|
28
|
+
#
|
29
|
+
attr_reader :url
|
30
|
+
|
31
|
+
# @return [Hash={Symbol=>String}] options specific to each concrete
|
32
|
+
# downloader.
|
33
|
+
#
|
34
|
+
attr_reader :options
|
35
|
+
|
36
|
+
# @param [String, Pathname] target_path @see target_path
|
37
|
+
# @param [String] url @see url
|
38
|
+
# @param [Hash={Symbol=>String}] options @see options
|
39
|
+
#
|
40
|
+
# @todo There is no need of the download only option, it should be
|
41
|
+
# deprecated and the GitHub downloader should be initialized by
|
42
|
+
# other means.
|
43
|
+
#
|
44
|
+
def initialize(target_path, url, options)
|
45
|
+
require 'pathname'
|
46
|
+
@target_path, @url, @options = Pathname.new(target_path), url, options
|
47
|
+
@max_cache_size = DEFAULT_MAX_CACHE_SIZE
|
48
|
+
|
49
|
+
accepted_options = self.class.options + [:download_only]
|
50
|
+
unrecognized_options = options.keys - accepted_options
|
51
|
+
unless unrecognized_options.empty?
|
52
|
+
raise DownloaderError, "Unrecognized options `#{unrecognized_options}`"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [String] the name of the downloader.
|
57
|
+
#
|
58
|
+
# @example Downloader::Mercurial name
|
59
|
+
#
|
60
|
+
# "Mercurial"
|
61
|
+
#
|
62
|
+
def name
|
63
|
+
self.class.name.split('::').last
|
64
|
+
end
|
65
|
+
|
66
|
+
#-----------------------------------------------------------------------#
|
67
|
+
|
68
|
+
# @!group Configuration
|
69
|
+
|
70
|
+
# @return [Fixnum] The maximum allowed size for the cache expressed in
|
71
|
+
# Mb. Defaults to `500` Mb.
|
72
|
+
#
|
73
|
+
# @note This is specific per downloader class.
|
74
|
+
#
|
75
|
+
attr_accessor :max_cache_size
|
76
|
+
|
77
|
+
# @return [String] The directory to use as root of the cache. If no
|
78
|
+
# specified the caching will not be used. Defaults to `nil`.
|
79
|
+
#
|
80
|
+
attr_accessor :cache_root
|
81
|
+
|
82
|
+
# @return [Bool] Whether the downloader should use a more aggressive
|
83
|
+
# caching or ensure that the cache always return the value of the
|
84
|
+
# remote. Defaults to `false`.
|
85
|
+
#
|
86
|
+
attr_accessor :aggressive_cache
|
87
|
+
alias_method :aggressive_cache?, :aggressive_cache
|
88
|
+
|
89
|
+
#-----------------------------------------------------------------------#
|
90
|
+
|
91
|
+
# @!group Downloading
|
92
|
+
|
93
|
+
# Downloads the revision specified in the option of a source. If no
|
94
|
+
# revision is specified it fall-back to {#download_head}.
|
95
|
+
#
|
96
|
+
# @return [void]
|
97
|
+
#
|
98
|
+
def download
|
99
|
+
ui_action("#{name} download") do
|
100
|
+
target_path.mkpath
|
101
|
+
download!
|
102
|
+
prune_cache
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Downloads the head revision of a source.
|
107
|
+
#
|
108
|
+
# @todo Spec for raise.
|
109
|
+
#
|
110
|
+
# @return [void]
|
111
|
+
#
|
112
|
+
def download_head
|
113
|
+
ui_action("#{name} HEAD download") do
|
114
|
+
if self.respond_to?(:download_head!, true)
|
115
|
+
download_head!
|
116
|
+
else
|
117
|
+
raise DownloaderError, "The `#{name}` downloader does not support " \
|
118
|
+
"the HEAD option."
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# @return [Bool] Whether the options provided completely identify a source
|
124
|
+
# or could lead to the download of different files in future.
|
125
|
+
#
|
126
|
+
def options_specific?
|
127
|
+
true
|
128
|
+
end
|
129
|
+
|
130
|
+
# @return [Hash{Symbol=>String}] The options that would allow to
|
131
|
+
# re-download the exact files.
|
132
|
+
#
|
133
|
+
def checkout_options
|
134
|
+
raise "Abstract method"
|
135
|
+
end
|
136
|
+
|
137
|
+
#-----------------------------------------------------------------------#
|
138
|
+
|
139
|
+
# @!group Cache
|
140
|
+
|
141
|
+
public
|
142
|
+
|
143
|
+
# @return [Pathname] The directory where the cache for the current url
|
144
|
+
# should be stored.
|
145
|
+
#
|
146
|
+
# @note The name of the directory is the SHA1 hash value of the URL.
|
147
|
+
#
|
148
|
+
def cache_path
|
149
|
+
require 'digest/sha1'
|
150
|
+
if cache_root
|
151
|
+
@cache_path ||= class_cache_dir + "#{Digest::SHA1.hexdigest(url.to_s)}"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
# @return [Pathname] The directory where the caches are stored.
|
158
|
+
#
|
159
|
+
def class_cache_dir
|
160
|
+
Pathname.new(File.expand_path(cache_root)) + name
|
161
|
+
end
|
162
|
+
|
163
|
+
# @return [Bool] Whether the downloader should use the cache.
|
164
|
+
#
|
165
|
+
def use_cache?
|
166
|
+
!cache_root.nil? && !@options[:download_only]
|
167
|
+
end
|
168
|
+
|
169
|
+
# The default maximum allowed size for the cache expressed in Mb.
|
170
|
+
#
|
171
|
+
DEFAULT_MAX_CACHE_SIZE = 500
|
172
|
+
|
173
|
+
# @return [Integer] The global size of the cache expressed in Mb.
|
174
|
+
#
|
175
|
+
def caches_size
|
176
|
+
`du -cm`.split("\n").last.to_i
|
177
|
+
end
|
178
|
+
|
179
|
+
# @return [void] Deletes the oldest caches until they the global size is
|
180
|
+
# below the maximum allowed.
|
181
|
+
#
|
182
|
+
def prune_cache
|
183
|
+
return unless cache_root && class_cache_dir.exist?
|
184
|
+
Dir.chdir(class_cache_dir) do
|
185
|
+
repos = Pathname.new(class_cache_dir).children.select { |c| c.directory? }.sort_by(&:ctime)
|
186
|
+
while caches_size >= max_cache_size && !repos.empty?
|
187
|
+
dir = repos.shift
|
188
|
+
ui_message "Removing #{name} cache for `#{cache_origin_url(dir)}'"
|
189
|
+
dir.rmtree
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
# Defines two methods for an executable, based on its name. The bag version
|
197
|
+
# raises if the executable terminates with a non-zero exit code.
|
198
|
+
#
|
199
|
+
# For example
|
200
|
+
#
|
201
|
+
# executable :git
|
202
|
+
#
|
203
|
+
# generates
|
204
|
+
#
|
205
|
+
# def git(command)
|
206
|
+
# Hooks.execute_with_check("git", command, false)
|
207
|
+
# end
|
208
|
+
#
|
209
|
+
# def git!(command)
|
210
|
+
# Hooks.execute_with_check("git", command, true)
|
211
|
+
# end
|
212
|
+
#
|
213
|
+
# @param [Symbol] name
|
214
|
+
# the name of the executable.
|
215
|
+
#
|
216
|
+
# @return [void]
|
217
|
+
#
|
218
|
+
def self.executable(name)
|
219
|
+
define_method(name) do |command|
|
220
|
+
execute_command(name.to_s, command, false)
|
221
|
+
end
|
222
|
+
|
223
|
+
define_method(name.to_s + "!") do |command|
|
224
|
+
execute_command(name.to_s, command, true)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
module Pod
|
2
|
+
module Downloader
|
3
|
+
|
4
|
+
# Concreted Downloader class that provides support for specifications with
|
5
|
+
# git sources.
|
6
|
+
#
|
7
|
+
class Git < Base
|
8
|
+
|
9
|
+
def self.options
|
10
|
+
[:commit, :tag, :branch, :submodules]
|
11
|
+
end
|
12
|
+
|
13
|
+
def options_specific?
|
14
|
+
!options[:commit].nil? || !options[:tag].nil?
|
15
|
+
end
|
16
|
+
|
17
|
+
def checkout_options
|
18
|
+
Dir.chdir(target_path) do
|
19
|
+
options = {}
|
20
|
+
options[:git] = url
|
21
|
+
options[:commit] = `git rev-parse HEAD`.chomp
|
22
|
+
options
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
executable :git
|
29
|
+
|
30
|
+
def download!
|
31
|
+
create_cache if use_cache? && !cache_exist?
|
32
|
+
if options[:tag]
|
33
|
+
download_tag
|
34
|
+
elsif options[:branch]
|
35
|
+
download_branch
|
36
|
+
elsif options[:commit]
|
37
|
+
download_commit
|
38
|
+
else
|
39
|
+
download_head!
|
40
|
+
end
|
41
|
+
Dir.chdir(target_path) { git! "submodule update --init" } if options[:submodules]
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [void] Checkouts the HEAD of the git source in the destination
|
45
|
+
# path.
|
46
|
+
#
|
47
|
+
def download_head!
|
48
|
+
update_cache if use_cache?
|
49
|
+
clone(clone_url, target_path)
|
50
|
+
Dir.chdir(target_path) { git! "submodule update --init" } if options[:submodules]
|
51
|
+
end
|
52
|
+
|
53
|
+
#--------------------------------------#
|
54
|
+
|
55
|
+
# @!group Download implementations
|
56
|
+
|
57
|
+
# @return [Pathname] The clone URL, which resolves to the cache path.
|
58
|
+
#
|
59
|
+
def clone_url
|
60
|
+
use_cache? ? cache_path : url
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [void] Convenience method to perform clones operations.
|
64
|
+
#
|
65
|
+
def clone(from, to)
|
66
|
+
ui_sub_action("Cloning to Pods folder") do
|
67
|
+
git! %Q|clone "#{from}" "#{to}"|
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [void] Checkouts a specific tag of the git source in the
|
72
|
+
# destination path.
|
73
|
+
#
|
74
|
+
# @note Tags trigger a cache update unless aggressive cache is
|
75
|
+
# specified.
|
76
|
+
#
|
77
|
+
# @note Git fetch and checkout output to standard error and thus they
|
78
|
+
# are redirected to stdout.
|
79
|
+
#
|
80
|
+
def download_tag
|
81
|
+
if use_cache?
|
82
|
+
if aggressive_cache?
|
83
|
+
ensure_ref_exists(options[:tag])
|
84
|
+
else
|
85
|
+
update_cache
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
Dir.chdir(target_path) do
|
90
|
+
git! "init"
|
91
|
+
git! "remote add origin '#{clone_url}'"
|
92
|
+
git! "fetch origin tags/#{options[:tag]} 2>&1"
|
93
|
+
git! "reset --hard FETCH_HEAD"
|
94
|
+
git! "checkout -b activated-pod-commit 2>&1"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# @return [void] Checkouts a specific commit of the git source in the
|
99
|
+
# destination path.
|
100
|
+
#
|
101
|
+
# @note Git checkouts output to standard error and thus it is
|
102
|
+
# redirected to stdout.
|
103
|
+
#
|
104
|
+
def download_commit
|
105
|
+
ensure_ref_exists(options[:commit]) if use_cache?
|
106
|
+
clone(clone_url, target_path)
|
107
|
+
Dir.chdir(target_path) do
|
108
|
+
git! "checkout -b activated-pod-commit #{options[:commit]} 2>&1"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# @return [void] Checkouts the HEAD of a specific branch of the git
|
113
|
+
# source in the destination path.
|
114
|
+
#
|
115
|
+
# @note Git checkouts output to standard error and thus it is
|
116
|
+
# redirected to stdout.
|
117
|
+
#
|
118
|
+
def download_branch
|
119
|
+
update_cache if use_cache?
|
120
|
+
clone(clone_url, target_path)
|
121
|
+
Dir.chdir(target_path) do
|
122
|
+
git! "remote add upstream '#{@url}'" # we need to add the original url, not the cache url
|
123
|
+
git! "fetch -q upstream" # refresh the branches
|
124
|
+
git! "checkout --track -b activated-pod-commit upstream/#{options[:branch]} 2>&1" # create a new tracking branch
|
125
|
+
ui_message("Just downloaded and checked out branch: #{options[:branch]} from upstream #{clone_url}")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
#--------------------------------------#
|
130
|
+
|
131
|
+
# @!group Checking references
|
132
|
+
|
133
|
+
# @return [Bool] Whether a reference (commit SHA or tag)
|
134
|
+
#
|
135
|
+
def ref_exists?(ref)
|
136
|
+
if cache_exist?
|
137
|
+
Dir.chdir(cache_path) { git "rev-list --max-count=1 #{ref}" }
|
138
|
+
$? == 0
|
139
|
+
else
|
140
|
+
false
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# @return [void] Checks if a reference exists in the cache and updates
|
145
|
+
# only if necessary.
|
146
|
+
#
|
147
|
+
# @raise If after the update the reference can't be found.
|
148
|
+
#
|
149
|
+
def ensure_ref_exists(ref)
|
150
|
+
return if ref_exists?(ref)
|
151
|
+
update_cache
|
152
|
+
raise DownloaderError, "Cache unable to find git reference `#{ref}' for `#{url}'." unless ref_exists?(ref)
|
153
|
+
end
|
154
|
+
|
155
|
+
# @return [Bool] Whether a branch exists in the cache.
|
156
|
+
#
|
157
|
+
def branch_exists?(branch)
|
158
|
+
Dir.chdir(cache_path) { git "branch --all | grep #{branch}$" } # check for remote branch and do suffix matching ($ anchor)
|
159
|
+
$? == 0
|
160
|
+
end
|
161
|
+
|
162
|
+
# @return [void] Checks if a branch exists in the cache and updates
|
163
|
+
# only if necessary.
|
164
|
+
#
|
165
|
+
# @raise If after the update the branch can't be found.
|
166
|
+
#
|
167
|
+
def ensure_remote_branch_exists(branch)
|
168
|
+
return if branch_exists?(branch)
|
169
|
+
update_cache
|
170
|
+
raise DownloaderError, "Cache unable to find git reference `#{branch}' for `#{url}' (#{$?})." unless branch_exists?(branch)
|
171
|
+
end
|
172
|
+
|
173
|
+
#--------------------------------------#
|
174
|
+
|
175
|
+
# @!group Cache
|
176
|
+
|
177
|
+
# @return [Bool] Whether the cache exits.
|
178
|
+
#
|
179
|
+
# @note The previous implementation of the cache didn't use a barebone
|
180
|
+
# git repo. This method takes into account this fact and checks
|
181
|
+
# that the cache is actually a barebone repo. If the cache was
|
182
|
+
# not barebone it will be deleted and recreated.
|
183
|
+
#
|
184
|
+
def cache_exist?
|
185
|
+
cache_path.exist? &&
|
186
|
+
cache_origin_url(cache_path).to_s == url.to_s &&
|
187
|
+
Dir.chdir(cache_path) { git("config core.bare").chomp == "true" }
|
188
|
+
end
|
189
|
+
|
190
|
+
# @return [String] The origin URL of the cache with the given directory.
|
191
|
+
#
|
192
|
+
# @param [String] dir The directory of the cache.
|
193
|
+
#
|
194
|
+
def cache_origin_url(dir)
|
195
|
+
Dir.chdir(dir) { `git config remote.origin.url`.chomp }
|
196
|
+
end
|
197
|
+
|
198
|
+
# @return [void] Creates the barebone repo that will serve as the cache
|
199
|
+
# for the current repo.
|
200
|
+
#
|
201
|
+
def create_cache
|
202
|
+
ui_sub_action("Creating cache git repo (#{cache_path})") do
|
203
|
+
cache_path.rmtree if cache_path.exist?
|
204
|
+
cache_path.mkpath
|
205
|
+
git! %Q|clone --mirror "#{url}" "#{cache_path}"|
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# @return [void] Updates the barebone repo used as a cache against its
|
210
|
+
# remote creating it if needed.
|
211
|
+
#
|
212
|
+
def update_cache
|
213
|
+
if cache_exist?
|
214
|
+
ui_sub_action("Updating cache git repo (#{cache_path})") do
|
215
|
+
Dir.chdir(cache_path) { git! "remote update" }
|
216
|
+
end
|
217
|
+
else
|
218
|
+
create_cache
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
#---------------------------------------------------------------------------#
|
224
|
+
|
225
|
+
# Allows to download tarballs from GitHub.
|
226
|
+
#
|
227
|
+
class GitHub < Git
|
228
|
+
|
229
|
+
require 'open-uri'
|
230
|
+
|
231
|
+
def download_head!
|
232
|
+
download_only? ? download_and_extract_tarball('master') : super
|
233
|
+
end
|
234
|
+
|
235
|
+
def download_tag
|
236
|
+
download_only? ? download_and_extract_tarball(options[:tag]) : super
|
237
|
+
end
|
238
|
+
|
239
|
+
def download_commit
|
240
|
+
download_only? ? download_and_extract_tarball(options[:commit]) : super
|
241
|
+
end
|
242
|
+
|
243
|
+
def download_branch
|
244
|
+
download_only? ? download_and_extract_tarball(options[:branch]) : super
|
245
|
+
end
|
246
|
+
|
247
|
+
def tarball_url_for(id)
|
248
|
+
original_url, username, reponame = *(url.match(/[:\/]([\w\-]+)\/([\w\-]+)\.git/))
|
249
|
+
"https://github.com/#{username}/#{reponame}/tarball/#{id}"
|
250
|
+
end
|
251
|
+
|
252
|
+
def tmp_path
|
253
|
+
target_path + "tarball.tar.gz"
|
254
|
+
end
|
255
|
+
|
256
|
+
private
|
257
|
+
|
258
|
+
def download_only?
|
259
|
+
@options[:download_only]
|
260
|
+
end
|
261
|
+
|
262
|
+
def download_and_extract_tarball(id)
|
263
|
+
File.open(tmp_path, "w+") do |tmpfile|
|
264
|
+
open tarball_url_for(id) do |archive|
|
265
|
+
tmpfile.write Zlib::GzipReader.new(archive).read
|
266
|
+
end
|
267
|
+
|
268
|
+
system "tar xf #{tmpfile.path} -C #{target_path} --strip-components 1"
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
|
3
|
+
module Pod
|
4
|
+
module Downloader
|
5
|
+
class Http < Base
|
6
|
+
|
7
|
+
def self.options
|
8
|
+
[:type, :flatten]
|
9
|
+
end
|
10
|
+
|
11
|
+
class UnsupportedFileTypeError < StandardError; end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
executable :curl
|
16
|
+
executable :unzip
|
17
|
+
executable :tar
|
18
|
+
|
19
|
+
attr_accessor :filename, :download_path
|
20
|
+
|
21
|
+
def download!
|
22
|
+
@filename = filename_with_type(type)
|
23
|
+
@download_path = target_path + @filename
|
24
|
+
download_file(@download_path)
|
25
|
+
extract_with_type(@download_path, type)
|
26
|
+
end
|
27
|
+
|
28
|
+
def download_head!
|
29
|
+
download!
|
30
|
+
end
|
31
|
+
|
32
|
+
def type
|
33
|
+
options[:type] || type_with_url(url)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @note The archive is flattened if it contains only one folder and its
|
37
|
+
# extension is either `tgz`, `tar`, `tbz` or the options specify
|
38
|
+
# it.
|
39
|
+
#
|
40
|
+
# @return [Bool] Whether the archive should be flattened if it contains
|
41
|
+
# only one folder.
|
42
|
+
#
|
43
|
+
def should_flatten?
|
44
|
+
if options.has_key?(:flatten)
|
45
|
+
true
|
46
|
+
elsif [:tgz, :tar, :tbz].include?(type)
|
47
|
+
true # those archives flatten by default
|
48
|
+
else
|
49
|
+
false # all others (actually only .zip) default not to flatten
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def type_with_url(url)
|
54
|
+
if url =~ /.zip$/
|
55
|
+
:zip
|
56
|
+
elsif url =~ /.(tgz|tar\.gz)$/
|
57
|
+
:tgz
|
58
|
+
elsif url =~ /.tar$/
|
59
|
+
:tar
|
60
|
+
elsif url =~ /.(tbz|tar\.bz2)$/
|
61
|
+
:tbz
|
62
|
+
else
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def filename_with_type(type=:zip)
|
68
|
+
case type
|
69
|
+
when :zip
|
70
|
+
"file.zip"
|
71
|
+
when :tgz
|
72
|
+
"file.tgz"
|
73
|
+
when :tar
|
74
|
+
"file.tar"
|
75
|
+
when :tbz
|
76
|
+
"file.tbz"
|
77
|
+
else
|
78
|
+
raise UnsupportedFileTypeError.new "Unsupported file type: #{type}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def download_file(full_filename)
|
83
|
+
curl! "-L -o '#{full_filename}' '#{url}'"
|
84
|
+
end
|
85
|
+
|
86
|
+
def extract_with_type(full_filename, type=:zip)
|
87
|
+
case type
|
88
|
+
when :zip
|
89
|
+
unzip! "'#{full_filename}' -d '#{target_path}'"
|
90
|
+
when :tgz
|
91
|
+
tar! "xfz '#{full_filename}' -C '#{target_path}'"
|
92
|
+
when :tar
|
93
|
+
tar! "xf '#{full_filename}' -C '#{target_path}'"
|
94
|
+
when :tbz
|
95
|
+
tar! "xfj '#{full_filename}' -C '#{target_path}'"
|
96
|
+
else
|
97
|
+
raise UnsupportedFileTypeError.new "Unsupported file type: #{type}"
|
98
|
+
end
|
99
|
+
|
100
|
+
# If the archive is a tarball and it only contained a folder, move its contents to the target (#727)
|
101
|
+
if should_flatten?
|
102
|
+
contents = target_path.children
|
103
|
+
contents.delete(full_filename)
|
104
|
+
entry = contents.first
|
105
|
+
if contents.count == 1 && entry.directory?
|
106
|
+
FileUtils.move(entry.children, target_path)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Pod
|
2
|
+
module Downloader
|
3
|
+
class Mercurial < Base
|
4
|
+
|
5
|
+
def self.options
|
6
|
+
[:revision]
|
7
|
+
end
|
8
|
+
|
9
|
+
def options_specific?
|
10
|
+
!options[:revision].nil?
|
11
|
+
end
|
12
|
+
|
13
|
+
def checkout_options
|
14
|
+
Dir.chdir(target_path) do
|
15
|
+
options = {}
|
16
|
+
options[:hg] = url
|
17
|
+
options[:revision] = `hg --debug id -i`.chomp
|
18
|
+
options
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
executable :hg
|
25
|
+
|
26
|
+
def download!
|
27
|
+
if options[:revision]
|
28
|
+
download_revision!
|
29
|
+
else
|
30
|
+
download_head!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def download_head!
|
35
|
+
hg! %|clone "#{url}" "#{target_path}"|
|
36
|
+
end
|
37
|
+
|
38
|
+
def download_revision!
|
39
|
+
hg! %|clone "#{url}" --rev '#{options[:revision]}' "#{target_path}"|
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Pod
|
2
|
+
module Downloader
|
3
|
+
class Subversion < Base
|
4
|
+
|
5
|
+
def self.options
|
6
|
+
[:revision, :tag, :folder]
|
7
|
+
end
|
8
|
+
|
9
|
+
def options_specific?
|
10
|
+
!options[:revision].nil? || !options[:tag].nil?
|
11
|
+
end
|
12
|
+
|
13
|
+
def checkout_options
|
14
|
+
Dir.chdir(target_path) do
|
15
|
+
options = {}
|
16
|
+
options[:svn] = url
|
17
|
+
options[:revision] = @exported_revision
|
18
|
+
options
|
19
|
+
end
|
20
|
+
end
|
21
|
+
private
|
22
|
+
|
23
|
+
executable :svn
|
24
|
+
|
25
|
+
def download!
|
26
|
+
output = svn!(%|#{export_subcommand} "#{reference_url}" "#{target_path}"|)
|
27
|
+
store_exported_revision(output)
|
28
|
+
end
|
29
|
+
|
30
|
+
def download_head!
|
31
|
+
output = svn!(%|#{export_subcommand} "#{trunk_url}" "#{target_path}"|)
|
32
|
+
store_exported_revision(output)
|
33
|
+
end
|
34
|
+
|
35
|
+
def store_exported_revision(output)
|
36
|
+
output.match(/Exported revision ([0-9]+)\./)
|
37
|
+
@exported_revision = $1
|
38
|
+
end
|
39
|
+
|
40
|
+
def export_subcommand
|
41
|
+
result = 'export --non-interactive --trust-server-cert --force'
|
42
|
+
end
|
43
|
+
|
44
|
+
def reference_url
|
45
|
+
result = url.dup
|
46
|
+
result << '/' << options[:folder] if options[:folder]
|
47
|
+
result << '/tags/' << options[:tag] if options[:tag]
|
48
|
+
result << '" -r "' << options[:revision] if options[:revision]
|
49
|
+
result
|
50
|
+
end
|
51
|
+
|
52
|
+
def trunk_url
|
53
|
+
result = url.dup
|
54
|
+
result << '/' << options[:folder] if options[:folder]
|
55
|
+
result << '/trunk'
|
56
|
+
result
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cocoapods-downloader
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eloy Duran
|
8
|
+
- Fabio Pelosin
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-25 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description:
|
15
|
+
email:
|
16
|
+
- eloy.de.enige@gmail.com
|
17
|
+
- fabiopelosin@gmail.com
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- lib/cocoapods-downloader/api.rb
|
23
|
+
- lib/cocoapods-downloader/api_exposable.rb
|
24
|
+
- lib/cocoapods-downloader/base.rb
|
25
|
+
- lib/cocoapods-downloader/gem_version.rb
|
26
|
+
- lib/cocoapods-downloader/git.rb
|
27
|
+
- lib/cocoapods-downloader/http.rb
|
28
|
+
- lib/cocoapods-downloader/mercurial.rb
|
29
|
+
- lib/cocoapods-downloader/subversion.rb
|
30
|
+
- lib/cocoapods-downloader.rb
|
31
|
+
- README.markdown
|
32
|
+
- LICENSE
|
33
|
+
homepage: https://github.com/CocoaPods/Downloader
|
34
|
+
licenses:
|
35
|
+
- MIT
|
36
|
+
metadata: {}
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
requirements: []
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 2.0.0
|
54
|
+
signing_key:
|
55
|
+
specification_version: 3
|
56
|
+
summary: A small library for downloading files from remotes in a folder.
|
57
|
+
test_files: []
|
58
|
+
has_rdoc:
|