capistrano-distribution 0.2.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 51e2dcb1ba15f838b76d6eaf72e6f451c793f32b
4
+ data.tar.gz: 1cb882b4e420dc6c2292e897657f681b831d5515
5
+ SHA512:
6
+ metadata.gz: 8f0275b355b9ee5311373aff3b70640ddb381fecd3dd3b6b57b9eab3817e17217a2c25667b53047dc69aa8fc3065050203fe7d828026c2f4bcfa1dc63ac2788c
7
+ data.tar.gz: 20a82ec2110fe7bfebd8ae1dfcd149e249f75332b1cde1d1fbeb4ac86afa6df2dff926958b2473b26c19882627f13414a02bc954540bc4cda5d87a2b5b70b78d
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --protected --private abstract lib/**/*.rb - README.md NEWS.md LICENSE
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2014 Spiceworks, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/NEWS.md ADDED
@@ -0,0 +1,35 @@
1
+ # News and Notifications by Version
2
+
3
+ This file lists noteworthy changes which may affect users of this project. More
4
+ detailed information is available in the rest of the documentation.
5
+
6
+ **NOTE:** Date stamps in the following entries are in YYYY/MM/DD format.
7
+
8
+
9
+ ## v0.2.0 (2016/02/29)
10
+
11
+ * Provide a setting (`:distribution_runner_opts`) to configure runner options.
12
+ * Must be a hash of options acceptable by the `on` method of sshkit.
13
+ * Git distributables can now change the repo URL without manual intervention.
14
+ * Pruning of dead Git branches now happens during repository update.
15
+ * Avoids too many dead branches causing updates to run forever.
16
+
17
+ ## v0.1.0 (2016/02/09)
18
+
19
+ * Incompatible changes
20
+ * The `distribution` setting is now a stack of distributor definitions to be
21
+ applied in order as a series of overlays in the release area.
22
+ * Provides the `distribution:set_current_revision` task used by newer
23
+ Capistrano releases.
24
+ * A `release_id` setting is required to provide a reasonable value.
25
+
26
+ ## v0.0.2 (2014/03/17)
27
+
28
+ * Ensure that the internal_path attribute of AbstractArchiver is a Pathname
29
+ * Fixes extraction of ZIP archives.
30
+ * Silence curl and unzip in the CurlZip distributor
31
+ * Silence unzip in the Zip distributor
32
+
33
+ ## v0.0.1 (2014/02/27)
34
+
35
+ * Birthday
data/README.md ADDED
@@ -0,0 +1,165 @@
1
+ # Capistrano Distribution: More Than Just Git Deployments
2
+
3
+ Flexible distribution for Capistrano 3.x.
4
+
5
+ ## LINKS
6
+
7
+ * Homepage :: http://github.com/spiceworks/capistrano-distribution
8
+ * Documentation :: http://rdoc.info/gems/capistrano-distribution/frames
9
+ * Source :: http://github.com/spiceworks/capistrano-distribution
10
+
11
+ ## DESCRIPTION
12
+
13
+ This gem hooks into the SCM functionality of Capistrano 3.x in order to provide
14
+ a more generic distribution strategy. Not only is it possible to deploy from a
15
+ simple Git repository, but deployment of contents from Tar and ZIP files is also
16
+ possible. It is even easy to create deployments based on multiple sources.
17
+
18
+ With the exception of the Git push method, all distribution operations pull
19
+ content from providers on each target host. For Git-based distributions
20
+ whether using the push or pull method, this is very efficient after the first
21
+ deployment because a mirror git repository is saved on each host such that only
22
+ differentials are needed for future deployments. All other types perform a
23
+ full download of the source artifact, so these could be expensive and/or slow.
24
+
25
+ ## Features
26
+
27
+ * Distribute from Git repositories and Tar/Zip archives.
28
+ * Use remote and local sources, anything supported by Git and Curl.
29
+ * Create homogenous deployments from multiple sources.
30
+
31
+ ## Known Bugs/Limitations
32
+
33
+ * None so far...
34
+
35
+ ## SYNOPSIS
36
+
37
+ Simple Git-based distribution (master branch of the current repository):
38
+
39
+ ```ruby
40
+ # config/deploy.rb
41
+ ...
42
+ set :scm, :distribution
43
+ set :release_id, ->{ `git rev-parse HEAD`.chomp }
44
+ set :distribution, ->{
45
+ [[
46
+ 'http://example.com/repositories/example.git',
47
+ fetch(:release_id)
48
+ ]]
49
+ }
50
+ ...
51
+ ```
52
+
53
+ Simple Tar-based distribution (everything in the `example.tar.gz` archive is
54
+ within an `example` subdirectory):
55
+
56
+ ```ruby
57
+ # config/deploy.rb
58
+ ...
59
+ set :scm, :distribution
60
+ set :release_id, ->{ "example-#{fetch(:release_timestamp)}" }
61
+ set :distribution, 'http://example.com/tarballs/example.tar.gz'
62
+ ...
63
+ ```
64
+
65
+ Heterogenous distribution (a Redmine deployment with plugins):
66
+
67
+ ```ruby
68
+ # config/deploy.rb
69
+ ...
70
+ set :scm, :distribution
71
+ set :release_id, ->{ "2.3.4-#{fetch(:release_timestamp)}" }
72
+ set :distribution,
73
+ [
74
+ 'http://www.redmine.org/releases/redmine-2.3.4.tar.gz'
75
+ ],
76
+ [
77
+ 'https://bitbucket.org/akiko_pusu/redmine_issue_templates/get/0.0.4.tar.gz',
78
+ {
79
+ subtree: 'akiko_pusu-redmine_issue_templates-b885dfe8263d',
80
+ target: 'plugins/redmine_issue_templates'
81
+ }
82
+ ],
83
+ [
84
+ 'https://bitbucket.org/haru_iida/redmine_code_review/get/0.6.2.tar.gz',
85
+ {
86
+ subtree: 'haru_iida-redmine_code_review-e79f98b8a77f',
87
+ target: 'plugins/redmine_code_review'
88
+ }
89
+ ],
90
+ [
91
+ 'https://github.com/Undev/notify_custom_users/archive/0.0.5.tar.gz',
92
+ {
93
+ subtree: 'notify_custom_users-0.0.5',
94
+ target: 'plugins/notify_custom_users'
95
+ }
96
+ ],
97
+ [
98
+ 'https://github.com/thorin/redmine_ldap_sync/archive/2.0.3.tar.gz',
99
+ {
100
+ subtree: 'redmine_ldap_sync-2.0.3',
101
+ target: 'plugins/redmine_ldap_sync'
102
+ }
103
+ ]
104
+ ...
105
+ ```
106
+
107
+ ## REQUIREMENTS
108
+
109
+ * Git binary (for Git repositories)
110
+ * Curl binary (for remote archives)
111
+ * Unzip binary (for ZIP archives)
112
+ * Tar binary (for Tar archives)
113
+
114
+ ## DEVELOPERS
115
+
116
+ After checking out the source, run:
117
+
118
+ $ bundle install
119
+ $ bundle exec rake test yard
120
+
121
+ This will install all dependencies, run the tests/specs, and generate the
122
+ documentation.
123
+
124
+ ## AUTHORS
125
+
126
+ Thanks to all contributors. Without your help this project would not exist.
127
+
128
+ * Jeremy Bopp :: jeremyb@spiceworks.com
129
+
130
+ ## Contributing
131
+
132
+ Contributions for bug fixes, documentation, extensions, tests, etc. are
133
+ encouraged.
134
+
135
+ 1. Clone the repository.
136
+ 2. Fix a bug or add a feature.
137
+ 3. Add tests for the fix or feature.
138
+ 4. Make a pull request.
139
+
140
+ ## LICENSE
141
+
142
+ ```
143
+ (The MIT License)
144
+
145
+ Copyright (c) 2014 Spiceworks, Inc.
146
+
147
+ Permission is hereby granted, free of charge, to any person obtaining
148
+ a copy of this software and associated documentation files (the
149
+ 'Software'), to deal in the Software without restriction, including
150
+ without limitation the rights to use, copy, modify, merge, publish,
151
+ distribute, sublicense, and/or sell copies of the Software, and to
152
+ permit persons to whom the Software is furnished to do so, subject to
153
+ the following conditions:
154
+
155
+ The above copyright notice and this permission notice shall be
156
+ included in all copies or substantial portions of the Software.
157
+
158
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
159
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
160
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
161
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
162
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
163
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
164
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
165
+ ```
data/Rakefile ADDED
@@ -0,0 +1,243 @@
1
+ # encoding: UTF-8
2
+ # -*- ruby -*-
3
+
4
+ require 'rubygems'
5
+
6
+ require 'erb'
7
+ require 'rake/testtask'
8
+ require 'rubygems/package_task'
9
+ require 'rake/clean'
10
+ require 'yard'
11
+
12
+ # Load the gemspec file for this project.
13
+ GEMSPEC = Dir['*.gemspec'].first
14
+ SPEC = eval(File.read(GEMSPEC), nil, GEMSPEC)
15
+
16
+ # The path to the version.rb file and a string to eval to find the version.
17
+ VERSION_RB = "lib/#{SPEC.name.gsub('-', '/')}/version.rb"
18
+ VERSION_REF =
19
+ "#{SPEC.name.split('-').map { |p| p.split('_').map(&:capitalize).join }.join('::')}::VERSION"
20
+
21
+ # A dynamically generated list of files that should match the manifest (the
22
+ # combined contents of SPEC.files and SPEC.test_files). The idea is for this
23
+ # list to contain all project files except for those that have been explicitly
24
+ # excluded. This list will be compared with the manifest from the SPEC in order
25
+ # to help catch the addition or removal of files to or from the project that
26
+ # have not been accounted for either by an exclusion here or an inclusion in the
27
+ # SPEC manifest.
28
+ #
29
+ # NOTE:
30
+ # It is critical that the manifest is *not* automatically generated via globbing
31
+ # and the like; otherwise, this will yield a simple comparison between
32
+ # redundantly generated lists of files that probably will not protect the
33
+ # project from the unintentional inclusion or exclusion of files in the
34
+ # distribution.
35
+ PKG_FILES = FileList.new(Dir.glob('**/*', File::FNM_DOTMATCH)) do |files|
36
+ # Exclude anything that doesn't exist as well as directories.
37
+ files.exclude {|file| ! File.exist?(file) || File.directory?(file)}
38
+ # Exclude Git administrative files.
39
+ files.exclude(%r{(^|[/\\])\.git(ignore|modules|keep)?([/\\]|$)})
40
+ # Exclude editor swap/temporary files.
41
+ files.exclude('**/.*.sw?')
42
+ # Exclude the gemspec file.
43
+ files.exclude(GEMSPEC)
44
+ # Exclude the README template file.
45
+ files.exclude('README.md.erb')
46
+ # Exclude resources for bundler.
47
+ files.exclude('Gemfile', 'Gemfile.lock')
48
+ files.exclude(%r{^.bundle([/\\]|$)})
49
+ files.exclude(%r{^vendor/bundle([/\\]|$)})
50
+ # Exclude generated content, except for the README file.
51
+ files.exclude(%r{^(pkg|doc|.yardoc)([/\\]|$)})
52
+ # Exclude Rubinius compiled Ruby files.
53
+ files.exclude('**/*.rbc')
54
+ end
55
+
56
+ # Make sure that :clean and :clobber will not whack the repository files.
57
+ CLEAN.exclude('.git/**')
58
+ # Vim swap files are fair game for clean up.
59
+ CLEAN.include('**/.*.sw?')
60
+
61
+ # Returns the value of the VERSION environment variable as a Gem::Version object
62
+ # assuming it is set and a valid Gem version string. Otherwise, raises an
63
+ # exception.
64
+ def get_version_argument
65
+ version = ENV['VERSION']
66
+ if version.to_s.empty?
67
+ raise "No version specified: Add VERSION=X.Y.Z to the command line"
68
+ end
69
+ begin
70
+ Gem::Version.create(version.dup)
71
+ rescue ArgumentError
72
+ raise "Invalid version specified in `VERSION=#{version}'"
73
+ end
74
+ end
75
+
76
+ # Performs an in place, per line edit of the file indicated by _path_ by calling
77
+ # the sub method on each line and passing _pattern_, _replacement_, and _b_ as
78
+ # arguments.
79
+ def file_sub(path, pattern, replacement = nil, &b)
80
+ tmp_path = "#{path}.tmp"
81
+ File.open(path) do |infile|
82
+ File.open(tmp_path, 'w') do |outfile|
83
+ infile.each do |line|
84
+ outfile.write(line.sub(pattern, replacement, &b))
85
+ end
86
+ end
87
+ end
88
+ File.rename(tmp_path, path)
89
+ end
90
+
91
+ # Updates the version string in the gemspec file and a version.rb file it to the
92
+ # string in _version_.
93
+ def set_version(version)
94
+ file_sub(GEMSPEC, /(\.version\s*=\s*).*/, "\\1'#{version}'")
95
+ file_sub(VERSION_RB, /^(\s*VERSION\s*=\s*).*/, "\\1'#{version}'")
96
+ end
97
+
98
+ # Returns a string that is line wrapped at word boundaries, where each line is
99
+ # no longer than _line_width_ characters.
100
+ #
101
+ # This is mostly lifted directly from ActionView::Helpers::TextHelper.
102
+ def word_wrap(text, line_width = 80)
103
+ text.split("\n").collect do |line|
104
+ line.length > line_width ?
105
+ line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip :
106
+ line
107
+ end * "\n"
108
+ end
109
+
110
+ desc 'Alias for build:gem'
111
+ task :build => 'build:gem'
112
+
113
+ # Build related tasks.
114
+ namespace :build do
115
+ # Create the gem and package tasks.
116
+ Gem::PackageTask.new(SPEC).define
117
+
118
+ # Ensure that the that the manifest is consulted when building the gem. Any
119
+ # generated/compiled files should be available at that time.
120
+ task :gem => :check_manifest
121
+
122
+ desc 'Verify the manifest'
123
+ task :check_manifest do
124
+ manifest_files = (SPEC.files + SPEC.test_files).sort.uniq
125
+ pkg_files = PKG_FILES.sort.uniq
126
+ if manifest_files != pkg_files then
127
+ common_files = manifest_files & pkg_files
128
+ manifest_files -= common_files
129
+ pkg_files -= common_files
130
+ message = ["The manifest does not match the automatic file list."]
131
+ unless manifest_files.empty? then
132
+ message << " Extraneous files:\n " + manifest_files.join("\n ")
133
+ end
134
+ unless pkg_files.empty?
135
+ message << " Missing files:\n " + pkg_files.join("\n ")
136
+ end
137
+ raise message.join("\n")
138
+ end
139
+ end
140
+
141
+ # Creates the README.md file from a template and the gemspec contents.
142
+ file 'README.md' => ['README.md.erb', GEMSPEC] do
143
+ spec = SPEC
144
+ File.open('README.md', 'w') do |readme|
145
+ readme.write(
146
+ ERB.new(File.read('README.md.erb'), nil, '-').result(binding)
147
+ )
148
+ end
149
+ end
150
+ end
151
+
152
+ # Ensure that the clobber task also clobbers package files.
153
+ task :clobber => 'build:clobber_package'
154
+
155
+ # Create the documentation task.
156
+ YARD::Rake::YardocTask.new
157
+ # Ensure that the README file is (re)generated first.
158
+ task :yard => 'README.md'
159
+
160
+ # Gem related tasks.
161
+ namespace :gem do
162
+ desc 'Alias for build:gem'
163
+ task :build => 'build:gem'
164
+
165
+ desc 'Publish the gemfile'
166
+ task :publish => ['version:check', :test, 'repo:tag', :build] do
167
+ sh "gem push pkg/#{SPEC.name}-#{SPEC.version}*.gem"
168
+ end
169
+ end
170
+
171
+ Rake::TestTask.new do |t|
172
+ t.pattern = 'spec/**/*_spec.rb'
173
+ end
174
+
175
+ # Version string management tasks.
176
+ namespace :version do
177
+ desc 'Set the version for the project to a specified version'
178
+ task :set do
179
+ set_version(get_version_argument)
180
+ end
181
+
182
+ desc 'Set the version for the project back to 0.0.0'
183
+ task :reset do
184
+ set_version('0.0.0')
185
+ end
186
+
187
+ desc 'Check that all version strings are correctly set'
188
+ task :check => ['version:check:spec', 'version:check:version_rb', 'version:check:news']
189
+
190
+ namespace :check do
191
+ desc 'Check that the version in the gemspec is correctly set'
192
+ task :spec do
193
+ version = get_version_argument
194
+ if version != SPEC.version
195
+ raise "The given version `#{version}' does not match the gemspec version `#{SPEC.version}'"
196
+ end
197
+ end
198
+
199
+ desc 'Check that the version in the version.rb file is correctly set'
200
+ task :version_rb do
201
+ version = get_version_argument
202
+ begin
203
+ load VERSION_RB
204
+ internal_version = Gem::Version.create(eval(VERSION_REF))
205
+ if version != internal_version
206
+ raise "The given version `#{version}' does not match the version.rb version `#{internal_version}'"
207
+ end
208
+ rescue ArgumentError
209
+ raise "Invalid version specified in `#{VERSION_RB}'"
210
+ end
211
+ end
212
+
213
+ desc 'Check that the NEWS.md file mentions the version'
214
+ task :news do
215
+ version = get_version_argument
216
+ begin
217
+ File.open('NEWS.md') do |news|
218
+ unless news.each_line.any? {|l| l =~ /^## v#{Regexp.escape(version.to_s)} /}
219
+ raise "The NEWS.md file does not mention version `#{version}'"
220
+ end
221
+ end
222
+ rescue Errno::ENOENT
223
+ raise 'No NEWS.md file found'
224
+ end
225
+ end
226
+ end
227
+ end
228
+
229
+ # Repository and workspace management tasks.
230
+ namespace :repo do
231
+ desc 'Tag the current HEAD with the version string'
232
+ task :tag => :check_workspace do
233
+ version = get_version_argument
234
+ sh "git tag -s -m 'Release v#{version}' v#{version}"
235
+ end
236
+
237
+ desc 'Ensure the workspace is fully committed and clean'
238
+ task :check_workspace => ['README.md'] do
239
+ unless `git status --untracked-files=all --porcelain`.empty?
240
+ raise 'Workspace has been modified. Commit pending changes and try again.'
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,85 @@
1
+ module Capistrano
2
+ class Distribution
3
+ module Distributor
4
+
5
+ ##
6
+ # @abstract Subclass and override {#check} and {#distribute} to create a
7
+ # distributor.
8
+ #
9
+ # An abstract distributor upon which all distributors should ultimately be
10
+ # based.
11
+ #
12
+ # See the existing concrete distributor definitions for examples.
13
+ class Abstract
14
+ ##
15
+ # @param context [(#test, #execute)] a Capistrano context used to run
16
+ # commands.
17
+ # @param url [URI, String] a URL to be used for fetching the artifact to be
18
+ # distributed
19
+ # @param opts [Hash] options to override default settings
20
+ # @option opts [String] :target ('') a path within the release area to be the
21
+ # root of the distribution.
22
+ def initialize(context, url, opts = {})
23
+ @context = context
24
+ @url = URI === url ? url : URI.parse(url)
25
+ @target = opts.fetch(:target, '')
26
+
27
+ @repo_id = Digest::SHA1.hexdigest(url.to_s)
28
+ end
29
+
30
+ ##
31
+ # @abstract Override to provide a meaningful check for prerequisites during
32
+ # deployment.
33
+ #
34
+ # @return [Boolean] +true+ when prerequisites are met and +false+
35
+ # otherwise.
36
+ def check
37
+ false
38
+ end
39
+
40
+ ##
41
+ # @abstract Override to provide meaningful distribution logic during
42
+ # deployment.
43
+ #
44
+ # @return [nil]
45
+ #
46
+ # @raise [exception] when distribution fails.
47
+ def distribute
48
+ end
49
+
50
+ private
51
+
52
+ ##
53
+ # The source URL for the artifact to be distributed.
54
+ attr_reader :url
55
+
56
+ ##
57
+ # The Capistrano context in which the distributor will operate.
58
+ attr_reader :context
59
+
60
+ ##
61
+ # A unique identifier for the distributor for use under {#repo_path}.
62
+ attr_reader :repo_id
63
+
64
+ ##
65
+ # A path relative to the release location in which to distribute the artifact.
66
+ attr_reader :target
67
+
68
+ ##
69
+ # @return [Pathname] a path to the release location in which to distribute the
70
+ # artifact based on {#target}
71
+ def release_path
72
+ context.release_path.join(target)
73
+ end
74
+
75
+ ##
76
+ # @return [Pathname] a path under the repo location to a unique workspace for
77
+ # the distributor
78
+ def repo_path
79
+ context.repo_path.join(repo_id)
80
+ end
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,60 @@
1
+ require 'pathname'
2
+
3
+ require 'capistrano/distribution/distributor/abstract'
4
+
5
+ module Capistrano
6
+ class Distribution
7
+ module Distributor
8
+
9
+ ##
10
+ # @abstract Subclass and override {#distribute} to create a distributor that
11
+ # extracts archives found on the local filesystem.
12
+ #
13
+ # A convenience class for distributors that extract an archive file.
14
+ class AbstractArchiver < Abstract
15
+ ##
16
+ # A regexp that matches file extentions typically found on archives used to
17
+ # distribute program binaries and source releases.
18
+ EXT_MATCHER = %r{(\.[^\d.]+[^.]*)+$}
19
+
20
+ ##
21
+ # @param context [{#test, #execute}] a Capistrano context used to run
22
+ # commands.
23
+ # @param url [URI, String] a URL to be used for fetching the artifact to be
24
+ # distributed
25
+ # @param opts [Hash] options to override default settings
26
+ # @option opts [String] :subtree a path within the archive to extract as if
27
+ # its contents were at the root of the archive
28
+ def initialize(context, url, opts = {})
29
+ super(context, url, opts)
30
+ @subtree = Pathname.new(
31
+ opts.fetch(:subtree, File.basename(url).sub(ext_matcher, ''))
32
+ )
33
+ end
34
+
35
+ ##
36
+ # Tests whether or not the archive indicated by {#url} is locally available.
37
+ #
38
+ # @return [Boolean] +true+ if the archive is available; otherwise, +false+.
39
+ #
40
+ # @see Abstract#check
41
+ def check
42
+ context.test '[', '-f', url.path, ']'
43
+ end
44
+
45
+ private
46
+
47
+ ##
48
+ # The path within the archive to extract. May be an empty string.
49
+ attr_reader :subtree
50
+
51
+ ##
52
+ # @return [Regexp] a regexp that matches file extentions.
53
+ def ext_matcher
54
+ EXT_MATCHER
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,28 @@
1
+ require 'capistrano/distribution/distributor/abstract_archiver'
2
+
3
+ module Capistrano
4
+ class Distribution
5
+ module Distributor
6
+
7
+ ##
8
+ # @abstract Subclass and override {#distribute} to create a distributor that
9
+ # extracts archives reachable via +curl+ command.
10
+ #
11
+ # A convenience class for distributors that extract an archive downloaded using
12
+ # the +curl+ command.
13
+ class AbstractCurl < AbstractArchiver
14
+ ##
15
+ # Tests whether or not the archive indicated by {#url} is available via the
16
+ # +curl+ command.
17
+ #
18
+ # @return [Boolean] +true+ if the archive is available; otherwise, +false+.
19
+ #
20
+ # @see Abstract#check
21
+ def check
22
+ context.test 'curl', '--fail', '--location', '--silent', '--head', '--request', 'GET', url
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end