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.

@@ -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
+
@@ -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,10 @@
1
+ module Pod
2
+ module Downloader
3
+
4
+ # @return [String] Downloader’s version, following
5
+ # [semver](http://semver.org).
6
+ #
7
+ VERSION = '0.1.0'
8
+
9
+ end
10
+ 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: