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.
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,207 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dpl
4
+ module Providers
5
+ class Elasticbeanstalk < Provider
6
+ register :elasticbeanstalk
7
+
8
+ status :stable
9
+
10
+ full_name 'AWS Elastic Beanstalk'
11
+
12
+ description sq(<<-STR)
13
+ Deploy to AWS Elastic Beanstalk: https://aws.amazon.com/elasticbeanstalk/
14
+
15
+ This provider:
16
+
17
+ * Creates a zip file (or uses one you provide)
18
+ * Uploads it to your EB application
19
+ * Optionally deploys to a specific EB environment
20
+ * Optionally waits until the deployment finishes
21
+ STR
22
+
23
+ gem 'aws-sdk-elasticbeanstalk', '~> 1'
24
+ gem 'aws-sdk-s3', '~> 1'
25
+ gem 'rubyzip', '~> 2.3', require: 'zip'
26
+ gem 'pathspec', '~> 1.1', require: 'pathspec'
27
+
28
+ env :aws, :elastic_beanstalk
29
+ config '~/.aws/credentials', '~/.aws/config', prefix: 'aws'
30
+
31
+ opt '--access_key_id ID', 'AWS Access Key ID', required: true, secret: true
32
+ opt '--secret_access_key KEY', 'AWS Secret Key', required: true, secret: true
33
+ opt '--region REGION', 'AWS Region the Elastic Beanstalk app is running in', default: 'us-east-1'
34
+ opt '--app NAME', 'Elastic Beanstalk application name', default: :repo_name
35
+ opt '--env NAME', 'Elastic Beanstalk environment name to be updated.'
36
+ opt '--bucket NAME', 'Bucket name to upload app to', required: true, alias: :bucket_name
37
+ opt '--bucket_path PATH', 'Location within Bucket to upload app to'
38
+ opt '--description DESC', 'Description for the application version'
39
+ opt '--label LABEL', 'Label for the application version'
40
+ opt '--zip_file PATH', 'The zip file that you want to deploy. If not given, a zipfile will be created from the current directory, honoring .ebignore and .gitignore.'
41
+ opt '--wait_until_deployed', 'Wait until the deployment has finished', requires: :env
42
+ opt '--wait_until_deployed_timeout SEC', 'How many seconds to wait for Elastic Beanstalk deployment update.', type: :integer, default: 600
43
+ opt '--debug', internal: true
44
+
45
+ msgs login: 'Using Access Key: %{access_key_id}',
46
+ zip_add: 'Adding %s'
47
+
48
+ msgs clean_description: 'Removed non-printable characters from the version description'
49
+
50
+ attr_reader :started, :object, :version
51
+
52
+ def login
53
+ info :login
54
+ end
55
+
56
+ def setup
57
+ info :login
58
+ Aws.config.update(credentials:, region:)
59
+ end
60
+
61
+ def deploy
62
+ @started = Time.now
63
+ bucket.create unless bucket.exists?
64
+ create_zip unless zip_exists?
65
+ upload
66
+ create_version
67
+ update_app if env?
68
+ end
69
+
70
+ def zip_file
71
+ zip_file? ? expand(super) : archive_name
72
+ end
73
+
74
+ def archive_name
75
+ "#{label}.zip"
76
+ end
77
+
78
+ def label
79
+ @label ||= super || "travis-#{git_sha}-#{Time.now.to_i}"
80
+ end
81
+
82
+ def description
83
+ super || git_commit_msg
84
+ end
85
+
86
+ def bucket_path
87
+ bucket_path? ? "#{super.gsub(%r{/*$}, '')}/#{archive_name}" : archive_name
88
+ end
89
+
90
+ def cwd
91
+ @cwd ||= "#{Dir.pwd}/"
92
+ end
93
+
94
+ def zip_exists?
95
+ File.exist?(zip_file)
96
+ end
97
+
98
+ def create_zip
99
+ ::Zip::File.open(zip_file, ::Zip::File::CREATE) do |zip|
100
+ files.each do |path|
101
+ debug :zip_add, path
102
+ zip.add(path.sub(cwd, ''), path)
103
+ end
104
+ end
105
+ end
106
+
107
+ def upload
108
+ @object = bucket.object(bucket_path)
109
+ object.put(body: File.open(zip_file))
110
+ sleep 5 # s3 eventual consistency
111
+ end
112
+
113
+ def create_version
114
+ @version = eb.create_application_version(
115
+ application_name: app,
116
+ version_label: label,
117
+ description: clean(description[0, 200]),
118
+ source_bundle: {
119
+ s3_bucket: bucket.name,
120
+ s3_key: object.key
121
+ },
122
+ auto_create_application: false
123
+ )
124
+ end
125
+
126
+ def update_app
127
+ eb.update_environment(
128
+ environment_name: env,
129
+ version_label: version[:application_version][:version_label]
130
+ )
131
+ wait_until_deployed if wait_until_deployed?
132
+ end
133
+
134
+ def wait_until_deployed
135
+ msgs = []
136
+ 1.upto(wait_until_deployed_timeout / 5) { return if check_deployment(msgs) }
137
+ error 'Deploy status unknown due to timeout. Increase the wait_until_deployed_timeout option'
138
+ end
139
+
140
+ def check_deployment(msgs)
141
+ sleep 5
142
+ events.each do |event|
143
+ msg = "#{event.event_date} [#{event.severity}] #{event.message}"
144
+ error "Deployment failed: #{msg}" if event.severity == 'ERROR'
145
+ info msg unless msgs.include?(msg)
146
+ msgs << msg
147
+ end
148
+ environment[:status] == 'Ready'
149
+ rescue Aws::Errors::ServiceError => e
150
+ info "Caught #{e}: #{e.message}. Retrying ..."
151
+ end
152
+
153
+ def files
154
+ files = Dir.glob('**/*', File::FNM_DOTMATCH)
155
+ ignore = %w[.ebignore .gitignore].detect { |file| file?(file) }
156
+ files = filter(files, ignore) if ignore
157
+ files
158
+ end
159
+
160
+ def filter(files, spec)
161
+ spec = PathSpec.from_filename(spec)
162
+ files.reject { |file| spec.match(file) }
163
+ end
164
+
165
+ def events
166
+ args = { environment_name: env, start_time: started.utc.iso8601 }
167
+ eb.describe_events(args)[:events].reverse
168
+ end
169
+
170
+ def environment
171
+ args = { application_name: app, environment_names: [env] }
172
+ eb.describe_environments(args)[:environments].first
173
+ end
174
+
175
+ def credentials
176
+ Aws::Credentials.new(access_key_id, secret_access_key)
177
+ end
178
+
179
+ def s3
180
+ @s3 ||= Aws::S3::Resource.new
181
+ end
182
+
183
+ def bucket
184
+ @bucket ||= s3.bucket(super)
185
+ end
186
+
187
+ def eb
188
+ @eb ||= Aws::ElasticBeanstalk::Client.new(retry_limit: 10)
189
+ end
190
+
191
+ # We do not actually know what characters are valid on AWS EB's side,
192
+ # see: https://github.com/aws/aws-sdk-ruby/issues/1502
193
+ #
194
+ # Reference: https://www.w3.org/TR/xml/#charsets
195
+ NON_PRINTABLE_CHARS = "\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF"
196
+
197
+ def clean(str)
198
+ str.gsub!(/[^#{NON_PRINTABLE_CHARS}]/, '') && info(:clean_description)
199
+ str
200
+ end
201
+
202
+ def debug(*args)
203
+ info(*args) if debug?
204
+ end
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dpl
4
+ module Providers
5
+ class Engineyard < Provider
6
+ register :engineyard
7
+
8
+ status :alpha
9
+
10
+ description sq(<<-STR)
11
+ tbd
12
+ STR
13
+
14
+ gem 'ey-core', '~> 3.6'
15
+
16
+ required :api_key, %i[email password]
17
+
18
+ env :engineyard, :ey
19
+
20
+ opt '--api_key KEY', 'Engine Yard API key', secret: true, note: 'can be obtained at https://cloud.engineyard.com/cli'
21
+ opt '--email EMAIL', 'Engine Yard account email'
22
+ opt '--password PASS', 'Engine Yard password', secret: true
23
+ opt '--app APP', 'Engine Yard application name', default: :repo_name
24
+ opt '--env ENV', 'Engine Yard application environment', alias: :environment
25
+ opt '--migrate CMD', 'Engine Yard migration commands'
26
+ opt '--account NAME', 'Engine Yard account name'
27
+
28
+ msgs deploy: 'Deploying ...',
29
+ login: 'Authenticating via email and password ...',
30
+ write_rc: 'Authenticating via api token ...',
31
+ authenticated: 'Authenticated as %{name}',
32
+ invalid_migrate: 'Invalid migration command, try --migrate="rake db:migrate"',
33
+ envs: 'Checking environment ...',
34
+ no_env: 'No matching environment found',
35
+ too_many_envs: 'Multiple environments match, please be more specific: %s',
36
+ env_entry: 'environment=%s account=%s'
37
+
38
+ cmds login: "ey-core login << str\n%{email}\n%{password}\nstr",
39
+ whoami: 'ey-core whoami',
40
+ envs: 'ey-core environments',
41
+ deploy: 'ey-core deploy %{deploy_opts}'
42
+
43
+ def login
44
+ api_key? ? write_rc : authenticate
45
+ info :authenticated, name: whoami
46
+ end
47
+
48
+ def validate
49
+ error :invalid_migrate if invalid_migrate?
50
+ env
51
+ end
52
+
53
+ def deploy
54
+ shell :deploy
55
+ end
56
+
57
+ private
58
+
59
+ def authenticate
60
+ shell :login, echo: false, capture: true
61
+ end
62
+
63
+ def whoami
64
+ shell(:whoami, echo: false, capture: true) =~ /email\s*:\s*"(.+)"/ && ::Regexp.last_match(1)
65
+ end
66
+
67
+ def write_rc
68
+ info :write_rc
69
+ write_file '~/.ey-core', "https://api.engineyard.com/: #{api_key}"
70
+ end
71
+
72
+ def invalid_migrate?
73
+ migrate.is_a?(TrueClass) || migrate == 'true'
74
+ end
75
+
76
+ def deploy_opts
77
+ opts = [%(--ref="#{git_sha}" --environment="#{env}")]
78
+ opts << opts_for(%i[app account])
79
+ opts << migrate_opt
80
+ opts.join(' ')
81
+ end
82
+
83
+ def migrate_opt
84
+ migrate? ? opts_for(%i[migrate]) : '--no-migrate'
85
+ end
86
+
87
+ def env
88
+ @env ||= super || detect_env(envs)
89
+ end
90
+
91
+ def detect_env(envs)
92
+ case envs.size
93
+ when 1 then envs.first[:name]
94
+ when 0 then error :no_env
95
+ else too_many_envs(envs)
96
+ end
97
+ end
98
+
99
+ def envs
100
+ lines = shell(:envs, echo: false, capture: true).split("\n")[2..] || []
101
+ envs = lines.map { |line| line.split('|')[1..].map(&:strip) }
102
+ envs = envs.map { |pair| %i[name account].zip(pair).to_h }
103
+ envs.select { |env| env[:name] == opts[:env] } if env?
104
+ envs
105
+ end
106
+
107
+ def too_many_envs(envs)
108
+ envs = envs.map { |env| msg(:env_entry) % env.values_at(:name, :account) }
109
+ error msg(:too_many_envs) % envs.join(', ')
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dpl
4
+ module Providers
5
+ class Firebase < Provider
6
+ register :firebase
7
+
8
+ status :stable
9
+
10
+ description sq(<<-STR)
11
+ tbd
12
+ STR
13
+
14
+ node_js '>= 8.0.0'
15
+
16
+ npm 'firebase-tools@^6.3', 'firebase'
17
+
18
+ path 'node_modules/.bin'
19
+
20
+ env :firebase
21
+
22
+ opt '--token TOKEN', 'Firebase CI access token (generate with firebase login:ci)', required: true, secret: true
23
+ opt '--project NAME', 'Firebase project to deploy to (defaults to the one specified in your firebase.json)'
24
+ opt '--message MSG', 'Message describing this deployment.'
25
+ opt '--only SERVICES', 'Firebase services to deploy', note: 'can be a comma-separated list'
26
+ opt '--force', 'Whether or not to delete Cloud Functions missing from the current working directory'
27
+
28
+ cmds deploy: 'firebase deploy --non-interactive %{deploy_opts}'
29
+ errs deploy: 'Firebase deployment failed'
30
+ msgs missing_config: 'Missing firebase.json'
31
+
32
+ def validate
33
+ error :missing_config unless File.exist?('firebase.json')
34
+ end
35
+
36
+ def deploy
37
+ shell :deploy
38
+ end
39
+
40
+ def deploy_opts
41
+ opts_for(%i[project message token only force])
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dpl
4
+ module Providers
5
+ class Flynn < Provider
6
+ register :flynn
7
+
8
+ status :dev
9
+
10
+ full_name 'Flynn'
11
+
12
+ description sq(<<-STR)
13
+ Flynn provider for Dpl
14
+ STR
15
+
16
+ opt '--git URL', 'Flynn Git remote URL', required: true
17
+
18
+ needs :git, :git_http_user_agent
19
+
20
+ cmds fetch: 'git fetch origin $TRAVIS_BRANCH --unshallow',
21
+ push: 'git push %{remote} HEAD:refs/heads/master -f'
22
+
23
+ def deploy
24
+ shell :fetch, assert: false
25
+ shell :push
26
+ end
27
+
28
+ private
29
+
30
+ def remote
31
+ git
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dpl
4
+ module Providers
5
+ class Gae < Provider
6
+ register :gae
7
+
8
+ status :stable
9
+
10
+ full_name 'Google App Engine'
11
+
12
+ description sq(<<-STR)
13
+ tbd
14
+ STR
15
+
16
+ python '>= 2.7.9'
17
+
18
+ env :gae, :googlecloud, :cloudsdk_core, allow_skip_underscore: true
19
+
20
+ opt '--project ID', 'Project ID used to identify the project on Google Cloud', required: true
21
+ opt '--keyfile FILE', 'Path to the JSON file containing your Service Account credentials in JSON Web Token format. To be obtained via the Google Developers Console. Should be handled with care as it contains authorization keys.', default: 'service-account.json'
22
+ opt '--config FILE', 'Path to your service configuration file', type: :array, default: 'app.yaml'
23
+ opt '--version VER', 'The version of the app that will be created or replaced by this deployment. If you do not specify a version, one will be generated for you'
24
+ opt '--verbosity LEVEL', 'Adjust the log verbosity', default: 'warning'
25
+ opt '--promote', 'Whether to promote the deployed version', default: true
26
+ opt '--stop_previous_version', 'Prevent the deployment from stopping a previously promoted version', default: true
27
+ opt '--install_sdk', 'Whether to install the Google Cloud SDK', default: true
28
+
29
+ URL = 'https://dl.google.com/dl/cloudsdk/channels/rapid/google-cloud-sdk.tar.gz'
30
+
31
+ cmds install: 'curl -L %{URL} | tar xz -C ~ && ~/google-cloud-sdk/install.sh --path-update false --usage-reporting false --command-completion false',
32
+ login: 'gcloud -q auth activate-service-account --key-file %{keyfile}',
33
+ deploy: 'gcloud -q app deploy %{config} %{deploy_opts}',
34
+ cat_logs: 'find $HOME/.config/gcloud/logs -type f -print -exec cat {} \;'
35
+
36
+ errs install: 'Failed to download Google Cloud SDK.',
37
+ login: 'Failed to authenticate.'
38
+
39
+ msgs failed: 'Deployment failed.'
40
+
41
+ path '~/google-cloud-sdk/bin'
42
+
43
+ def install
44
+ return unless install_sdk?
45
+
46
+ shell :install
47
+ end
48
+
49
+ def login
50
+ shell :login
51
+ end
52
+
53
+ def deploy
54
+ shell :deploy
55
+ failed unless success?
56
+ end
57
+
58
+ private
59
+
60
+ def deploy_opts
61
+ opts = [*opts_for(%i[project verbosity version])]
62
+ opts << '--no-promote' unless promote?
63
+ opts << '--no-stop-previous-version' unless stop_previous_version?
64
+ opts.join(' ')
65
+ end
66
+
67
+ def failed
68
+ warn :failed
69
+ shell :cat_logs
70
+ error ''
71
+ end
72
+
73
+ def project
74
+ super || File.dirname(build_dir)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kconv'
4
+
5
+ module Dpl
6
+ module Providers
7
+ class Gcs < Provider
8
+ register :gcs
9
+
10
+ status :stable
11
+
12
+ full_name 'Google Cloud Store'
13
+
14
+ description sq(<<-STR)
15
+ tbd
16
+ STR
17
+
18
+ gem 'mime-types', '~> 3.4.1'
19
+
20
+ python '>= 2.7.9'
21
+
22
+ env :gcs
23
+
24
+ required :key_file, %i[access_key_id secret_access_key]
25
+
26
+ opt '--key_file FILE', 'Path to a GCS service account key JSON file'
27
+ opt '--access_key_id ID', 'GCS Interoperable Access Key ID', secret: true
28
+ opt '--secret_access_key KEY', 'GCS Interoperable Access Secret', secret: true
29
+ opt '--bucket BUCKET', 'GCS Bucket', required: true
30
+ opt '--local_dir DIR', 'Local directory to upload from', default: '.'
31
+ opt '--upload_dir DIR', 'GCS directory to upload to'
32
+ opt '--dot_match', 'Upload hidden files starting with a dot'
33
+ opt '--acl ACL', 'Access control to set for uploaded objects', default: 'private', enum: %w[private public-read public-read-write authenticated-read bucket-owner-read bucket-owner-full-control], see: 'https://cloud.google.com/storage/docs/reference-headers#xgoogacl'
34
+ opt '--detect_encoding', 'HTTP header Content-Encoding to set for files compressed with gzip and compress utilities.'
35
+ opt '--cache_control HEADER', 'HTTP header Cache-Control to suggest that the browser cache the file.', see: 'https://cloud.google.com/storage/docs/xml-api/reference-headers#cachecontrol'
36
+ opt '--glob GLOB', default: '**/*'
37
+
38
+ cmds install: 'curl -L %{URL} | tar xz -C ~ && ~/google-cloud-sdk/install.sh --quiet --path-update false --usage-reporting false --command-completion false',
39
+ login_key: 'gcloud auth activate-service-account --key-file=%{key_file}',
40
+ rsync: 'gsutil %{gs_opts} rsync %{rsync_opts} %{glob} %{target}',
41
+ copy: 'gsutil %{gs_opts} cp %{copy_opts} -r %{source} %{target}'
42
+
43
+ msgs login_key: 'Authenticating with service account key file %{key_file}',
44
+ login_creds: 'Authenticating with access key: %{access_key_id}'
45
+
46
+ errs copy: 'Failed uploading files.'
47
+
48
+ URL = 'https://dl.google.com/dl/cloudsdk/channels/rapid/google-cloud-sdk.tar.gz'
49
+
50
+ BOTO = sq(<<-STR)
51
+ [Credentials]
52
+ gs_access_key_id = %{access_key_id}
53
+ gs_secret_access_key = %{secret_access_key}
54
+ STR
55
+
56
+ path '~/google-cloud-sdk'
57
+ move '/etc/boto.cfg'
58
+
59
+ def install
60
+ shell :install
61
+ end
62
+
63
+ def login
64
+ key_file? ? login_key : login_creds
65
+ end
66
+
67
+ def deploy
68
+ Dir.chdir(local_dir) do
69
+ files.each { |file| copy(file) }
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def login_key
76
+ shell :login_key
77
+ end
78
+
79
+ def login_creds
80
+ info :login_creds
81
+ write_boto
82
+ end
83
+
84
+ def write_boto
85
+ write_file '~/.boto', interpolate(BOTO, opts, secure: true), 0600
86
+ end
87
+
88
+ def files
89
+ Dir.glob(*glob_args).select { |path| File.file?(path) }
90
+ end
91
+
92
+ def copy(source)
93
+ to = [target.sub(%r{/$}, ''), source].join('/')
94
+ shell :copy, gs_opts: gs_opts(source), source:, target: to
95
+ end
96
+
97
+ def dirname(path)
98
+ dir = File.dirname(path)
99
+ dir unless dir.empty? || dir == '.'
100
+ end
101
+
102
+ def gs_opts(path)
103
+ opts = []
104
+ opts << %(-h "Cache-Control:#{cache_control}") if cache_control?
105
+ opts << %(-h "Content-Encoding:#{encoding(path)}") if detect_encoding?
106
+ opts << %(-h "Content-type:#{mime_type(path)}") if mime_type(path)
107
+ "#{opts.join(' ')} " if opts.any?
108
+ end
109
+
110
+ def copy_opts
111
+ opts = []
112
+ opts << %(-a "#{acl}") if acl?
113
+ "#{opts.join(' ')} " if opts.any?
114
+ end
115
+
116
+ def target
117
+ "gs://#{bucket}/#{upload_dir}"
118
+ end
119
+
120
+ def mime_type(path)
121
+ type = MIME::Types.type_for(path).first
122
+ type&.to_s
123
+ end
124
+
125
+ def glob_args
126
+ args = [glob]
127
+ args << File::FNM_DOTMATCH if dot_match?
128
+ args
129
+ end
130
+ end
131
+ end
132
+ end