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.

@@ -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: