travis_dpl_test 2.0.3.beta.4.ror
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +172 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +392 -0
- data/Gemfile +32 -0
- data/Gemfile.lock +611 -0
- data/LICENSE +19 -0
- data/README.md +2744 -0
- data/Rakefile +210 -0
- data/bin/dpl +11 -0
- data/config/transliterate.yml +733 -0
- data/dpl.gemspec +23 -0
- data/lib/dpl/assets/atlas/install +19 -0
- data/lib/dpl/assets/convox/install +11 -0
- data/lib/dpl/assets/dpl/README.erb.md +138 -0
- data/lib/dpl/assets/dpl/git_ssh +8 -0
- data/lib/dpl/assets/git/detect_private_key +8 -0
- data/lib/dpl/assets/hephy/filter_log +3 -0
- data/lib/dpl/assets/pypi/install +4 -0
- data/lib/dpl/assets/scalingo/install +6 -0
- data/lib/dpl/cli.rb +100 -0
- data/lib/dpl/ctx/bash.rb +549 -0
- data/lib/dpl/ctx/test.rb +255 -0
- data/lib/dpl/ctx.rb +4 -0
- data/lib/dpl/helper/assets.rb +38 -0
- data/lib/dpl/helper/cmd.rb +169 -0
- data/lib/dpl/helper/config_file.rb +49 -0
- data/lib/dpl/helper/cookbook_site_streaming_uploader.rb +249 -0
- data/lib/dpl/helper/env.rb +92 -0
- data/lib/dpl/helper/github.rb +22 -0
- data/lib/dpl/helper/interpolate.rb +160 -0
- data/lib/dpl/helper/memoize.rb +23 -0
- data/lib/dpl/helper/squiggle.rb +24 -0
- data/lib/dpl/helper/transliterate.rb +13 -0
- data/lib/dpl/helper/wrap.rb +11 -0
- data/lib/dpl/helper/zip.rb +71 -0
- data/lib/dpl/provider/dsl.rb +410 -0
- data/lib/dpl/provider/examples.rb +132 -0
- data/lib/dpl/provider/status.rb +61 -0
- data/lib/dpl/provider.rb +651 -0
- data/lib/dpl/providers/anynines.rb +71 -0
- data/lib/dpl/providers/azure_web_apps.rb +63 -0
- data/lib/dpl/providers/bintray.rb +324 -0
- data/lib/dpl/providers/bluemixcloudfoundry.rb +98 -0
- data/lib/dpl/providers/boxfuse.rb +52 -0
- data/lib/dpl/providers/cargo.rb +32 -0
- data/lib/dpl/providers/chef_supermarket.rb +132 -0
- data/lib/dpl/providers/cloud66.rb +46 -0
- data/lib/dpl/providers/cloudfiles.rb +62 -0
- data/lib/dpl/providers/cloudformation.rb +281 -0
- data/lib/dpl/providers/cloudfoundry.rb +89 -0
- data/lib/dpl/providers/codedeploy.rb +190 -0
- data/lib/dpl/providers/convox.rb +130 -0
- data/lib/dpl/providers/datica.rb +64 -0
- data/lib/dpl/providers/ecr.rb +129 -0
- data/lib/dpl/providers/elasticbeanstalk.rb +207 -0
- data/lib/dpl/providers/engineyard.rb +113 -0
- data/lib/dpl/providers/firebase.rb +45 -0
- data/lib/dpl/providers/flynn.rb +35 -0
- data/lib/dpl/providers/gae.rb +78 -0
- data/lib/dpl/providers/gcs.rb +132 -0
- data/lib/dpl/providers/git_push.rb +273 -0
- data/lib/dpl/providers/gleis.rb +74 -0
- data/lib/dpl/providers/hackage.rb +53 -0
- data/lib/dpl/providers/hephy.rb +107 -0
- data/lib/dpl/providers/heroku/api.rb +123 -0
- data/lib/dpl/providers/heroku/git.rb +54 -0
- data/lib/dpl/providers/heroku.rb +111 -0
- data/lib/dpl/providers/lambda.rb +211 -0
- data/lib/dpl/providers/launchpad.rb +80 -0
- data/lib/dpl/providers/netlify.rb +38 -0
- data/lib/dpl/providers/npm.rb +130 -0
- data/lib/dpl/providers/nuget.rb +41 -0
- data/lib/dpl/providers/openshift.rb +52 -0
- data/lib/dpl/providers/opsworks.rb +146 -0
- data/lib/dpl/providers/packagecloud.rb_ +194 -0
- data/lib/dpl/providers/pages/api.rb +106 -0
- data/lib/dpl/providers/pages/git.rb +262 -0
- data/lib/dpl/providers/pages.rb +18 -0
- data/lib/dpl/providers/puppetforge.rb +50 -0
- data/lib/dpl/providers/pypi.rb +125 -0
- data/lib/dpl/providers/releases.rb +234 -0
- data/lib/dpl/providers/rubygems.rb +97 -0
- data/lib/dpl/providers/s3.rb +251 -0
- data/lib/dpl/providers/scalingo.rb +69 -0
- data/lib/dpl/providers/script.rb +32 -0
- data/lib/dpl/providers/snap.rb +68 -0
- data/lib/dpl/providers/surge.rb +59 -0
- data/lib/dpl/providers/testfairy.rb +101 -0
- data/lib/dpl/providers/transifex.rb +72 -0
- data/lib/dpl/providers.rb +48 -0
- data/lib/dpl/string_ext.rb +23 -0
- data/lib/dpl/support/aws_sdk_patch.rb +26 -0
- data/lib/dpl/support/gems.rb +73 -0
- data/lib/dpl/support/gstore_patch.rb +8 -0
- data/lib/dpl/support/version.rb +84 -0
- data/lib/dpl/version.rb +5 -0
- data/lib/dpl.rb +23 -0
- data/status.json +237 -0
- metadata +161 -0
data/lib/dpl/provider.rb
ADDED
@@ -0,0 +1,651 @@
|
|
1
|
+
require 'cl'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'forwardable'
|
4
|
+
require 'shellwords'
|
5
|
+
require 'dpl/helper/assets'
|
6
|
+
require 'dpl/helper/cmd'
|
7
|
+
require 'dpl/helper/config_file'
|
8
|
+
require 'dpl/helper/env'
|
9
|
+
require 'dpl/helper/interpolate'
|
10
|
+
require 'dpl/helper/memoize'
|
11
|
+
require 'dpl/helper/squiggle'
|
12
|
+
require 'dpl/provider/dsl'
|
13
|
+
require 'dpl/provider/examples'
|
14
|
+
require 'dpl/version'
|
15
|
+
|
16
|
+
module Dpl
|
17
|
+
# Base class for all concrete providers that `dpl` supports.
|
18
|
+
#
|
19
|
+
# These are subclasses of `Cl::Cmd` which means they are going to be detected
|
20
|
+
# by the first argument passed to `dpl [provider]`, instantiated, and run.
|
21
|
+
#
|
22
|
+
# Implementors are encouraged to use the provider DSL to declare various
|
23
|
+
# features, requirements, and attributes that apply to their provider, to
|
24
|
+
# implement any of the following stages (methods) according to their needs
|
25
|
+
# and semantics:
|
26
|
+
#
|
27
|
+
# * init
|
28
|
+
# * install
|
29
|
+
# * login
|
30
|
+
# * setup
|
31
|
+
# * validate
|
32
|
+
# * prepare
|
33
|
+
# * deploy
|
34
|
+
# * finish
|
35
|
+
#
|
36
|
+
# The main logic should sit in the `deploy` stage.
|
37
|
+
#
|
38
|
+
# If at any time the method `error` is called, or any exception raised the
|
39
|
+
# deploy process will be halted, and subsequent stages skipped. However, the
|
40
|
+
# stage `finish` will run even if previous stages have raised an error,
|
41
|
+
# giving the provider the opportunity to potentially clean up stage.
|
42
|
+
#
|
43
|
+
# In addition to this the following methods will be called if implemented
|
44
|
+
# by the provider:
|
45
|
+
#
|
46
|
+
# * run_cmd
|
47
|
+
# * add_key
|
48
|
+
# * remove_key
|
49
|
+
#
|
50
|
+
# Like the `finish` stage, the method `remove_key` will be called even if
|
51
|
+
# previous stages have raised an error.
|
52
|
+
#
|
53
|
+
# See the respective method's documentation for details on these.
|
54
|
+
#
|
55
|
+
# The following stages are not meant to be overwritten, but considered
|
56
|
+
# internal:
|
57
|
+
#
|
58
|
+
# * before_install
|
59
|
+
# * before_setup
|
60
|
+
# * before_prepare
|
61
|
+
# * before_finish
|
62
|
+
#
|
63
|
+
# Dependencies declared as required, such as APT, NPM, or Python are going to
|
64
|
+
# be installed as part of the `before_install` stage .
|
65
|
+
#
|
66
|
+
# Cleanup is run as part of the `before_prepare` stage if the option
|
67
|
+
# `--cleanup` was given. This will use `git stash --all` in order to reset
|
68
|
+
# the working directory to the committed state, and cleanup any left over
|
69
|
+
# artifacts from the build process. Providers can use the DSL method `keep`
|
70
|
+
# in order to declare known artifacts (such as CLI tooling installed to the
|
71
|
+
# working directory) that needs to be moved out of the way and restored after
|
72
|
+
# the cleanup process. (It is recommended to place such artifacts outside of
|
73
|
+
# the build working directory though, for example in `~/.dpl`).
|
74
|
+
#
|
75
|
+
# The method `run_cmd` is called for each command specified using the `--run`
|
76
|
+
# option. By default, these command are going to be run as local shell
|
77
|
+
# commands, but providers can choose to overwrite this method in order to run
|
78
|
+
# the command on a remote machine.
|
79
|
+
#
|
80
|
+
# @see https://github.com/svenfuchs/cl Cl's documentation for details on how
|
81
|
+
# providers (commands) are declared and run.
|
82
|
+
|
83
|
+
class Provider < Cl::Cmd
|
84
|
+
extend Dsl, Forwardable
|
85
|
+
include Assets, Env, ConfigFile, FileUtils, Interpolate, Memoize, Squiggle
|
86
|
+
|
87
|
+
class << self
|
88
|
+
def examples
|
89
|
+
@examples ||= super || Examples.new(self).cmds
|
90
|
+
end
|
91
|
+
|
92
|
+
def move_files(ctx)
|
93
|
+
ctx.move_files(move) if move.any?
|
94
|
+
end
|
95
|
+
|
96
|
+
def unmove_files(ctx)
|
97
|
+
ctx.unmove_files(move) if move.any?
|
98
|
+
end
|
99
|
+
|
100
|
+
def install_deps?
|
101
|
+
apt? || gem? || npm? || pip?
|
102
|
+
end
|
103
|
+
|
104
|
+
def install_deps(ctx)
|
105
|
+
ctx.apts_get(apt) if apt?
|
106
|
+
ctx.gems_require(gem) if gem?
|
107
|
+
npm.each { |npm| ctx.npm_install *npm } if npm?
|
108
|
+
pip.each { |pip| ctx.pip_install *pip } if pip?
|
109
|
+
end
|
110
|
+
|
111
|
+
def validate_runtimes(ctx)
|
112
|
+
ctx.validate_runtimes(runtimes) if runtimes.any?
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Fold names to display in the build log.
|
117
|
+
FOLDS = {
|
118
|
+
init: 'Initialize deployment',
|
119
|
+
setup: 'Setup deployment',
|
120
|
+
validate: 'Validate deployment',
|
121
|
+
install: 'Install deployment dependencies',
|
122
|
+
login: 'Authenticate deployment',
|
123
|
+
prepare: 'Prepare deployment',
|
124
|
+
deploy: 'Run deployment',
|
125
|
+
finish: 'Finish deployment',
|
126
|
+
}
|
127
|
+
|
128
|
+
# Deployment process stages.
|
129
|
+
#
|
130
|
+
# In addition to the stages listed here the stage `finish` will be run at
|
131
|
+
# the end of the process.
|
132
|
+
#
|
133
|
+
# Also, the methods `add_key` (called before `setup`), `remove_key` (called
|
134
|
+
# before `finish`), and `run_cmd` (called after `deploy`) may be of
|
135
|
+
# interest to implementors.
|
136
|
+
STAGES = %i(
|
137
|
+
init
|
138
|
+
install
|
139
|
+
login
|
140
|
+
setup
|
141
|
+
validate
|
142
|
+
prepare
|
143
|
+
deploy
|
144
|
+
)
|
145
|
+
|
146
|
+
abstract
|
147
|
+
|
148
|
+
opt '--cleanup', 'Clean up build artifacts from the Git working directory before the deployment', negate: %w(skip)
|
149
|
+
opt '--run CMD', 'Commands to execute after the deployment finished successfully', type: :array
|
150
|
+
opt '--stage NAME', 'Execute the given stage(s) only', type: :array, internal: true, default: STAGES
|
151
|
+
opt '--backtrace', 'Print the backtrace for exceptions', internal: true
|
152
|
+
opt '--fold', 'Wrap log output in folds', internal: true
|
153
|
+
opt '--edge', internal: true
|
154
|
+
|
155
|
+
vars *%i(
|
156
|
+
git_author_email
|
157
|
+
git_author_name
|
158
|
+
git_branch
|
159
|
+
git_commit_author
|
160
|
+
git_commit_msg
|
161
|
+
git_sha
|
162
|
+
git_tag
|
163
|
+
)
|
164
|
+
|
165
|
+
msgs before_install: 'Installing deployment dependencies',
|
166
|
+
before_setup: 'Setting the build environment up for the deployment',
|
167
|
+
setup_git_ssh: 'Setting up git-ssh',
|
168
|
+
cleanup: 'Cleaning up git repository with `git stash --all`',
|
169
|
+
ssh_keygen: 'Generating SSH key',
|
170
|
+
setup_git_ua: 'Setting up git HTTP user agent',
|
171
|
+
ssh_remote_host: 'SSH remote is %s at port %s',
|
172
|
+
ssh_try_connect: 'Waiting for SSH connection ...',
|
173
|
+
ssh_connected: 'SSH connection established.',
|
174
|
+
ssh_failed: 'Failed to establish SSH connection.'
|
175
|
+
|
176
|
+
def_delegators :'self.class', :status, :full_name, :install_deps,
|
177
|
+
:install_deps?, :keep, :move_files, :unmove_files, :needs?, :runtimes,
|
178
|
+
:validate_runtimes, :user_agent
|
179
|
+
|
180
|
+
def_delegators :ctx, :apt_get, :gem_require, :npm_install, :pip_install,
|
181
|
+
:build_dir, :build_number, :encoding, :file_size, :git_author_email,
|
182
|
+
:git_author_name, :git_branch, :git_branch, :git_commit_author,
|
183
|
+
:git_commit_msg, :git_commit_msg, :git_dirty?, :git_log, :git_log,
|
184
|
+
:git_ls_files, :git_ls_remote?, :git_remote_urls, :git_remote_urls,
|
185
|
+
:git_rev_parse, :git_rev_parse, :git_sha, :git_tag, :last_err, :last_out,
|
186
|
+
:last_out, :logger, :machine_name, :mv, :node_version, :node_version,
|
187
|
+
:npm_version, :rendezvous, :rendezvous, :repo_slug, :sleep, :sleep,
|
188
|
+
:ssh_keygen, :success?, :test?, :test?, :tmp_dir, :tty?, :which, :which,
|
189
|
+
:write_file, :write_netrc
|
190
|
+
|
191
|
+
attr_reader :repo_name, :key_name
|
192
|
+
|
193
|
+
def initialize(ctx, *args)
|
194
|
+
@repo_name = ctx.repo_name
|
195
|
+
@key_name = ctx.machine_name
|
196
|
+
super
|
197
|
+
end
|
198
|
+
|
199
|
+
# Runs all stages, all commands provided by the user, as well as the final
|
200
|
+
# stage `finish` (which will be run even if an error has been raised during
|
201
|
+
# previous stages).
|
202
|
+
def run
|
203
|
+
stages = stage.select { |stage| run_stage?(stage) }
|
204
|
+
stages.each { |stage| run_stage(stage) }
|
205
|
+
run_cmds
|
206
|
+
rescue Error
|
207
|
+
raise
|
208
|
+
rescue Exception => e
|
209
|
+
raise Error.new("#{e.message} (#{e.class})", backtrace: backtrace? ? e.backtrace : nil) unless test?
|
210
|
+
raise
|
211
|
+
ensure
|
212
|
+
run_stage(:finish, fold: false) if finish?
|
213
|
+
end
|
214
|
+
|
215
|
+
# Whether or not a stage needs to be run
|
216
|
+
def run_stage?(stage)
|
217
|
+
respond_to?(:"before_#{stage}") || respond_to?(stage)
|
218
|
+
end
|
219
|
+
|
220
|
+
def finish?
|
221
|
+
stage.size == STAGES.size
|
222
|
+
end
|
223
|
+
|
224
|
+
# Runs a single stage.
|
225
|
+
#
|
226
|
+
# For each stage the base class has the opportunity to implement a `before`
|
227
|
+
# stage method, in order to apply default behaviour. Provider implementors
|
228
|
+
# are asked to not overwrite these methods.
|
229
|
+
#
|
230
|
+
# Any log output from both the before stage and stage method is going to be
|
231
|
+
# folded in the resulting build log.
|
232
|
+
def run_stage(stage, opts = {})
|
233
|
+
fold(stage, opts) do
|
234
|
+
send(:"before_#{stage}") if respond_to?(:"before_#{stage}")
|
235
|
+
send(stage) if respond_to?(stage)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# Initialize the deployment process.
|
240
|
+
#
|
241
|
+
# This will:
|
242
|
+
#
|
243
|
+
# * Displays warning messages about the provider's maturity status, and deprecated
|
244
|
+
# options used.
|
245
|
+
# * Setup a ~/.dpl working directory
|
246
|
+
# * Move files out of the way that have been declared as such
|
247
|
+
def before_init
|
248
|
+
warn status.msg if status && status.announce?
|
249
|
+
deprecations.each { |(key, msg)| ctx.deprecate_opt(key, msg) }
|
250
|
+
setup_dpl_dir
|
251
|
+
move_files(ctx)
|
252
|
+
end
|
253
|
+
|
254
|
+
# Install APT, NPM, and Python dependencies as declared by the provider.
|
255
|
+
def before_install
|
256
|
+
validate_runtimes(ctx)
|
257
|
+
return unless install_deps?
|
258
|
+
info :before_install
|
259
|
+
install_deps(ctx)
|
260
|
+
end
|
261
|
+
|
262
|
+
# Sets the build environment up for the deployment.
|
263
|
+
#
|
264
|
+
# This will:
|
265
|
+
#
|
266
|
+
# * Setup a ~/.dpl working directory
|
267
|
+
# * Create a temporary, per build SSH key, and call `add_key` if the feature `ssh_key` has been declared as required.
|
268
|
+
# * Setup git config (email and user name) if the feature `git` has been declared as required.
|
269
|
+
# * Either set or unset the environment variable `GIT_HTTP_USER_AGENT` depending if the feature `git_http_user_agent` has been declared as required.
|
270
|
+
def before_setup
|
271
|
+
info :before_setup
|
272
|
+
setup_ssh_key if needs?(:ssh_key)
|
273
|
+
setup_git_config if needs?(:git)
|
274
|
+
setup_git_http_user_agent
|
275
|
+
end
|
276
|
+
|
277
|
+
# Prepares the deployment by cleaning up the working directory.
|
278
|
+
#
|
279
|
+
# @see Provider#cleanup
|
280
|
+
def before_prepare
|
281
|
+
cleanup if cleanup?
|
282
|
+
end
|
283
|
+
|
284
|
+
# Runs each command as given by the user using the `--run` option.
|
285
|
+
#
|
286
|
+
# For a command that matches `restart` the method `restart` will be called
|
287
|
+
# (which can be overwritten by providers, e.g. in order to restart service
|
288
|
+
# instances).
|
289
|
+
#
|
290
|
+
# All other commands will be passed to the method `run_cmd`. By default this
|
291
|
+
# will be run as a shell command locally, but providers can choose to
|
292
|
+
# overwrite this method in order to run the command on a remote machine.
|
293
|
+
def run_cmds
|
294
|
+
Array(opts[:run]).each do |cmd|
|
295
|
+
cmd.downcase == 'restart' ? restart : run_cmd(cmd)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def run_cmd(cmd)
|
300
|
+
cmd.downcase == 'restart' ? restart : shell(cmd)
|
301
|
+
end
|
302
|
+
|
303
|
+
# Finalizes the deployment process.
|
304
|
+
#
|
305
|
+
# This will:
|
306
|
+
#
|
307
|
+
# * Call the method `remove_key` if implemented by the provider, and if the
|
308
|
+
# feature `ssh_key` has been declared as required.
|
309
|
+
# * Revert the cleanup process, i.e. restore files moved out of the way
|
310
|
+
# during `cleanup`.
|
311
|
+
# * Remove the temporary directory `~/.dpl`
|
312
|
+
def before_finish
|
313
|
+
remove_key if needs?(:ssh_key) && respond_to?(:remove_key)
|
314
|
+
uncleanup if cleanup?
|
315
|
+
unmove_files(ctx)
|
316
|
+
remove_dpl_dir
|
317
|
+
end
|
318
|
+
|
319
|
+
# Resets the current working directory to the commited state.
|
320
|
+
#
|
321
|
+
# Cleanup will use `git stash --all` in order to reset the working
|
322
|
+
# directory to the committed state, and cleanup any left over artifacts
|
323
|
+
# from the build process. Providers can use the DSL method `keep` in order
|
324
|
+
# to declare known artifacts (such as CLI tooling installed to the working
|
325
|
+
# directory) that needs to be moved out of the way and restored after the
|
326
|
+
# cleanup process.
|
327
|
+
def cleanup
|
328
|
+
info :cleanup
|
329
|
+
keep.each { |path| shell "mv ./#{path} ~/#{path}", echo: false, assert: false }
|
330
|
+
shell 'git stash --all'
|
331
|
+
keep.each { |path| shell "mv ~/#{path} ./#{path}", echo: false, assert: false }
|
332
|
+
end
|
333
|
+
|
334
|
+
# Restore files that have been cleaned up.
|
335
|
+
def uncleanup
|
336
|
+
shell 'git stash pop', assert: false
|
337
|
+
end
|
338
|
+
|
339
|
+
# Creates the directory `~/.dpl` as an internal working directory.
|
340
|
+
def setup_dpl_dir
|
341
|
+
rm_rf '~/.dpl'
|
342
|
+
mkdir_p '~/.dpl'
|
343
|
+
chmod 0700, '~/.dpl'
|
344
|
+
end
|
345
|
+
|
346
|
+
# Remove the internal working directory `~/.dpl`.
|
347
|
+
def remove_dpl_dir
|
348
|
+
rm_rf '~/.dpl'
|
349
|
+
end
|
350
|
+
|
351
|
+
# Creates an SSH key, and sets up git-ssh if needed.
|
352
|
+
#
|
353
|
+
# This will:
|
354
|
+
#
|
355
|
+
# * Create a temporary, per build SSH key.
|
356
|
+
# * Setup a `git-ssh` executable to use that key.
|
357
|
+
# * Call the method `add_key` if implemented by the provider.
|
358
|
+
def setup_ssh_key
|
359
|
+
ssh_keygen(key_name, '~/.dpl/id_rsa')
|
360
|
+
setup_git_ssh('~/.dpl/id_rsa')
|
361
|
+
add_key('~/.dpl/id_rsa.pub') if respond_to?(:add_key)
|
362
|
+
end
|
363
|
+
|
364
|
+
# Setup git config
|
365
|
+
#
|
366
|
+
# This adds the current user's name and email address (as user@localhost)
|
367
|
+
# to the git config.
|
368
|
+
def setup_git_config
|
369
|
+
shell "git config user.email >/dev/null 2>/dev/null || git config user.email `whoami`@localhost", echo: false, assert: false
|
370
|
+
shell "git config user.name >/dev/null 2>/dev/null || git config user.name `whoami`", echo: false, assert: false
|
371
|
+
end
|
372
|
+
|
373
|
+
# Sets up `git-ssh` and the GIT_SSH env var
|
374
|
+
def setup_git_ssh(key)
|
375
|
+
info :setup_git_ssh
|
376
|
+
path, conf = '~/.dpl/git-ssh', asset(:dpl, :git_ssh).read % expand(key)
|
377
|
+
open(path, 'w+') { |file| file.write(conf) }
|
378
|
+
chmod(0740, path)
|
379
|
+
ENV['GIT_SSH'] = expand(path)
|
380
|
+
end
|
381
|
+
|
382
|
+
# Generates an SSH key.
|
383
|
+
def ssh_keygen(key, path)
|
384
|
+
info :ssh_keygen
|
385
|
+
ctx.ssh_keygen(key, expand(path))
|
386
|
+
end
|
387
|
+
|
388
|
+
# Sets or unsets the environment variable `GIT_HTTP_USER_AGENT`.
|
389
|
+
def setup_git_http_user_agent
|
390
|
+
return ENV.delete('GIT_HTTP_USER_AGENT') unless needs?(:git_http_user_agent)
|
391
|
+
info :setup_git_ua
|
392
|
+
ENV['GIT_HTTP_USER_AGENT'] = user_agent(git: `git --version`[/[\d\.]+/])
|
393
|
+
end
|
394
|
+
|
395
|
+
# Waits for SSH access on the given host and port.
|
396
|
+
#
|
397
|
+
# This will try to connect to the given SSH host and port, and keep
|
398
|
+
# retrying 30 times, waiting a second inbetween retries.
|
399
|
+
def wait_for_ssh_access(host, port)
|
400
|
+
info :ssh_remote_host, host, port
|
401
|
+
1.upto(20) { try_ssh_access(host, port) && break || sleep(3) }
|
402
|
+
success? ? info(:ssh_connected) : error(:ssh_failed)
|
403
|
+
end
|
404
|
+
|
405
|
+
# Tries to connect to the given SSH host and port.
|
406
|
+
def try_ssh_access(host, port)
|
407
|
+
info :ssh_try_connect
|
408
|
+
shell "#{ENV['GIT_SSH']} #{host} -p #{port} 2>&1 | grep -c 'PTY allocation request failed' > /dev/null", echo: false, assert: false
|
409
|
+
end
|
410
|
+
|
411
|
+
# Creates a log fold.
|
412
|
+
#
|
413
|
+
# Folds any log output from the given block into a fold with the given
|
414
|
+
# name.
|
415
|
+
def fold(name, opts = {}, &block)
|
416
|
+
return yield unless fold?(name, opts)
|
417
|
+
title = FOLDS[name] || "deploy.#{name}"
|
418
|
+
ctx.fold(title, &block)
|
419
|
+
end
|
420
|
+
|
421
|
+
# Checks if the given stage needs to be folded.
|
422
|
+
#
|
423
|
+
# Depends on the option `--fold`, also omits folds for the init and finish
|
424
|
+
# stages. Can be overwritten by passing `fold: false`.
|
425
|
+
def fold?(name, opts = {})
|
426
|
+
!opts[:fold].is_a?(FalseClass) && super() && !%i(init).include?(name)
|
427
|
+
end
|
428
|
+
|
429
|
+
# Runs a script as a shell command.
|
430
|
+
#
|
431
|
+
# Scripts can be stored as separate files (assets) in the directory
|
432
|
+
# `lib/dpl/assets/[provider]`.
|
433
|
+
#
|
434
|
+
# This is meant for large shell commands that would be hard to read if
|
435
|
+
# embedded in Ruby code. Storing them as separate files helps with proper
|
436
|
+
# syntax highlighting etc in editors, and allows to execute them for
|
437
|
+
# testing purposes.
|
438
|
+
#
|
439
|
+
# Scripts can have interpolation variables. See Dpl::Interpolate for
|
440
|
+
# details on interpolating variables.
|
441
|
+
#
|
442
|
+
# See Ctx::Bash#shell for details on the options accepted.
|
443
|
+
def script(name, opts = {})
|
444
|
+
opts[:assert] = name if opts[:assert].is_a?(TrueClass)
|
445
|
+
shell(asset(name).read, opts.merge(echo: false))
|
446
|
+
end
|
447
|
+
|
448
|
+
# Runs a single shell command.
|
449
|
+
#
|
450
|
+
# Shell commands can have interpolation variables. See Dpl::Interpolate for
|
451
|
+
# details on interpolating variables.
|
452
|
+
#
|
453
|
+
# See Ctx::Bash#shell for details on the options accepted.
|
454
|
+
def shell(cmd, *args)
|
455
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
456
|
+
cmd = Cmd.new(self, cmd, opts)
|
457
|
+
ctx.shell(cmd)
|
458
|
+
end
|
459
|
+
|
460
|
+
# @!method print
|
461
|
+
# Prints a partial message to stdout
|
462
|
+
#
|
463
|
+
# This method does not append a newline character to the given message,
|
464
|
+
# which usually is not the desired behaviour. The method is intended to be
|
465
|
+
# used if an initial, partial message is supposed to be printed, which will
|
466
|
+
# be completed later (using the method `info`).
|
467
|
+
#
|
468
|
+
# For example:
|
469
|
+
#
|
470
|
+
# print 'Starting a long running task ...'
|
471
|
+
# run_long_running_task
|
472
|
+
# info 'done.'
|
473
|
+
#
|
474
|
+
# Messages support interpolation variables. See Dpl::Interpolate for
|
475
|
+
# details on interpolating variables.
|
476
|
+
|
477
|
+
# @!method info
|
478
|
+
# Outputs an info message to stdout
|
479
|
+
#
|
480
|
+
# This method is intended to be used for default, info level messages that
|
481
|
+
# are supposed to show up in the build log.
|
482
|
+
#
|
483
|
+
# @!method warn
|
484
|
+
# Outputs an warning message to stderr
|
485
|
+
#
|
486
|
+
# This method is intended to be used for warning messages that are supposed
|
487
|
+
# to show up in the build log, but do not qualify as errors that would
|
488
|
+
# abort the deployment process. The warning will be highlighted as red
|
489
|
+
# text. Use sparingly.
|
490
|
+
#
|
491
|
+
# Messages support interpolation variables. See Dpl::Interpolate for
|
492
|
+
# details on interpolating variables.
|
493
|
+
|
494
|
+
# @!method error
|
495
|
+
# Outputs an error message to stderr, and raises an error, halting the
|
496
|
+
# deployment process.
|
497
|
+
#
|
498
|
+
# This method is intended to be used for all error conditions that require
|
499
|
+
# the deployment process to be aborted.
|
500
|
+
#
|
501
|
+
# Messages support interpolation variables. See Dpl::Interpolate for
|
502
|
+
# details on interpolating variables.
|
503
|
+
%i(print info warn error).each do |level|
|
504
|
+
define_method(level) do |msg, *args|
|
505
|
+
msg = interpolate(self.msg(msg), args) if msg.is_a?(Symbol)
|
506
|
+
ctx.send(level, msg)
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
# @!method cmd
|
511
|
+
# Looks up a shell command from the commands declared by the provider
|
512
|
+
# (using the class level DSL).
|
513
|
+
#
|
514
|
+
# Not usually useful to be used by provider implementors directly. Use the
|
515
|
+
# method `shell` in order to execute shell commands.
|
516
|
+
|
517
|
+
# @!method err
|
518
|
+
# Looks up an error message from the error messages declared by the
|
519
|
+
# provider (using the class level DSL), as needed by the option `assert`
|
520
|
+
# when passed to the method `shell`.
|
521
|
+
|
522
|
+
# @!method msg
|
523
|
+
# Looks up a message from the messages declared by the provider (using the
|
524
|
+
# class level DSL).
|
525
|
+
#
|
526
|
+
# For example, a message declared on the class body like so:
|
527
|
+
#
|
528
|
+
# ```ruby
|
529
|
+
# msgs commit_msg: 'Commit build artifacts on build %{build_number}'
|
530
|
+
# ```
|
531
|
+
#
|
532
|
+
# could be used by the implementation like so:
|
533
|
+
#
|
534
|
+
# ```ruby
|
535
|
+
# def commit_msg
|
536
|
+
# interpolate(msg(:commit_msg))
|
537
|
+
# end
|
538
|
+
# ```
|
539
|
+
#
|
540
|
+
# Note that the the method `interpolate` needs to be used in order to
|
541
|
+
# interpolate variables used in a message (if any).
|
542
|
+
%i(cmd err msg str).each do |name|
|
543
|
+
define_method(name) do |*keys|
|
544
|
+
key = keys.detect { |key| key.is_a?(Symbol) }
|
545
|
+
self.class.send(:"#{name}s")[key] if key
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
# Escapes the given string so it can be safely used in Bash.
|
550
|
+
def escape(str)
|
551
|
+
Shellwords.escape(str)
|
552
|
+
end
|
553
|
+
|
554
|
+
# Double quotes the given string.
|
555
|
+
def quote(str)
|
556
|
+
%("#{str.to_s.gsub('"', '\"')}")
|
557
|
+
end
|
558
|
+
|
559
|
+
# Outdents the given string.
|
560
|
+
#
|
561
|
+
# @see Dpl::Squiggle
|
562
|
+
def sq(str)
|
563
|
+
self.class.sq(str)
|
564
|
+
end
|
565
|
+
|
566
|
+
# Generate shell option strings to be passed to a shell command.
|
567
|
+
#
|
568
|
+
# This generates strings like `--key="value"` for the option keys passed.
|
569
|
+
# These keys are supposed to correspond to methods on the provider
|
570
|
+
# instance, which will be called in order to determine the option value.
|
571
|
+
#
|
572
|
+
# If the returned value is an array then the option will be repeated
|
573
|
+
# multiple times. If it is a String then it will be double quoted.
|
574
|
+
# Otherwise it is assumed to be a flag that does not have a value.
|
575
|
+
#
|
576
|
+
# @option prefix [String] Use this to set a single dash as an option prefix (defaults to two dashes).
|
577
|
+
# @option dashed [Boolean] Use this to dasherize the option key (rather than underscore it, defaults to underscore).
|
578
|
+
def opts_for(keys, opts = {})
|
579
|
+
strs = Array(keys).map { |key| opt_for(key, opts) if send(:"#{key}?") }.compact
|
580
|
+
strs.join(' ') if strs.any?
|
581
|
+
end
|
582
|
+
|
583
|
+
def opt_for(key, opts = {})
|
584
|
+
case value = send(key)
|
585
|
+
when String then "#{opt_key(key, opts)}=#{value.inspect}"
|
586
|
+
when Array then value.map { |value| "#{opt_key(key, opts)}=#{value.inspect}" }
|
587
|
+
else opt_key(key, opts)
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
def opt_key(key, opts)
|
592
|
+
"#{opts[:prefix] || '--'}#{opts[:dashed] ? key.to_s.gsub('_', '-') : key}"
|
593
|
+
end
|
594
|
+
|
595
|
+
# Compacts the given hash by rejecting nil values.
|
596
|
+
def compact(hash)
|
597
|
+
hash.reject { |_, value| value.nil? }
|
598
|
+
end
|
599
|
+
|
600
|
+
# Returns a new hash with the given keys selected from the given hash.
|
601
|
+
def only(hash, *keys)
|
602
|
+
hash.select { |key, _| keys.include?(key) }
|
603
|
+
end
|
604
|
+
|
605
|
+
# Deep symbolizes the given hash's keys
|
606
|
+
def symbolize(obj)
|
607
|
+
case obj
|
608
|
+
when Hash
|
609
|
+
obj.map { |key, obj| [key.to_sym, symbolize(obj)] }.to_h
|
610
|
+
when Array
|
611
|
+
obj.map { |obj| symbolize(obj) }
|
612
|
+
else
|
613
|
+
obj
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
617
|
+
def file?(path)
|
618
|
+
File.file?(expand(path))
|
619
|
+
end
|
620
|
+
|
621
|
+
def mkdir_p(path)
|
622
|
+
FileUtils.mkdir_p(expand(path))
|
623
|
+
end
|
624
|
+
|
625
|
+
def chmod(perm, path)
|
626
|
+
super(perm, expand(path))
|
627
|
+
end
|
628
|
+
|
629
|
+
def mv(src, dest)
|
630
|
+
super(expand(src), expand(dest))
|
631
|
+
end
|
632
|
+
|
633
|
+
def rm_rf(path)
|
634
|
+
super(expand(path))
|
635
|
+
end
|
636
|
+
|
637
|
+
def open(path, *args, &block)
|
638
|
+
File.open(expand(path), *args, &block)
|
639
|
+
end
|
640
|
+
|
641
|
+
def read(path)
|
642
|
+
File.read(expand(path))
|
643
|
+
end
|
644
|
+
|
645
|
+
def expand(*args)
|
646
|
+
File.expand_path(*args)
|
647
|
+
end
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
require 'dpl/providers'
|