travis_dpl_test 2.0.3.beta.4.ror

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.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +172 -0
  3. data/CODE_OF_CONDUCT.md +74 -0
  4. data/CONTRIBUTING.md +392 -0
  5. data/Gemfile +32 -0
  6. data/Gemfile.lock +611 -0
  7. data/LICENSE +19 -0
  8. data/README.md +2744 -0
  9. data/Rakefile +210 -0
  10. data/bin/dpl +11 -0
  11. data/config/transliterate.yml +733 -0
  12. data/dpl.gemspec +23 -0
  13. data/lib/dpl/assets/atlas/install +19 -0
  14. data/lib/dpl/assets/convox/install +11 -0
  15. data/lib/dpl/assets/dpl/README.erb.md +138 -0
  16. data/lib/dpl/assets/dpl/git_ssh +8 -0
  17. data/lib/dpl/assets/git/detect_private_key +8 -0
  18. data/lib/dpl/assets/hephy/filter_log +3 -0
  19. data/lib/dpl/assets/pypi/install +4 -0
  20. data/lib/dpl/assets/scalingo/install +6 -0
  21. data/lib/dpl/cli.rb +100 -0
  22. data/lib/dpl/ctx/bash.rb +549 -0
  23. data/lib/dpl/ctx/test.rb +255 -0
  24. data/lib/dpl/ctx.rb +4 -0
  25. data/lib/dpl/helper/assets.rb +38 -0
  26. data/lib/dpl/helper/cmd.rb +169 -0
  27. data/lib/dpl/helper/config_file.rb +49 -0
  28. data/lib/dpl/helper/cookbook_site_streaming_uploader.rb +249 -0
  29. data/lib/dpl/helper/env.rb +92 -0
  30. data/lib/dpl/helper/github.rb +22 -0
  31. data/lib/dpl/helper/interpolate.rb +160 -0
  32. data/lib/dpl/helper/memoize.rb +23 -0
  33. data/lib/dpl/helper/squiggle.rb +24 -0
  34. data/lib/dpl/helper/transliterate.rb +13 -0
  35. data/lib/dpl/helper/wrap.rb +11 -0
  36. data/lib/dpl/helper/zip.rb +71 -0
  37. data/lib/dpl/provider/dsl.rb +410 -0
  38. data/lib/dpl/provider/examples.rb +132 -0
  39. data/lib/dpl/provider/status.rb +61 -0
  40. data/lib/dpl/provider.rb +651 -0
  41. data/lib/dpl/providers/anynines.rb +71 -0
  42. data/lib/dpl/providers/azure_web_apps.rb +63 -0
  43. data/lib/dpl/providers/bintray.rb +324 -0
  44. data/lib/dpl/providers/bluemixcloudfoundry.rb +98 -0
  45. data/lib/dpl/providers/boxfuse.rb +52 -0
  46. data/lib/dpl/providers/cargo.rb +32 -0
  47. data/lib/dpl/providers/chef_supermarket.rb +132 -0
  48. data/lib/dpl/providers/cloud66.rb +46 -0
  49. data/lib/dpl/providers/cloudfiles.rb +62 -0
  50. data/lib/dpl/providers/cloudformation.rb +281 -0
  51. data/lib/dpl/providers/cloudfoundry.rb +89 -0
  52. data/lib/dpl/providers/codedeploy.rb +190 -0
  53. data/lib/dpl/providers/convox.rb +130 -0
  54. data/lib/dpl/providers/datica.rb +64 -0
  55. data/lib/dpl/providers/ecr.rb +129 -0
  56. data/lib/dpl/providers/elasticbeanstalk.rb +207 -0
  57. data/lib/dpl/providers/engineyard.rb +113 -0
  58. data/lib/dpl/providers/firebase.rb +45 -0
  59. data/lib/dpl/providers/flynn.rb +35 -0
  60. data/lib/dpl/providers/gae.rb +78 -0
  61. data/lib/dpl/providers/gcs.rb +132 -0
  62. data/lib/dpl/providers/git_push.rb +273 -0
  63. data/lib/dpl/providers/gleis.rb +74 -0
  64. data/lib/dpl/providers/hackage.rb +53 -0
  65. data/lib/dpl/providers/hephy.rb +107 -0
  66. data/lib/dpl/providers/heroku/api.rb +123 -0
  67. data/lib/dpl/providers/heroku/git.rb +54 -0
  68. data/lib/dpl/providers/heroku.rb +111 -0
  69. data/lib/dpl/providers/lambda.rb +211 -0
  70. data/lib/dpl/providers/launchpad.rb +80 -0
  71. data/lib/dpl/providers/netlify.rb +38 -0
  72. data/lib/dpl/providers/npm.rb +130 -0
  73. data/lib/dpl/providers/nuget.rb +41 -0
  74. data/lib/dpl/providers/openshift.rb +52 -0
  75. data/lib/dpl/providers/opsworks.rb +146 -0
  76. data/lib/dpl/providers/packagecloud.rb_ +194 -0
  77. data/lib/dpl/providers/pages/api.rb +106 -0
  78. data/lib/dpl/providers/pages/git.rb +262 -0
  79. data/lib/dpl/providers/pages.rb +18 -0
  80. data/lib/dpl/providers/puppetforge.rb +50 -0
  81. data/lib/dpl/providers/pypi.rb +125 -0
  82. data/lib/dpl/providers/releases.rb +234 -0
  83. data/lib/dpl/providers/rubygems.rb +97 -0
  84. data/lib/dpl/providers/s3.rb +251 -0
  85. data/lib/dpl/providers/scalingo.rb +69 -0
  86. data/lib/dpl/providers/script.rb +32 -0
  87. data/lib/dpl/providers/snap.rb +68 -0
  88. data/lib/dpl/providers/surge.rb +59 -0
  89. data/lib/dpl/providers/testfairy.rb +101 -0
  90. data/lib/dpl/providers/transifex.rb +72 -0
  91. data/lib/dpl/providers.rb +48 -0
  92. data/lib/dpl/string_ext.rb +23 -0
  93. data/lib/dpl/support/aws_sdk_patch.rb +26 -0
  94. data/lib/dpl/support/gems.rb +73 -0
  95. data/lib/dpl/support/gstore_patch.rb +8 -0
  96. data/lib/dpl/support/version.rb +84 -0
  97. data/lib/dpl/version.rb +5 -0
  98. data/lib/dpl.rb +23 -0
  99. data/status.json +237 -0
  100. metadata +161 -0
@@ -0,0 +1,549 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require 'cl'
5
+ require 'fileutils'
6
+ require 'logger'
7
+ require 'open3'
8
+ require 'tmpdir'
9
+ require 'securerandom'
10
+ require 'dpl/support/version'
11
+
12
+ module Dpl
13
+ module Ctx
14
+ class Bash < Cl::Ctx
15
+ include FileUtils
16
+
17
+ attr_accessor :folds, :stdout, :stderr, :last_out, :last_err
18
+
19
+ def initialize(stdout = $stdout, stderr = $stderr)
20
+ @stdout = stdout
21
+ @stderr = stderr
22
+ @folds = 0
23
+ super('dpl', abort: false)
24
+ end
25
+
26
+ # Folds any log output from the given block
27
+ #
28
+ # Starts a log fold with the given fold message, calls the block, and
29
+ # closes the fold.
30
+ #
31
+ # @param msg [String] the message that will appear on the log fold
32
+ def fold(msg)
33
+ self.folds += 1
34
+ print "travis_fold:start:dpl.#{folds}\r\e[K"
35
+ time do
36
+ info "\e[33m#{msg}\e[0m"
37
+ yield
38
+ end
39
+ ensure
40
+ print "\ntravis_fold:end:dpl.#{folds}\r\e[K"
41
+ end
42
+
43
+ # Times the given block
44
+ #
45
+ # Starts a travis time log tag, calls the block, and closes the tag,
46
+ # including timing information. This makes a timing badge appear on
47
+ # the surrounding log fold.
48
+ def time
49
+ id = SecureRandom.hex[0, 8]
50
+ start = Time.now.to_i * (10**9)
51
+ print "travis_time:start:#{id}\r\e[K"
52
+ yield
53
+ ensure
54
+ finish = Time.now.to_i * (10**9)
55
+ duration = finish - start
56
+ print "\ntravis_time:end:#{id}:start=#{start},finish=#{finish},duration=#{duration}\r\e[K"
57
+ end
58
+
59
+ # Outputs a deprecation warning for a given deprecated option key to stderr.
60
+ #
61
+ # @param key [Symbol] the deprecated option key
62
+ # @param msg [String or Symbol] the deprecation message. if given a Symbol this will be wrapped into the string "Please use #{symbol}".
63
+ def deprecate_opt(key, msg)
64
+ msg = "please use #{msg}" if msg.is_a?(Symbol)
65
+ warn "Deprecated option #{key} used (#{msg})."
66
+ end
67
+
68
+ # Outputs an info level message to stdout.
69
+ def info(*msgs)
70
+ stdout.puts(*msgs)
71
+ end
72
+
73
+ # Prints an info level message to stdout.
74
+ #
75
+ # This method does not append a newline character to the given message,
76
+ # which usually is not the desired behaviour. The method is intended to
77
+ # be used if an initial, partial message is supposed to be printed, which
78
+ # will be completed later (using the method `info`).
79
+ #
80
+ # For example:
81
+ #
82
+ # print 'Starting a long running task ...'
83
+ # run_long_running_task
84
+ # info 'done.'
85
+ def print(chars)
86
+ stdout.print(chars)
87
+ end
88
+
89
+ # Outputs an warning message to stderr
90
+ #
91
+ # This method is intended to be used for warning messages that are
92
+ # supposed to show up in the build log, but do not qualify as errors that
93
+ # would abort the deployment process. The warning will be highlighted as
94
+ # yellow text. Use sparingly.
95
+ def warn(*msgs)
96
+ msgs = msgs.join("\n").lines
97
+ msgs.each { |msg| stderr.puts("\e[33;1m#{msg}\e[0m") }
98
+ end
99
+
100
+ # Raises an exception, halting the deployment process.
101
+ #
102
+ # The calling executable `bin/dpl` will catch the exception, and abort
103
+ # the ruby process with the given error message.
104
+ #
105
+ # This method is intended to be used for all error conditions that
106
+ # require the deployment process to be aborted.
107
+ def error(message)
108
+ raise Error, message
109
+ end
110
+
111
+ # Returns a logger
112
+ #
113
+ # Returns a logger instance, with the given log level set. This can be
114
+ # used to pass to clients that accept a Ruby logger, such as Faraday,
115
+ # for debugging purposes.
116
+ #
117
+ # Use with care.
118
+ #
119
+ # @param level [Symbol] the Ruby logger log level
120
+ def logger(level = :info)
121
+ logger = Logger.new(stderr)
122
+ logger.level = Logger.const_get(level.to_s.upcase)
123
+ logger
124
+ end
125
+
126
+ def validate_runtimes(runtimes)
127
+ failed = runtimes.reject(&method(:validate_runtime))
128
+ failed = failed.map { |name, versions| "#{name} (#{versions.join(', ')})" }
129
+ error "Failed validating runtimes: #{failed.join(', ')}" if failed.any?
130
+ end
131
+
132
+ def validate_runtime(args)
133
+ name, required = *args
134
+ info "Validating required runtime version: #{name} (#{required.join(', ')})"
135
+ version = name == :node_js ? node_version : python_version
136
+ required.all? { |required| Version.new(version).satisfies?(required) }
137
+ end
138
+
139
+ def apts_get(packages)
140
+ packages = packages.reject { |name, cmd = name| which(cmd || name) }
141
+ return unless packages.any?
142
+
143
+ apt_update
144
+ packages.each { |package, cmd| apt_get(package, cmd || package, update: false) }
145
+ end
146
+
147
+ # Installs an APT package
148
+ #
149
+ # Installs the APT package with the given name, unless the command is already
150
+ # available (as determined by `which [cmd]`.
151
+ #
152
+ # @param package [String] the package name
153
+ # @param cmd [String] an executable installed by the package, defaults to the package name
154
+ def apt_get(package, cmd = package, opts = {})
155
+ return if which(cmd)
156
+
157
+ apt_update unless opts[:update].is_a?(FalseClass)
158
+ shell "sudo apt-get -qq install #{package}", retry: true
159
+ end
160
+
161
+ def apt_update
162
+ shell 'sudo apt-get update', retry: true
163
+ end
164
+
165
+ # Requires source files from Ruby gems, installing them on demand if required
166
+ #
167
+ # Installs the Ruby gems with the given version, if not already installed, and
168
+ # requires the specified source files from that gem.
169
+ #
170
+ # This happens using the bundler/inline API.
171
+ #
172
+ # @param gems [Array<String, String, Hash>] Array of gem requirements: gem name, version, and options (`require`: A single path or a list of paths to source files to require from this Ruby gem)
173
+ #
174
+ # @see https://bundler.io/v2.0/guides/bundler_in_a_single_file_ruby_script.html
175
+ def gems_require(gems)
176
+ # A local Gemfile.lock might interfer with bundler/inline, even though
177
+ # it should not. Switching to a temporary dir fixes this.
178
+ Dir.chdir(tmp_dir) do
179
+ require 'bundler/inline'
180
+ info "Installing gem dependencies: #{gems.map { |name, version, _| "#{name} #{"(#{version})" if version}".strip }.join(', ')}"
181
+ env = ENV.to_h
182
+ # Bundler.reset!
183
+ # Gem.loaded_specs.clear
184
+ gemfile do
185
+ source 'https://rubygems.org'
186
+ gems.each { |g| gem(*g) }
187
+ end
188
+ # https://github.com/bundler/bundler/issues/7181
189
+ ENV.replace(env)
190
+ end
191
+ end
192
+
193
+ # Installs an NPM package
194
+ #
195
+ # Installs the NPM package with the given name, unless the command is already
196
+ # available (as determined by `which [cmd]`.
197
+ #
198
+ # @param package [String] the package name
199
+ # @param cmd [String] an executable installed by the package, defaults to the package name
200
+ def npm_install(package, cmd = package)
201
+ shell "npm install -g #{package}", retry: true unless which(cmd)
202
+ end
203
+
204
+ # Installs a Python package
205
+ #
206
+ # Installs the Python package with the given name. A previously installed
207
+ # package is uninstalled before that, but only if `version` was given.
208
+ #
209
+ # @param package [String] Package name (required).
210
+ # @param cmd [String] Executable command installed by that package (optional, defaults to the package name).
211
+ # @param version [String] Package version (optional).
212
+ def pip_install(package, cmd = package, version = nil)
213
+ ENV['VIRTUAL_ENV'] = File.expand_path('~/dpl_venv')
214
+ ENV['PATH'] = File.expand_path("~/dpl_venv/bin:#{ENV['PATH']}")
215
+ shell 'virtualenv --no-site-packages ~/dpl_venv', echo: true
216
+ shell 'pip install urllib3[secure]'
217
+ cmd = "pip install #{package}"
218
+ cmd << pip_version(version) if version
219
+ shell cmd, retry: true
220
+ end
221
+
222
+ def pip_version(version)
223
+ version =~ /^\d+/ ? "==#{version}" : version
224
+ end
225
+
226
+ # Generates an SSH key
227
+ #
228
+ # @param name [String] the key name
229
+ # @param file [String] path to the key file
230
+ def ssh_keygen(name, file)
231
+ shell %(ssh-keygen -t rsa -N "" -C #{name} -f #{file})
232
+ end
233
+
234
+ # Runs a single shell command
235
+ #
236
+ # This the is the central point of executing any shell commands. It allows two
237
+ # strategies for running commands in subprocesses:
238
+ #
239
+ # * Using [Kernel#system](https://ruby-doc.org/core-2.6.3/Kernel.html#method-i-system)
240
+ # which is the default strategy, and should be used when possible. The stdout
241
+ # and stderr streams will not be captured, but streamed directly to the parent
242
+ # process (so any output on these streams appears in the build log as soon as
243
+ # possible).
244
+ #
245
+ # * Using [Open3.capture3](https://ruby-doc.org/stdlib-2.6.3/libdoc/open3/rdoc/Open3.html#method-c-capture3)
246
+ # which captures both stdout and stderr, and does not automatically output it
247
+ # to the build log. Implementors can choose to display it after the shell command
248
+ # has completed, using the `%{out}` and `%{err}` interpolation variables. Use
249
+ # sparingly.
250
+ #
251
+ # The method accepts the following options:
252
+ #
253
+ # @param cmd [String] the shell command to execute
254
+ # @param opts [Hash] options
255
+ #
256
+ # @option opts [Boolean] :echo output the command to stdout before running it
257
+ # @option opts [Boolean] :silence silence all log output by redirecting stdout and stderr to `/dev/null`
258
+ # @option opts [Boolean] :capture use `Open3.capture3` to capture stdout and stderr
259
+ # @option opts [String] :python wrap the command into Bash code that enforces the given Python version to be used
260
+ # @option opts [String] :retry retries the command 2 more times if it fails
261
+ # @option opts [String] :info message to output to stdout if the command has exited with the exit code 0 (supports the interpolation variable `${out}` for stdout in case it was captured.
262
+ # @option opts [String] :assert error message to be raised if the command has exited with a non-zero exit code (supports the interpolation variable `${out}` for stdout in case it was captured.
263
+ #
264
+ # @return [Boolean] whether or not the command was successful (has exited with the exit code 0)
265
+ def shell(cmd, opts = {})
266
+ cmd = Cmd.new(nil, cmd, opts) if cmd.is_a?(String)
267
+ info cmd.msg if cmd.msg?
268
+ info cmd.echo if cmd.echo?
269
+
270
+ @last_out, @last_err, @last_status = retrying(cmd.retry ? 2 : 0) do
271
+ send(cmd.capture? ? :open3 : :system, cmd.cmd, cmd.opts)
272
+ end
273
+
274
+ info format(cmd.success, out: last_out) if success? && cmd.success?
275
+ error format(cmd.error, err: last_err) if failed? && cmd.assert?
276
+
277
+ success? && cmd.capture? ? last_out.chomp : @last_status
278
+ end
279
+
280
+ def retrying(max, tries = 0, status = false)
281
+ loop do
282
+ tries += 1
283
+ out, err, status = yield
284
+ return [out, err, status] if status || tries > max
285
+ end
286
+ end
287
+
288
+ # Runs a shell command and captures stdout, stderr, and the exit status
289
+ #
290
+ # Runs the given command using `Open3.capture3`, which will capture the
291
+ # stdout and stderr streams, as well as the exit status. I.e. this will
292
+ # *not* stream log output in real time, but capture the output, and allow
293
+ # implementors to display it later (using the `%{out}` and `%{err}`
294
+ # interpolation variables.
295
+ #
296
+ # Use sparingly.
297
+ #
298
+ # @option chdir [String] directory temporarily to change to before running the command
299
+ def open3(cmd, opts)
300
+ opts = [opts[:chdir] ? only(opts, :chdir) : nil].compact
301
+ out, err, status = Open3.capture3(cmd, *opts)
302
+ [out, err, status.success?]
303
+ end
304
+
305
+ # Runs a shell command, streaming any stdout or stderr output, and
306
+ # returning the exit status
307
+ #
308
+ # This is the default method for executing shell commands. The stdout and
309
+ # stderr will not be captured, but streamed directly to the parent process.
310
+ #
311
+ # @option chdir [String] directory temporarily to change to before running the command
312
+ def system(cmd, opts = {})
313
+ opts = [opts[:chdir] ? only(opts, :chdir) : nil].compact
314
+ Kernel.system(cmd, *opts)
315
+ ['', '', last_process_status]
316
+ end
317
+
318
+ # Whether or not the last executed shell command was successful.
319
+ def success?
320
+ !!@last_status
321
+ end
322
+
323
+ # Whether or not the last executed shell command has failed.
324
+ def failed?
325
+ !success?
326
+ end
327
+
328
+ # Returns the last child process' exit status
329
+ #
330
+ # Internal, and not to be used by implementors. $? is a read-only
331
+ # variable, so we use a method that we can stub during tests.
332
+ def last_process_status
333
+ $CHILD_STATUS.success?
334
+ end
335
+
336
+ # Whether or not the current Ruby process runs with superuser priviledges.
337
+ def sudo?
338
+ Process::UID.eid.zero?
339
+ end
340
+
341
+ # Returns current repository name
342
+ #
343
+ # Uses the environment variable `TRAVIS_REPO_SLUG` if present, or the
344
+ # current directory's base name.
345
+ #
346
+ # Note that this might return an unexpected string outside of the context
347
+ # of Travis CI build environments if the method is called at a time when
348
+ # the current working directory has changed.
349
+ def repo_name
350
+ ENV['TRAVIS_REPO_SLUG'] ? ENV['TRAVIS_REPO_SLUG'].split('/').last : File.basename(Dir.pwd)
351
+ end
352
+
353
+ # Returns current repository slug
354
+ #
355
+ # Uses the environment variable `TRAVIS_REPO_SLUG` if present, or the
356
+ # last two segmens of the current working directory's path.
357
+ #
358
+ # Note that this might return an unexpected string outside of the context
359
+ # of Travis CI build environments if the method is called at a time when
360
+ # the current working directory has changed.
361
+ def repo_slug
362
+ ENV['TRAVIS_REPO_SLUG'] || Dir.pwd.split('/')[-2, 2].join('/')
363
+ end
364
+
365
+ # Returns the current build directory
366
+ #
367
+ # Uses the environment variable `TRAVIS_REPO_SLUG` if present, and
368
+ # defaults to `.` otherwise.
369
+ #
370
+ # Note that this might return an unexpected string outside of the context
371
+ # of Travis CI build environments if the method is called at a time when
372
+ # the current working directory has changed.
373
+ def build_dir
374
+ ENV['TRAVIS_BUILD_DIR'] || '.'
375
+ end
376
+
377
+ # Returns the current build number
378
+ #
379
+ # Returns the value of the environment variable `TRAVIS_BUILD_NUMBER` if
380
+ # present.
381
+ def build_number
382
+ ENV['TRAVIS_BUILD_NUMBER'] || raise('TRAVIS_BUILD_NUMBER not set')
383
+ end
384
+
385
+ # Returns the encoding of the given file, as determined by `file`.
386
+ def encoding(path)
387
+ case `file '#{path}'`
388
+ when /gzip compressed/
389
+ 'gzip'
390
+ when /compress'd/
391
+ 'compress'
392
+ when /text/
393
+ 'text'
394
+ when /data/
395
+ # shrugs?
396
+ end
397
+ end
398
+
399
+ # Returns the current branch name
400
+ def git_branch
401
+ ENV['TRAVIS_BRANCH'] || git_rev_parse('HEAD')
402
+ end
403
+
404
+ # Returns the message of the commit `git_sha`.
405
+ def git_commit_msg
406
+ `git log #{git_sha} -n 1 --pretty=%B`.chomp
407
+ end
408
+
409
+ # Returns the committer name of the commit `git_sha`.
410
+ def git_author_name
411
+ `git log #{git_sha} -n 1 --pretty=%an`.chomp
412
+ end
413
+
414
+ # Returns the comitter email of the commit `git_sha`.
415
+ def git_author_email
416
+ `git log #{git_sha} -n 1 --pretty=%ae`.chomp
417
+ end
418
+
419
+ # Whether or not the git working directory is dirty or has new or deleted files
420
+ def git_dirty?
421
+ !`git status --short`.chomp.empty?
422
+ end
423
+
424
+ # Returns the output of `git log`, using the given args.
425
+ def git_log(args)
426
+ `git log #{args}`.chomp
427
+ end
428
+
429
+ # Returns the Git log, separated by NULs
430
+ #
431
+ # Returns the output of `git ls-files -z`, which separates log entries by
432
+ # NULs, rather than newline characters.
433
+ def git_ls_files
434
+ `git ls-files -z`.split("\x0")
435
+ end
436
+
437
+ # Returns true if the given ref exists remotely
438
+ def git_ls_remote?(url, ref)
439
+ Kernel.system("git ls-remote --exit-code #{url} #{ref} > /dev/null 2>&1")
440
+ end
441
+
442
+ # Returns known Git remote URLs
443
+ def git_remote_urls
444
+ `git remote -v`.scan(/\t[^\s]+\s/).map(&:strip).uniq
445
+ end
446
+
447
+ # Returns the sha for the given Git ref
448
+ def git_rev_parse(ref)
449
+ `git rev-parse #{ref}`.strip
450
+ end
451
+
452
+ # Returns the latest tag name, if any
453
+ def git_tag
454
+ `git describe --tags --exact-match 2>/dev/null`.chomp
455
+ end
456
+
457
+ # Returns the current commit sha
458
+ def git_sha
459
+ ENV['TRAVIS_COMMIT'] || `git rev-parse HEAD`.chomp
460
+ end
461
+
462
+ # Returns the local machine's hostname
463
+ def machine_name
464
+ `hostname`.strip
465
+ end
466
+
467
+ # Returns the current Node.js version
468
+ def node_version
469
+ `node -v`.sub(/^v/, '').chomp
470
+ end
471
+
472
+ # Returns the current NPM version
473
+ def npm_version
474
+ `npm --version`
475
+ end
476
+
477
+ # Returns the current Node.js version
478
+ def python_version
479
+ `python --version 2>&1`.sub(/^Python /, '').chomp
480
+ end
481
+
482
+ # Returns true or false depending if the given command can be found
483
+ def which(cmd)
484
+ !`which #{cmd}`.chomp.empty? if cmd
485
+ end
486
+
487
+ # Returns a unique temporary directory name
488
+ def tmp_dir
489
+ @tmp_dir ||= Dir.mktmpdir
490
+ end
491
+
492
+ # Returns the size of the given file path
493
+ def file_size(path)
494
+ File.size(path)
495
+ end
496
+
497
+ def move_files(paths)
498
+ paths.each do |path|
499
+ target = "#{tmp_dir}/#{File.basename(path)}"
500
+ mv(path, target) if File.exist?(path)
501
+ end
502
+ end
503
+
504
+ def unmove_files(paths)
505
+ paths.each do |path|
506
+ source = "#{tmp_dir}/#{File.basename(path)}"
507
+ mv(source, path) if File.exist?(source)
508
+ end
509
+ end
510
+
511
+ def mv(src, dest)
512
+ Kernel.system("sudo mv #{src} #{dest} 2> /dev/null")
513
+ end
514
+
515
+ # Writes the given content to the given file path
516
+ def write_file(path, content, chmod = nil)
517
+ path = File.expand_path(path)
518
+ FileUtils.mkdir_p(File.dirname(path))
519
+ File.open(path, 'w+') { |f| f.write(content) }
520
+ FileUtils.chmod(chmod, path) if chmod
521
+ end
522
+
523
+ # Writes the given machine, login, and password to ~/.netrc
524
+ def write_netrc(machine, login, password)
525
+ require 'netrc'
526
+ netrc = Netrc.read
527
+ netrc[machine] = [login, password]
528
+ netrc.save
529
+ end
530
+
531
+ def sleep(sec)
532
+ Kernel.sleep(sec)
533
+ end
534
+
535
+ def tty?
536
+ $stdout.isatty
537
+ end
538
+
539
+ # Returns a copy of the given hash, reduced to the given keys
540
+ def only(hash, *keys)
541
+ hash.select { |key, _| keys.include?(key) }.to_h
542
+ end
543
+
544
+ def test?
545
+ false
546
+ end
547
+ end
548
+ end
549
+ end