dpl 1.10.17.travis.6637.6 → 2.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +74 -0
  3. data/CONTRIBUTING.md +392 -0
  4. data/Gemfile +17 -3
  5. data/Gemfile.lock +373 -0
  6. data/LICENSE +16 -19
  7. data/NOTES.md +275 -0
  8. data/README.md +1977 -707
  9. data/Rakefile +2 -2
  10. data/bin/dpl +7 -3
  11. data/lib/dpl.rb +20 -0
  12. data/lib/dpl/assets/atlas/install +19 -0
  13. data/lib/dpl/assets/dpl/README.erb.md +133 -0
  14. data/lib/dpl/assets/dpl/git_ssh +2 -0
  15. data/lib/dpl/assets/git/detect_private_key +8 -0
  16. data/lib/dpl/assets/hephy/filter_log +3 -0
  17. data/lib/dpl/assets/pypi/install +4 -0
  18. data/lib/dpl/assets/scalingo/install +6 -0
  19. data/lib/dpl/cli.rb +36 -48
  20. data/lib/dpl/ctx.rb +2 -0
  21. data/lib/dpl/ctx/bash.rb +543 -0
  22. data/lib/dpl/ctx/test.rb +242 -0
  23. data/lib/dpl/helper/assets.rb +36 -0
  24. data/lib/dpl/helper/cmd.rb +167 -0
  25. data/lib/dpl/helper/config_file.rb +47 -0
  26. data/lib/dpl/helper/env.rb +39 -0
  27. data/lib/dpl/helper/interpolate.rb +126 -0
  28. data/lib/dpl/helper/memoize.rb +20 -0
  29. data/lib/dpl/helper/squiggle.rb +22 -0
  30. data/lib/dpl/helper/zip.rb +69 -0
  31. data/lib/dpl/provider.rb +562 -234
  32. data/lib/dpl/provider/dsl.rb +369 -0
  33. data/lib/dpl/provider/examples.rb +128 -0
  34. data/lib/dpl/provider/status.rb +59 -0
  35. data/lib/dpl/providers.rb +40 -0
  36. data/lib/dpl/providers/anynines.rb +65 -0
  37. data/lib/dpl/providers/atlas.rb +49 -0
  38. data/lib/dpl/providers/azure_web_apps.rb +59 -0
  39. data/lib/dpl/providers/bintray.rb +313 -0
  40. data/lib/dpl/providers/bluemixcloudfoundry.rb +92 -0
  41. data/lib/dpl/providers/boxfuse.rb +48 -0
  42. data/lib/dpl/providers/cargo.rb +19 -0
  43. data/lib/dpl/providers/chef_supermarket.rb +128 -0
  44. data/lib/dpl/providers/cloud66.rb +40 -0
  45. data/lib/dpl/providers/cloudfiles.rb +56 -0
  46. data/lib/dpl/providers/cloudfoundry.rb +81 -0
  47. data/lib/dpl/providers/codedeploy.rb +179 -0
  48. data/lib/dpl/providers/datica.rb +60 -0
  49. data/lib/dpl/providers/elasticbeanstalk.rb +195 -0
  50. data/lib/dpl/providers/engineyard.rb +107 -0
  51. data/lib/dpl/providers/firebase.rb +41 -0
  52. data/lib/dpl/providers/gae.rb +74 -0
  53. data/lib/dpl/providers/gcs.rb +105 -0
  54. data/lib/dpl/providers/hackage.rb +47 -0
  55. data/lib/dpl/providers/hephy.rb +101 -0
  56. data/lib/dpl/providers/heroku.rb +111 -0
  57. data/lib/dpl/providers/heroku/api.rb +119 -0
  58. data/lib/dpl/providers/heroku/git.rb +50 -0
  59. data/lib/dpl/providers/lambda.rb +202 -0
  60. data/lib/dpl/providers/launchpad.rb +74 -0
  61. data/lib/dpl/providers/netlify.rb +30 -0
  62. data/lib/dpl/providers/npm.rb +88 -0
  63. data/lib/dpl/providers/openshift.rb +46 -0
  64. data/lib/dpl/providers/opsworks.rb +142 -0
  65. data/lib/dpl/providers/packagecloud.rb +190 -0
  66. data/lib/dpl/providers/pages.rb +17 -0
  67. data/lib/dpl/providers/pages/api.rb +102 -0
  68. data/lib/dpl/providers/pages/git.rb +251 -0
  69. data/lib/dpl/providers/puppetforge.rb +44 -0
  70. data/lib/dpl/providers/pypi.rb +120 -0
  71. data/lib/dpl/providers/releases.rb +214 -0
  72. data/lib/dpl/providers/rubygems.rb +89 -0
  73. data/lib/dpl/providers/s3.rb +243 -0
  74. data/lib/dpl/providers/scalingo.rb +63 -0
  75. data/lib/dpl/providers/script.rb +28 -0
  76. data/lib/dpl/providers/snap.rb +59 -0
  77. data/lib/dpl/providers/surge.rb +55 -0
  78. data/lib/dpl/providers/testfairy.rb +93 -0
  79. data/lib/dpl/providers/transifex.rb +66 -0
  80. data/lib/dpl/support/aws_sdk_patch.rb +23 -0
  81. data/lib/dpl/support/gems.rb +69 -0
  82. data/lib/dpl/support/gstore_patch.rb +6 -0
  83. data/lib/dpl/support/version.rb +83 -0
  84. data/lib/dpl/version.rb +2 -2
  85. metadata +98 -169
  86. data/.coveralls.yml +0 -1
  87. data/.github/CONTRIBUTING.md +0 -173
  88. data/.github/stale.yml +0 -53
  89. data/.gitignore +0 -13
  90. data/.rspec +0 -2
  91. data/.travis.yml +0 -56
  92. data/dpl-anynines.gemspec +0 -3
  93. data/dpl-atlas.gemspec +0 -3
  94. data/dpl-azure_webapps.gemspec +0 -3
  95. data/dpl-bintray.gemspec +0 -3
  96. data/dpl-bitballoon.gemspec +0 -3
  97. data/dpl-bluemix_cloud_foundry.gemspec +0 -3
  98. data/dpl-boxfuse.gemspec +0 -3
  99. data/dpl-cargo.gemspec +0 -3
  100. data/dpl-catalyze.gemspec +0 -3
  101. data/dpl-chef_supermarket.gemspec +0 -20
  102. data/dpl-cloud66.gemspec +0 -3
  103. data/dpl-cloud_files.gemspec +0 -3
  104. data/dpl-cloud_foundry.gemspec +0 -3
  105. data/dpl-code_deploy.gemspec +0 -3
  106. data/dpl-deis.gemspec +0 -3
  107. data/dpl-elastic_beanstalk.gemspec +0 -3
  108. data/dpl-engine_yard.gemspec +0 -3
  109. data/dpl-firebase.gemspec +0 -3
  110. data/dpl-gae.gemspec +0 -3
  111. data/dpl-gcs.gemspec +0 -3
  112. data/dpl-hackage.gemspec +0 -3
  113. data/dpl-hephy.gemspec +0 -3
  114. data/dpl-heroku.gemspec +0 -3
  115. data/dpl-lambda.gemspec +0 -3
  116. data/dpl-launchpad.gemspec +0 -3
  117. data/dpl-npm.gemspec +0 -3
  118. data/dpl-openshift.gemspec +0 -3
  119. data/dpl-ops_works.gemspec +0 -3
  120. data/dpl-packagecloud.gemspec +0 -3
  121. data/dpl-pages.gemspec +0 -3
  122. data/dpl-puppet_forge.gemspec +0 -3
  123. data/dpl-pypi.gemspec +0 -3
  124. data/dpl-releases.gemspec +0 -8
  125. data/dpl-rubygems.gemspec +0 -3
  126. data/dpl-s3.gemspec +0 -3
  127. data/dpl-scalingo.gemspec +0 -3
  128. data/dpl-script.gemspec +0 -3
  129. data/dpl-snap.gemspec +0 -3
  130. data/dpl-surge.gemspec +0 -3
  131. data/dpl-testfairy.gemspec +0 -3
  132. data/dpl-transifex.gemspec +0 -3
  133. data/dpl.gemspec +0 -3
  134. data/gemspec_helper.rb +0 -51
  135. data/lib/dpl/error.rb +0 -3
  136. data/notes/engine_yard.md +0 -1
  137. data/notes/heroku.md +0 -3
  138. data/spec/cli_spec.rb +0 -36
  139. data/spec/provider_spec.rb +0 -191
  140. data/spec/spec_helper.rb +0 -20
@@ -0,0 +1,179 @@
1
+ module Dpl
2
+ module Providers
3
+ # split this up to CodeDeploy::Github and CodeDeploy::S3 using the
4
+ # revision_type, in order to make opts more strict
5
+ class Codedeploy < Provider
6
+ status :alpha
7
+
8
+ full_name 'AWS Code Deploy'
9
+
10
+ description sq(<<-str)
11
+ tbd
12
+ str
13
+
14
+ gem 'aws-sdk-codedeploy', '~> 1.0'
15
+ gem 'aws-sdk-s3', '~> 1.0'
16
+
17
+ env :aws
18
+ config '~/.aws/credentials', '~/.aws/config', prefix: 'aws'
19
+
20
+ opt '--access_key_id ID', 'AWS access key', required: true, secret: true
21
+ opt '--secret_access_key KEY', 'AWS secret access key', required: true, secret: true
22
+ opt '--application NAME', 'CodeDeploy application name', required: true
23
+ opt '--deployment_group GROUP', 'CodeDeploy deployment group name'
24
+ opt '--revision_type TYPE', 'CodeDeploy revision type', enum: %w(s3 github), downcase: true
25
+ opt '--commit_id SHA', 'Commit ID in case of GitHub'
26
+ opt '--repository NAME', 'Repository name in case of GitHub'
27
+ opt '--bucket NAME', 'S3 bucket in case of S3'
28
+ opt '--region REGION', 'AWS availability zone', default: 'us-east-1'
29
+ opt '--file_exists_behavior STR', 'How to handle files that already exist in a deployment target location', enum: %w(disallow overwrite retain), default: 'disallow'
30
+ opt '--wait_until_deployed', 'Wait until the deployment has finished'
31
+ opt '--bundle_type TYPE'
32
+ opt '--endpoint ENDPOINT'
33
+ opt '--key KEY'
34
+ opt '--description DESCR'
35
+
36
+ msgs login: 'Using Access Key: %{access_key_id}',
37
+ deploy_triggered: 'Deployment triggered: %s',
38
+ register_revision: 'Registering app revision with version=%s, etag=%s',
39
+ waiting_for_deploy: 'Waiting for the deployment to finish ',
40
+ finished_deploy: 'done: %s.',
41
+ description: 'Deploy build %{build_number} via Travis CI',
42
+ missing_bucket: 'Missing required bucket for S3 deployment',
43
+ missing_key: 'Missing required key for S3 deployment',
44
+ unknown_revision_type: 'Unknown revision type %p',
45
+ unknown_bundle_type: 'Unknown bundle type'
46
+
47
+ def login
48
+ info :login
49
+ end
50
+
51
+ def deploy
52
+ register_revision if revision_info[:version]
53
+ id = create_deployment
54
+ info :deploy_triggered, id
55
+ wait_until_deployed(id) if wait_until_deployed?
56
+ rescue Aws::CodeDeploy::Errors::DeploymentLimitExceededException => e
57
+ error e.message
58
+ end
59
+
60
+ def register_revision
61
+ info :register_revision, revision_info[:version], revision_info[:e_tag]
62
+ code_deploy.register_application_revision(
63
+ revision: revision,
64
+ application_name: application,
65
+ description: description
66
+ )
67
+ end
68
+
69
+ def create_deployment
70
+ deployment = code_deploy.create_deployment(
71
+ revision: revision,
72
+ application_name: application,
73
+ deployment_group_name: deployment_group,
74
+ description: description,
75
+ file_exists_behavior: file_exists_behavior.upcase
76
+ )
77
+ deployment.deployment_id
78
+ end
79
+
80
+ def wait_until_deployed(id)
81
+ print :waiting_for_deploy
82
+ status = poll(id) until %w(Succeeded Failed Stopped).include?(status)
83
+ info :finished_deploy, status
84
+ end
85
+
86
+ def poll(id)
87
+ sleep 5
88
+ print '.'
89
+ code_deploy.get_deployment(deployment_id: id)[:deployment_info][:status]
90
+ end
91
+
92
+ def revision_info
93
+ revision[:s3_location] || {}
94
+ end
95
+
96
+ def revision
97
+ @revision ||= case revision_type
98
+ when 's3' then s3_revision
99
+ when 'github' then github_revision
100
+ when nil then bucket? ? s3_revision : github_revision
101
+ else error :unknown_revision_type, revision_type
102
+ end
103
+ end
104
+
105
+ def s3_revision
106
+ {
107
+ revision_type: 'S3',
108
+ s3_location: compact(
109
+ bucket: bucket,
110
+ bundle_type: bundle_type,
111
+ version: revision_version_info[:version_id],
112
+ e_tag: revision_version_info[:etag],
113
+ key: key,
114
+ )
115
+ }
116
+ end
117
+
118
+ def revision_version_info
119
+ s3.head_object(bucket: bucket, key: key)
120
+ rescue Aws::Errors::ServiceError => e
121
+ error e.message
122
+ end
123
+
124
+ def github_revision
125
+ {
126
+ revision_type: 'GitHub',
127
+ git_hub_location: {
128
+ commit_id: commit_id,
129
+ repository: repository
130
+ }
131
+ }
132
+ end
133
+
134
+ def commit_id
135
+ super || git_sha
136
+ end
137
+
138
+ def repository
139
+ super || ENV['TRAVIS_REPO_SLUG'] || File.basename(Dir.pwd)
140
+ end
141
+
142
+ def bucket
143
+ super || error(:missing_bucket)
144
+ end
145
+
146
+ def key
147
+ super || error(:missing_key)
148
+ end
149
+
150
+ def bundle_type
151
+ super || key =~ /\.(tar|tgz|zip)$/ && $1 || error(:unknown_bundle_type)
152
+ end
153
+
154
+ def description
155
+ super || interpolate(msg(:description))
156
+ end
157
+
158
+ def build_number
159
+ ENV['TRAVIS_BUILD_NUMBER']
160
+ end
161
+
162
+ def code_deploy
163
+ @code_deploy ||= Aws::CodeDeploy::Client.new(client_options)
164
+ end
165
+
166
+ def s3
167
+ @s3 ||= Aws::S3::Client.new(client_options)
168
+ end
169
+
170
+ def client_options
171
+ compact(region: region, credentials: credentials, endpoint: endpoint)
172
+ end
173
+
174
+ def credentials
175
+ Aws::Credentials.new(access_key_id, secret_access_key)
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,60 @@
1
+ module Dpl
2
+ module Providers
3
+ class Datica < Provider
4
+ status :dev
5
+
6
+ register :datica, :catalyze
7
+
8
+ description sq(<<-str)
9
+ tbd
10
+ str
11
+
12
+ env :datica, :catalyze
13
+
14
+ opt '--target TARGET', 'The git remote repository to deploy to', required: true
15
+ opt '--path PATH', 'Path to files to deploy', default: '.'
16
+
17
+ needs :git
18
+
19
+ cmds checkout: 'git checkout HEAD',
20
+ add: 'git add %{path} --all --force',
21
+ commit: 'git commit -m "%{message}" --quiet',
22
+ push: 'git push --force %{target} HEAD:master'
23
+
24
+ msgs commit: 'Committing build files for deployment',
25
+ push: 'Deploying to Datica: %{target}'
26
+
27
+ def setup
28
+ commit unless cleanup?
29
+ end
30
+
31
+ def deploy
32
+ shell :push
33
+ end
34
+
35
+ private
36
+
37
+ def commit
38
+ info :commit
39
+ shell :checkout
40
+ shell :add
41
+ shell :commit
42
+ end
43
+
44
+ def message
45
+ vars.empty? ? 'Local build' : 'Build #%s (%s) of %s@%s' % vars
46
+ end
47
+
48
+ VARS = %w(
49
+ TRAVIS_BUILD_NUMBER
50
+ TRAVIS_COMMIT
51
+ TRAVIS_REPO_SLUG
52
+ TRAVIS_BRANCH
53
+ )
54
+
55
+ def vars
56
+ @vars ||= ENV.values_at(*VARS).compact
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,195 @@
1
+ module Dpl
2
+ module Providers
3
+ class Elasticbeanstalk < Provider
4
+ status :alpha
5
+
6
+ full_name 'AWS Elastic Beanstalk'
7
+
8
+ description sq(<<-str)
9
+ tbd
10
+ str
11
+
12
+ gem 'aws-sdk-elasticbeanstalk', '~> 1.0'
13
+ gem 'aws-sdk-s3', '~> 1.0'
14
+ gem 'rubyzip', '~> 1.2.2', require: 'zip'
15
+ gem 'pathspec', '~> 0.2.1', require: 'pathspec'
16
+
17
+ env :aws, :elastic_beanstalk
18
+ config '~/.aws/credentials', '~/.aws/config', prefix: 'aws'
19
+
20
+ opt '--access_key_id ID', 'AWS Access Key ID', required: true, secret: true
21
+ opt '--secret_access_key KEY', 'AWS Secret Key', required: true, secret: true
22
+ opt '--region REGION', 'AWS Region the Elastic Beanstalk app is running in', default: 'us-east-1'
23
+ opt '--app NAME', 'Elastic Beanstalk application name', default: :repo_name
24
+ opt '--env NAME', 'Elastic Beanstalk environment name which will be updated', required: true
25
+ opt '--bucket NAME', 'Bucket name to upload app to', required: true, alias: :bucket_name
26
+ opt '--bucket_path PATH', 'Location within Bucket to upload app to'
27
+ opt '--description DESC', 'Description for the application version'
28
+ opt '--label LABEL', 'Label for the application version'
29
+ opt '--zip_file PATH', 'The zip file that you want to deploy'
30
+ opt '--only_create_app_version', 'Only create the app version, do not actually deploy it'
31
+ opt '--wait_until_deployed', 'Wait until the deployment has finished'
32
+ opt '--debug', internal: true
33
+
34
+ msgs login: 'Using Access Key: %{access_key_id}',
35
+ zip_add: 'Adding %s'
36
+
37
+ msgs clean_description: 'Removed non-printable characters from the version description'
38
+
39
+ attr_reader :started, :object, :version
40
+
41
+ def login
42
+ info :login
43
+ end
44
+
45
+ def setup
46
+ info :login
47
+ Aws.config.update(credentials: credentials, region: region)
48
+ end
49
+
50
+ def deploy
51
+ @started = Time.now
52
+ bucket.create unless bucket.exists?
53
+ create_zip unless zip_exists?
54
+ upload
55
+ create_version
56
+ update_app unless only_create_app_version?
57
+ end
58
+
59
+ def zip_file
60
+ zip_file? ? expand(super) : archive_name
61
+ end
62
+
63
+ def archive_name
64
+ "#{label}.zip"
65
+ end
66
+
67
+ def label
68
+ @label ||= super || "travis-#{git_sha}-#{Time.now.to_i}"
69
+ end
70
+
71
+ def description
72
+ super || git_commit_msg
73
+ end
74
+
75
+ def bucket_path
76
+ bucket_path? ? "#{super.gsub(/\/*$/, '')}/#{archive_name}" : archive_name
77
+ end
78
+
79
+ def cwd
80
+ @cwd ||= "#{Dir.pwd}/"
81
+ end
82
+
83
+ def zip_exists?
84
+ File.exists?(zip_file)
85
+ end
86
+
87
+ def create_zip
88
+ ::Zip::File.open(zip_file, ::Zip::File::CREATE) do |zip|
89
+ files.each do |path|
90
+ debug :zip_add, path
91
+ zip.add(path.sub(cwd, ''), path)
92
+ end
93
+ end
94
+ end
95
+
96
+ def upload
97
+ @object = bucket.object(bucket_path)
98
+ object.put(body: File.open(zip_file))
99
+ sleep 5 # s3 eventual consistency
100
+ end
101
+
102
+ def create_version
103
+ @version = eb.create_application_version(
104
+ application_name: app,
105
+ version_label: label,
106
+ description: clean(description[0, 200]),
107
+ source_bundle: {
108
+ s3_bucket: bucket.name,
109
+ s3_key: object.key
110
+ },
111
+ auto_create_application: false
112
+ )
113
+ end
114
+
115
+ def update_app
116
+ eb.update_environment(
117
+ environment_name: env,
118
+ version_label: version[:application_version][:version_label]
119
+ )
120
+ wait_until_deployed if wait_until_deployed?
121
+ end
122
+
123
+ def wait_until_deployed
124
+ msgs = []
125
+ 1.upto(20) { return if check_deployment(msgs) }
126
+ error 'Too many failures'
127
+ end
128
+
129
+ def check_deployment(msgs)
130
+ sleep 5
131
+ events.each do |event|
132
+ msg = "#{event.event_date} [#{event.severity}] #{event.message}"
133
+ error "Deployment failed: #{msg}" if event.severity == 'ERROR'
134
+ info msg unless msgs.include?(msg)
135
+ msgs << msg
136
+ end
137
+ environment[:status] == 'Ready'
138
+ rescue Aws::Errors::ServiceError => e
139
+ info "Caught #{e}: #{e.message}. Retrying ..."
140
+ end
141
+
142
+ def files
143
+ files = Dir.glob('**/*', File::FNM_DOTMATCH)
144
+ files = filter(files, '.ebignore') if file?('.ebignore')
145
+ files
146
+ end
147
+
148
+ def filter(files, spec)
149
+ spec = PathSpec.from_filename(spec)
150
+ files.reject { |file| spec.match(file) }
151
+ end
152
+
153
+ def events
154
+ args = { environment_name: env, start_time: started.utc.iso8601 }
155
+ eb.describe_events(args)[:events].reverse
156
+ end
157
+
158
+ def environment
159
+ args = { application_name: app, environment_names: [env] }
160
+ eb.describe_environments(args)[:environments].first
161
+ end
162
+
163
+ def credentials
164
+ Aws::Credentials.new(access_key_id, secret_access_key)
165
+ end
166
+
167
+ def s3
168
+ @s3 ||= Aws::S3::Resource.new
169
+ end
170
+
171
+ def bucket
172
+ @bucket ||= s3.bucket(super)
173
+ end
174
+
175
+ def eb
176
+ @eb ||= Aws::ElasticBeanstalk::Client.new(retry_limit: 10)
177
+ end
178
+
179
+ # We do not actually know what characters are valid on AWS EB's side,
180
+ # see: https://github.com/aws/aws-sdk-ruby/issues/1502
181
+ #
182
+ # Reference: https://www.w3.org/TR/xml/#charsets
183
+ NON_PRINTABLE_CHARS = "\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF"
184
+
185
+ def clean(str)
186
+ str.gsub!(/[^#{NON_PRINTABLE_CHARS}]/, '') && info(:clean_description)
187
+ str
188
+ end
189
+
190
+ def debug(*args)
191
+ info *args if debug?
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,107 @@
1
+ module Dpl
2
+ module Providers
3
+ class Engineyard < Provider
4
+ status :alpha
5
+
6
+ description sq(<<-str)
7
+ tbd
8
+ str
9
+
10
+ gem 'ey-core', '~> 3.5'
11
+
12
+ required :api_key, [:email, :password]
13
+
14
+ opt '--api_key KEY', 'Engine Yard API key', secret: true
15
+ opt '--email EMAIL', 'Engine Yard account email'
16
+ opt '--password PASS', 'Engine Yard password', secret: true
17
+ opt '--app APP', 'Engine Yard application name', default: :repo_name
18
+ opt '--env ENV', 'Engine Yard application environment', alias: :environment
19
+ opt '--migrate CMD', 'Engine Yard migration commands'
20
+ opt '--account NAME', 'Engine Yard account name'
21
+
22
+ msgs deploy: 'Deploying ...',
23
+ login: 'Authenticating via email and password ...',
24
+ write_rc: 'Authenticating via api token ...',
25
+ authenticated: 'Authenticated as %{name}',
26
+ invalid_migrate: 'Invalid migration command, try --migrate="rake db:migrate"',
27
+ envs: 'Checking environment ...',
28
+ no_env: 'No matching environment found',
29
+ too_many_envs: 'Multiple environments match, please be more specific: %s',
30
+ env_entry: 'environment=%s account=%s'
31
+
32
+ cmds login: "ey-core login << str\n%{email}\n%{password}\nstr",
33
+ whoami: 'ey-core whoami',
34
+ envs: 'ey-core environments',
35
+ deploy: 'ey-core deploy %{deploy_opts}'
36
+
37
+ def login
38
+ api_key? ? write_rc : authenticate
39
+ info :authenticated, name: whoami
40
+ end
41
+
42
+ def validate
43
+ error :invalid_migrate if invalid_migrate?
44
+ env
45
+ end
46
+
47
+ def deploy
48
+ shell :deploy
49
+ end
50
+
51
+ private
52
+
53
+ def authenticate
54
+ shell :login, echo: false, capture: true
55
+ end
56
+
57
+ def whoami
58
+ shell(:whoami, echo: false, capture: true) =~ /email\s*:\s*"(.+)"/ && $1
59
+ end
60
+
61
+ def write_rc
62
+ info :write_rc
63
+ write_file '~/.ey-core', "https://api.engineyard.com/: #{api_key}"
64
+ end
65
+
66
+ def invalid_migrate?
67
+ migrate.is_a?(TrueClass) || migrate == 'true'
68
+ end
69
+
70
+ def deploy_opts
71
+ opts = [%(--ref="#{git_sha}" --environment="#{env}")]
72
+ opts << opts_for(%i(app account))
73
+ opts << migrate_opt
74
+ opts.join(' ')
75
+ end
76
+
77
+ def migrate_opt
78
+ migrate? ? opts_for(%i(migrate)) : '--no-migrate'
79
+ end
80
+
81
+ def env
82
+ @env ||= super || detect_env(envs)
83
+ end
84
+
85
+ def detect_env(envs)
86
+ case envs.size
87
+ when 1 then envs.first[:name]
88
+ when 0 then error :no_env
89
+ else too_many_envs(envs)
90
+ end
91
+ end
92
+
93
+ def envs
94
+ lines = shell(:envs, echo: false, capture: true).split("\n")[2..-1] || []
95
+ envs = lines.map { |line| line.split('|')[1..-1].map(&:strip) }
96
+ envs = envs.map { |pair| %i(name account).zip(pair).to_h }
97
+ envs.select { |env| env[:name] == opts[:env] } if env?
98
+ envs
99
+ end
100
+
101
+ def too_many_envs(envs)
102
+ envs = envs.map { |env| msg(:env_entry) % env.values_at(:name, :account) }
103
+ error msg(:too_many_envs) % envs.join(', ')
104
+ end
105
+ end
106
+ end
107
+ end