cocoapods-downloader 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
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
|
+
[![Build Status](https://travis-ci.org/CocoaPods/cocoapods-downloader.png?branch=master)](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:
|