engineyard 2.0.0.rc1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +160 -154
- data/lib/engineyard/cli.rb +19 -7
- data/lib/engineyard/cli/api.rb +67 -44
- data/lib/engineyard/cli/recipes.rb +1 -1
- data/lib/engineyard/cli/web.rb +31 -0
- data/lib/engineyard/commands/deploy.rb +4 -4
- data/lib/engineyard/config.rb +1 -1
- data/lib/engineyard/deploy_config/migrate.rb +4 -7
- data/lib/engineyard/eyrc.rb +2 -6
- data/lib/engineyard/serverside_runner.rb +24 -4
- data/lib/engineyard/thor.rb +2 -2
- data/lib/engineyard/version.rb +1 -1
- data/spec/engineyard/config_spec.rb +1 -1
- data/spec/engineyard/eyrc_spec.rb +1 -1
- data/spec/ey/web/restart_spec.rb +21 -0
- data/spec/spec_helper.rb +0 -4
- data/spec/support/helpers.rb +1 -1
- metadata +42 -19
data/README.rdoc
CHANGED
@@ -10,7 +10,7 @@ Note: Don't add engineyard to your application's Gemfile. The engineyard gem is
|
|
10
10
|
|
11
11
|
== Login
|
12
12
|
|
13
|
-
The first command you run will notice that you are not logged in and will ask you for your
|
13
|
+
The first command you run will notice that you are not logged in and will ask you for your Engine Yard email and password.
|
14
14
|
|
15
15
|
== Configuration
|
16
16
|
|
@@ -27,7 +27,6 @@ The ey.yml file allows options to be saved for each environment to which an appl
|
|
27
27
|
bundle_without: test development mygroup # exclude groups on bundle install
|
28
28
|
copy_exclude: # don't rsync the following dirs
|
29
29
|
- .git
|
30
|
-
verbose: true # always run verbose deploy (unless overriden on command line)
|
31
30
|
maintenance_on_restart: false # show maintenance page during app restart (default: false except for glassfish and mongrel)
|
32
31
|
maintenance_on_migrate: false # show maintenance page during migrations (default: true)
|
33
32
|
precompile_assets: true # enables rails assets precompilation (default: inferred using app/assets and config/application.rb)
|
@@ -37,193 +36,199 @@ Many of the options in ey.yml will only work if the file is committed to your ap
|
|
37
36
|
|
38
37
|
== Commands
|
39
38
|
|
40
|
-
|
41
|
-
ey deploy
|
39
|
+
=== ey deploy
|
42
40
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
-m, [--migrate=MIGRATE] # Run migrations via [MIGRATE], defaults to 'rake db:migrate'; use --no-migrate to avoid running migrations
|
49
|
-
-v, [--verbose] # Be verbose
|
50
|
-
[--ignore-default-branch] # Force a deploy of the specified branch even if a default is set
|
51
|
-
[--ignore-bad-master] # Force a deploy even if the master is in a bad state
|
52
|
-
[--extra-deploy-hook-options key:val] # Additional options to be made available in deploy hooks (in the 'config' hash)
|
53
|
-
# Add more keys as follows: --extra-deploy-hook-options key1:val1 key2:val2
|
41
|
+
This command must be run within the current directory containing the app to be
|
42
|
+
deployed. If ey.yml specifies a default branch then the ref parameter can be
|
43
|
+
omitted. Furthermore, if a default branch is specified but a different
|
44
|
+
command is supplied the deploy will fail unless --ignore-default-branch
|
45
|
+
is used.
|
54
46
|
|
47
|
+
If ey.yml does not specify a default migrate choice, you will be prompted to
|
48
|
+
specify a migration choice. A different command can later be specified via
|
49
|
+
--migrate "ruby do_migrations.rb". Migrations can also be skipped entirely
|
50
|
+
by using --no-migrate.
|
55
51
|
|
56
|
-
|
57
|
-
This command must be run within the current directory containing the app to be
|
58
|
-
deployed. If ey.yml specifies a default branch then the ref parameter can be
|
59
|
-
omitted. Furthermore, if a default branch is specified but a different
|
60
|
-
command is supplied the deploy will fail unless --ignore-default-branch
|
61
|
-
is used.
|
52
|
+
Options:
|
62
53
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
54
|
+
-r, [--ref=REF] [--branch=] [--tag=] # Git ref to deploy. May be a branch, a tag, or a SHA.
|
55
|
+
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
56
|
+
-a, [--app=APP] # Name of the application to deploy
|
57
|
+
-e, [--environment=ENVIRONMENT] # Environment in which to deploy this application
|
58
|
+
-m, [--migrate=MIGRATE] # Run migrations via [MIGRATE], defaults to 'rake db:migrate'; use --no-migrate to avoid running migrations
|
59
|
+
-v, [--verbose] # Be verbose
|
60
|
+
[--ignore-default-branch] # Force a deploy of the specified branch even if a default is set
|
61
|
+
[--ignore-bad-master] # Force a deploy even if the master is in a bad state
|
62
|
+
[--extra-deploy-hook-options key:val] # Additional options to be made available in deploy hooks (in the 'config' hash)
|
63
|
+
# Add more keys as follows: --extra-deploy-hook-options key1:val1 key2:val2
|
67
64
|
|
68
|
-
Command:
|
69
|
-
ey environments
|
70
65
|
|
71
|
-
|
72
|
-
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
73
|
-
-a, [--app=APP] # Name of the application containing the environments
|
74
|
-
-e, [--environment=ENVIRONMENT] # Show only environments matching named environment
|
75
|
-
-s, [--simple] # Print each environment name on its own on a new line
|
76
|
-
-a, [--all] # Show all environments, not just ones associated with this application.
|
66
|
+
=== ey status
|
77
67
|
|
78
|
-
|
79
|
-
By default, environments for this app are displayed. The --all option will display all environments, including those for this app.
|
68
|
+
Show the status of most recent deployment of the specified application and environment. This action only informational and will not change your application.
|
80
69
|
|
81
|
-
|
82
|
-
ey logs
|
70
|
+
Options:
|
83
71
|
|
84
|
-
|
85
|
-
|
86
|
-
|
72
|
+
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
73
|
+
-a, [--app=APP] # Name of the application containing the environment
|
74
|
+
-e, [--environment=ENVIRONMENT] # Name of the environment with the desired deployment
|
87
75
|
|
88
|
-
|
89
|
-
Displays Engine Yard configuration logs for all servers in the environment. If recipes were uploaded to the environment & run, their logs will also be displayed beneath the
|
90
|
-
main configuration logs.
|
76
|
+
=== ey environments
|
91
77
|
|
92
|
-
|
93
|
-
ey rebuild
|
94
|
-
|
95
|
-
Options:
|
96
|
-
-e, [--environment=ENVIRONMENT] # Environment to rebuild
|
97
|
-
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
78
|
+
By default, environments for this app are displayed. The --all option will display all environments, including those for this app.
|
98
79
|
|
99
|
-
|
100
|
-
Engine Yard's main configuration run occurs on all servers. Mainly used to fix failed configuration of new or existing servers, or to update servers to latest Engine Yard stack
|
101
|
-
(e.g. to apply an Engine Yard supplied security patch).
|
80
|
+
Options:
|
102
81
|
|
103
|
-
|
82
|
+
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
83
|
+
-a, [--app=APP] # Name of the application containing the environments
|
84
|
+
-e, [--environment=ENVIRONMENT] # Show only environments matching named environment
|
85
|
+
-s, [--simple] # Print each environment name on its own on a new line
|
86
|
+
-a, [--all] # Show all environments, not just ones associated with this application.
|
104
87
|
|
105
|
-
Command:
|
106
|
-
ey rollback
|
107
88
|
|
108
|
-
|
109
|
-
-v, [--verbose] # Be verbose
|
110
|
-
-a, [--app=APP] # Name of the application to roll back
|
111
|
-
-e, [--environment=ENVIRONMENT] # Environment in which to roll back the application
|
112
|
-
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
89
|
+
=== ey logs
|
113
90
|
|
114
|
-
|
115
|
-
Uses code from previous deploy in the "/data/APP_NAME/releases" directory on remote server(s) to restart application servers.
|
91
|
+
Displays Engine Yard configuration logs for all servers in the environment. If recipes were uploaded to the environment & run, their logs will also be displayed beneath the main configuration logs.
|
116
92
|
|
117
|
-
|
118
|
-
ey recipes apply
|
93
|
+
Options:
|
119
94
|
|
120
|
-
|
121
|
-
|
122
|
-
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
95
|
+
-e, [--environment=ENVIRONMENT] # Environment with the interesting logs
|
96
|
+
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
123
97
|
|
124
|
-
|
125
|
-
This is similar to 'ey rebuild' except Engine Yard's main configuration step is skipped.
|
98
|
+
=== ey rebuild
|
126
99
|
|
127
|
-
|
128
|
-
|
100
|
+
Engine Yard's main configuration run occurs on all servers. Mainly used to fix failed configuration of new or existing servers, or to update servers to latest Engine Yard stack
|
101
|
+
(e.g. to apply an Engine Yard supplied security patch).
|
129
102
|
|
130
|
-
|
131
|
-
-e, [--environment=ENVIRONMENT] # Environment that will receive the recipes
|
132
|
-
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
103
|
+
Note that uploaded recipes are also run after the main configuration run has successfully completed.
|
133
104
|
|
134
|
-
|
135
|
-
The current directory should contain a subdirectory named "cookbooks" to be uploaded.
|
105
|
+
Options:
|
136
106
|
|
137
|
-
|
138
|
-
|
107
|
+
-e, [--environment=ENVIRONMENT] # Environment to rebuild
|
108
|
+
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
139
109
|
|
140
|
-
|
141
|
-
-e, [--environment=ENVIRONMENT] # Environment for which to download the recipes
|
142
|
-
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
110
|
+
=== ey rollback
|
143
111
|
|
144
|
-
|
145
|
-
The recipes will be unpacked into a directory called "cookbooks" in the current directory.
|
112
|
+
Uses code from previous deploy in the "/data/APP_NAME/releases" directory on remote server(s) to restart application servers.
|
146
113
|
|
147
|
-
|
114
|
+
Options:
|
148
115
|
|
149
|
-
|
150
|
-
|
116
|
+
-v, [--verbose] # Be verbose
|
117
|
+
-a, [--app=APP] # Name of the application to roll back
|
118
|
+
-e, [--environment=ENVIRONMENT] # Environment in which to roll back the application
|
119
|
+
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
151
120
|
|
152
|
-
|
153
|
-
-v, [--verbose] # Be verbose
|
154
|
-
-a, [--app=APP] # Name of the application whose maintenance page will be removed
|
155
|
-
-e, [--environment=ENVIRONMENT] # Environment on which to take down the maintenance page
|
156
|
-
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
121
|
+
=== ey recipes apply
|
157
122
|
|
158
|
-
|
123
|
+
This is similar to 'ey rebuild' except Engine Yard's main configuration step is skipped.
|
159
124
|
|
160
|
-
|
161
|
-
ey web disable
|
125
|
+
Options:
|
162
126
|
|
163
|
-
|
164
|
-
|
165
|
-
-a, [--app=APP] # Name of the application whose maintenance page will be put up
|
166
|
-
-e, [--environment=ENVIRONMENT] # Environment on which to put up the maintenance page
|
167
|
-
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
127
|
+
-e, [--environment=ENVIRONMENT] # Environment in which to apply recipes
|
128
|
+
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
168
129
|
|
169
|
-
|
170
|
-
The maintenance page is taken from the app currently being deployed. This means that you can customize maintenance pages to tell users the reason for downtime on every
|
171
|
-
particular deploy.
|
130
|
+
=== ey recipes upload
|
172
131
|
|
173
|
-
|
174
|
-
* public/maintenance.html.custom
|
175
|
-
* public/maintenance.html.tmp
|
176
|
-
* public/maintenance.html
|
177
|
-
* public/system/maintenance.html.default
|
132
|
+
The current directory should contain a subdirectory named "cookbooks" to be uploaded.
|
178
133
|
|
179
|
-
|
180
|
-
ey ssh
|
134
|
+
Options:
|
181
135
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
[--db-master] # Run command on the master database server
|
187
|
-
-a, [--all] # Run command on all servers
|
188
|
-
[--db-slaves] # Run command on the slave database servers
|
189
|
-
-e, [--environment=ENVIRONMENT] # Environment to ssh into
|
190
|
-
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
136
|
+
-e, [--environment=ENVIRONMENT] # Environment that will receive the recipes
|
137
|
+
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
138
|
+
[--apply] # Apply the recipes (same as above) immediately after uploading
|
139
|
+
-f, [--file=FILE] # Specify a gzipped tar file (.tgz) for upload instead of cookbooks/ directory
|
191
140
|
|
192
|
-
|
193
|
-
If a command is supplied, it will be run, otherwise a session will be opened. The application master is used for environments with clusters. Option --all requires a command to
|
194
|
-
be supplied and runs it on all servers.
|
141
|
+
=== ey recipes download
|
195
142
|
|
196
|
-
|
143
|
+
The recipes will be unpacked into a directory called "cookbooks" in the current directory.
|
197
144
|
|
198
|
-
|
145
|
+
If the cookbooks directory already exists, an error will be raised.
|
199
146
|
|
200
|
-
|
201
|
-
ey launch
|
147
|
+
Options:
|
202
148
|
|
203
|
-
|
204
|
-
|
205
|
-
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
149
|
+
-e, [--environment=ENVIRONMENT] # Environment for which to download the recipes
|
150
|
+
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
206
151
|
|
207
|
-
|
208
|
-
Open the application in a browser.
|
152
|
+
=== ey web enable
|
209
153
|
|
210
|
-
|
211
|
-
ey whoami
|
154
|
+
Remove the maintenance page for this application in the given environment.
|
212
155
|
|
213
|
-
|
214
|
-
Who am I logged in as?
|
156
|
+
Options:
|
215
157
|
|
216
|
-
|
217
|
-
|
158
|
+
-v, [--verbose] # Be verbose
|
159
|
+
-a, [--app=APP] # Name of the application whose maintenance page will be removed
|
160
|
+
-e, [--environment=ENVIRONMENT] # Environment on which to take down the maintenance page
|
161
|
+
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
218
162
|
|
219
|
-
|
220
|
-
Log in and verify access to EY Cloud. Use logout first if you need to switch user accounts.
|
163
|
+
=== ey web disable
|
221
164
|
|
222
|
-
|
223
|
-
ey logout
|
165
|
+
The maintenance page is taken from the app currently being deployed. This means that you can customize maintenance pages to tell users the reason for downtime on every particular deploy.
|
224
166
|
|
225
|
-
|
226
|
-
|
167
|
+
Maintenance pages searched for in order of decreasing priority:
|
168
|
+
* public/maintenance.html.custom
|
169
|
+
* public/maintenance.html.tmp
|
170
|
+
* public/maintenance.html
|
171
|
+
* public/system/maintenance.html.default
|
172
|
+
|
173
|
+
Options:
|
174
|
+
|
175
|
+
-v, [--verbose] # Be verbose
|
176
|
+
-a, [--app=APP] # Name of the application whose maintenance page will be put up
|
177
|
+
-e, [--environment=ENVIRONMENT] # Environment on which to put up the maintenance page
|
178
|
+
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
179
|
+
|
180
|
+
|
181
|
+
=== ey web restart
|
182
|
+
|
183
|
+
Restarts the application servers for the given application. Enables maintenance pages if it would be enabled during a normal deploy. Respects the maintenance_on_restart ey.yml configuration.
|
184
|
+
|
185
|
+
Options:
|
186
|
+
|
187
|
+
-v, [--verbose] # Be verbose
|
188
|
+
-a, [--app=APP] # Name of the application to restart
|
189
|
+
-e, [--environment=ENVIRONMENT] # Name of the environment to restart
|
190
|
+
-c, [--account=ACCOUNT] # Name of the account in which the app and environment can be found
|
191
|
+
|
192
|
+
=== ey ssh
|
193
|
+
|
194
|
+
If a command is supplied, it will be run, otherwise a session will be opened. The application master is used for environments with clusters. Option --all requires a command to be supplied and runs it on all servers.
|
195
|
+
|
196
|
+
Note: this command is a bit picky about its ordering. To run a command with arguments on all servers, like "rm -f /some/file", you need to order it like so:
|
197
|
+
|
198
|
+
$ ey ssh "rm -f /some/file" -e my-environment --all
|
199
|
+
|
200
|
+
Options:
|
201
|
+
|
202
|
+
[--utilities=one two three] # Run command on the utility servers with the given names. If no names are given, run on all utility servers.
|
203
|
+
[--app-servers] # Run command on all application servers
|
204
|
+
[--db-servers] # Run command on the database servers
|
205
|
+
[--db-master] # Run command on the master database server
|
206
|
+
-A, [--all] # Run command on all servers
|
207
|
+
[--db-slaves] # Run command on the slave database servers
|
208
|
+
-e, [--environment=ENVIRONMENT] # Name of the environment to ssh into
|
209
|
+
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
210
|
+
|
211
|
+
=== ey launch
|
212
|
+
|
213
|
+
Open the application in a browser.
|
214
|
+
|
215
|
+
Options:
|
216
|
+
|
217
|
+
-c, [--account=ACCOUNT] # Name of the account in which the environment can be found
|
218
|
+
-a, [--app=APP] # Name of the application to launch
|
219
|
+
-e, [--environment=ENVIRONMENT] # Name of the environment for the application
|
220
|
+
|
221
|
+
=== ey whoami
|
222
|
+
|
223
|
+
Who am I logged in as? Prints the name and email of the current logged in user.
|
224
|
+
|
225
|
+
=== ey login
|
226
|
+
|
227
|
+
Log in and verify access to EY Cloud. Use logout first if you need to switch user accounts.
|
228
|
+
|
229
|
+
=== ey logout
|
230
|
+
|
231
|
+
Remove the current API key from ~/.eyrc or env variable $EYRC
|
227
232
|
|
228
233
|
|
229
234
|
== API Client
|
@@ -234,15 +239,16 @@ See https://github.com/engineyard/engineyard-cloud-client for the API client lib
|
|
234
239
|
|
235
240
|
The API commands will print internal information if $DEBUG is set:
|
236
241
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
242
|
+
$ DEBUG=1 ey environments --all
|
243
|
+
GET https://cloud.engineyard.com/api/v2/apps
|
244
|
+
Params {"no_instances"=>"true"}
|
245
|
+
Headers {"User-Agent"=>"EngineYard/2.0.0 EngineYardCloudClient/1.0.5",
|
246
|
+
"Accept"=>"application/json",
|
247
|
+
"X-EY-Cloud-Token"=>"YOURTOKEN"}
|
248
|
+
Response {"apps"=>
|
249
|
+
[{"environments"=>[],
|
250
|
+
"name"=>"myapp",
|
251
|
+
"repository_uri"=>"git@github.com:myaccount/myapp.git",
|
252
|
+
"app_type_id"=>"rails3",
|
253
|
+
"account"=>{"name"=>"myaccount", "id"=>1234},
|
254
|
+
"id"=>12345}]}
|
data/lib/engineyard/cli.rb
CHANGED
@@ -107,8 +107,8 @@ module EY
|
|
107
107
|
deployment.successful = runner.call(out, err)
|
108
108
|
rescue Interrupt
|
109
109
|
err << "Interrupted. Deployment halted.\n"
|
110
|
-
ui.warn "Recording canceled deployment in
|
111
|
-
ui.warn "WARNING: Interrupting again may result in a never-finished deployment in the deployment history on
|
110
|
+
ui.warn "Recording canceled deployment in Engine Yard Cloud..."
|
111
|
+
ui.warn "WARNING: Interrupting again may result in a never-finished deployment in the deployment history on Engine Yard Cloud."
|
112
112
|
raise
|
113
113
|
rescue StandardError => e
|
114
114
|
deployment.err << "Error encountered during deploy.\n#{e.class} #{e}\n"
|
@@ -118,11 +118,11 @@ module EY
|
|
118
118
|
deployment.finished
|
119
119
|
|
120
120
|
if deployment.successful?
|
121
|
-
ui.info "Successful deployment recorded on
|
121
|
+
ui.info "Successful deployment recorded on Engine Yard Cloud"
|
122
122
|
ui.info "Deploy complete"
|
123
123
|
ui.info "Now you can run `ey launch' to open the application in a browser."
|
124
124
|
else
|
125
|
-
ui.info "Failed deployment recorded on
|
125
|
+
ui.info "Failed deployment recorded on Engine Yard Cloud"
|
126
126
|
raise EY::Error, "Deploy failed"
|
127
127
|
end
|
128
128
|
end
|
@@ -204,7 +204,7 @@ module EY
|
|
204
204
|
|
205
205
|
if options[:simple]
|
206
206
|
if apps.size > 1
|
207
|
-
message = "# This app matches multiple Applications in
|
207
|
+
message = "# This app matches multiple Applications in Engine Yard Cloud:\n"
|
208
208
|
apps.each { |app| message << "#\t#{app.name}\n" }
|
209
209
|
message << "# The following environments contain those applications:\n\n"
|
210
210
|
ui.warn(message)
|
@@ -296,7 +296,7 @@ module EY
|
|
296
296
|
method_option :account, :type => :string, :aliases => %w(-c),
|
297
297
|
:required => true, :default => '',
|
298
298
|
:desc => "Name of the account in which the environment can be found"
|
299
|
-
method_option :all, :type => :boolean, :aliases => %(-
|
299
|
+
method_option :all, :type => :boolean, :aliases => %(-A),
|
300
300
|
:desc => "Run command on all servers"
|
301
301
|
method_option :app_servers, :type => :boolean,
|
302
302
|
:desc => "Run command on all application servers"
|
@@ -457,7 +457,19 @@ module EY
|
|
457
457
|
ui.say "#{current_user.name} (#{current_user.email})"
|
458
458
|
end
|
459
459
|
|
460
|
-
desc "login", "Log in and verify access to
|
460
|
+
desc "login", "Log in and verify access to Engine Yard Cloud."
|
461
|
+
long_desc <<-DESC
|
462
|
+
You may run this command to log in to EY Cloud without performing
|
463
|
+
any other action.
|
464
|
+
|
465
|
+
Once you are logged in, a file will be stored at ~/.eyrc with your
|
466
|
+
API token. You may override the location of this file using the
|
467
|
+
$EYRC environment variable.
|
468
|
+
|
469
|
+
Instead of logging in, you may specify a token on the command line
|
470
|
+
with --api-token or using the $ENGINEYARD_API_TOKEN environment
|
471
|
+
variable.
|
472
|
+
DESC
|
461
473
|
def login
|
462
474
|
whoami
|
463
475
|
end
|
data/lib/engineyard/cli/api.rb
CHANGED
@@ -5,71 +5,94 @@ require 'engineyard/eyrc'
|
|
5
5
|
module EY
|
6
6
|
class CLI
|
7
7
|
class API
|
8
|
-
|
9
|
-
ui.info("We need to fetch your API token; please log in.")
|
10
|
-
begin
|
11
|
-
email = ui.ask("Email: ")
|
12
|
-
password = ui.ask("Password: ", true)
|
13
|
-
token = EY::CloudClient.authenticate(email, password, ui)
|
14
|
-
EY::EYRC.load.api_token = token
|
15
|
-
token
|
16
|
-
rescue EY::CloudClient::InvalidCredentials
|
17
|
-
ui.warn "Invalid username or password; please try again."
|
18
|
-
retry
|
19
|
-
end
|
20
|
-
end
|
8
|
+
USER_AGENT = "EngineYard/#{EY::VERSION}"
|
21
9
|
|
22
10
|
attr_reader :token
|
23
11
|
|
24
12
|
def initialize(endpoint, ui, token = nil)
|
13
|
+
@client = EY::CloudClient.new(:endpoint => endpoint, :output => ui.out, :user_agent => USER_AGENT)
|
25
14
|
@ui = ui
|
26
|
-
EY::
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
@source = 'token from --api-token'
|
33
|
-
elsif @token = ENV['ENGINEYARD_API_TOKEN']
|
34
|
-
@specified = true
|
35
|
-
@source = 'token from $ENGINEYARD_API_TOKEN'
|
36
|
-
elsif @token = EY::EYRC.load.api_token
|
37
|
-
@specified = false
|
38
|
-
@source = "api_token in #{EY::EYRC.load.path}"
|
39
|
-
elsif @token = self.class.authenticate(ui)
|
40
|
-
@specified = false
|
41
|
-
@source = 'credentials'
|
42
|
-
else
|
43
|
-
raise EY::Error, "Sorry, we couldn't get your API token."
|
44
|
-
end
|
45
|
-
|
46
|
-
@api = EY::CloudClient.new(@token, @ui)
|
15
|
+
@eyrc = EY::EYRC.load
|
16
|
+
token_from('--api-token') { token } ||
|
17
|
+
token_from('$ENGINEYARD_API_TOKEN') { ENV['ENGINEYARD_API_TOKEN'] } ||
|
18
|
+
token_from(@eyrc.path, false) { @eyrc.api_token } ||
|
19
|
+
authenticate ||
|
20
|
+
token_not_loaded
|
47
21
|
end
|
48
22
|
|
49
23
|
def respond_to?(*a)
|
50
|
-
super or @
|
24
|
+
super or @client.respond_to?(*a)
|
51
25
|
end
|
52
26
|
|
53
27
|
protected
|
54
28
|
|
55
29
|
def method_missing(meth, *args, &block)
|
56
|
-
if @
|
57
|
-
@
|
30
|
+
if @client.respond_to?(meth)
|
31
|
+
with_reauthentication { @client.send(meth, *args, &block) }
|
58
32
|
else
|
59
33
|
super
|
60
34
|
end
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
35
|
+
end
|
36
|
+
|
37
|
+
def with_reauthentication
|
38
|
+
begin
|
39
|
+
yield
|
40
|
+
rescue EY::CloudClient::InvalidCredentials
|
41
|
+
if @specified || !@ui.interactive?
|
42
|
+
# If the token is specified, we raise immediately if it is rejected.
|
43
|
+
raise EY::Error, "Authentication failed: Invalid #{@source}."
|
44
|
+
else
|
45
|
+
@ui.warn "Authentication failed: Invalid #{@source}."
|
46
|
+
authenticate
|
47
|
+
retry
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get the token from the provided block, saving it if it works.
|
53
|
+
# Specified will help us know what to do if loading the token fails.
|
54
|
+
# Returns true if it gets a token.
|
55
|
+
# Returns false if there is no token.
|
56
|
+
def token_from(source, specified = true)
|
57
|
+
token = yield
|
58
|
+
if token
|
59
|
+
@client.token = token
|
60
|
+
@specified = specified
|
61
|
+
@source = "token from #{source}"
|
62
|
+
@token = token
|
63
|
+
true
|
65
64
|
else
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Load the token from EY Cloud if interactive and
|
70
|
+
# token wasn't explicitly specified previously.
|
71
|
+
def authenticate
|
72
|
+
if @specified
|
73
|
+
return false
|
74
|
+
end
|
75
|
+
|
76
|
+
@source = "credentials"
|
77
|
+
@specified = false
|
78
|
+
|
79
|
+
@ui.info("We need to fetch your API token; please log in.")
|
80
|
+
begin
|
81
|
+
email = @ui.ask("Email: ")
|
82
|
+
passwd = @ui.ask("Password: ", true)
|
83
|
+
@token = @client.authenticate!(email, passwd)
|
84
|
+
@eyrc.api_token = @token
|
85
|
+
true
|
86
|
+
rescue EY::CloudClient::InvalidCredentials
|
87
|
+
@ui.warn "Authentication failed. Please try again."
|
69
88
|
retry
|
70
89
|
end
|
71
90
|
end
|
72
91
|
|
92
|
+
# Occurs when all avenues for getting the token are exhausted.
|
93
|
+
def token_not_loaded
|
94
|
+
raise EY::Error, "Sorry, we couldn't get your API token."
|
95
|
+
end
|
73
96
|
end
|
74
97
|
end
|
75
98
|
end
|
@@ -28,7 +28,7 @@ module EY
|
|
28
28
|
"Upload custom chef recipes to specified environment so they can be applied."
|
29
29
|
long_desc <<-DESC
|
30
30
|
Make an archive of the "cookbooks/" subdirectory in your current working
|
31
|
-
directory and upload it to
|
31
|
+
directory and upload it to Engine Yard Cloud's recipe storage.
|
32
32
|
|
33
33
|
Alternatively, specify a .tgz of a cookbooks/ directory yourself as follows:
|
34
34
|
|
data/lib/engineyard/cli/web.rb
CHANGED
@@ -49,6 +49,37 @@ module EY
|
|
49
49
|
ui.info "Putting up maintenance page for '#{app_env.app.name}' in '#{app_env.environment.name}'"
|
50
50
|
serverside_runner(app_env, options[:verbose]).put_up_maintenance_page.call(ui.out, ui.err)
|
51
51
|
end
|
52
|
+
|
53
|
+
desc "restart [--environment ENVIRONMENT]",
|
54
|
+
"Restart the application servers without deploying."
|
55
|
+
long_desc <<-DESC
|
56
|
+
Restarts the application servers (e.g. Passenger, Unicorn, etc).
|
57
|
+
|
58
|
+
Respects the maintenance_on_restart settings in the application's ey.yml.
|
59
|
+
|
60
|
+
Note: Uses the version of the ey.yml currently checked out on the servers.
|
61
|
+
DESC
|
62
|
+
method_option :environment, :type => :string, :aliases => %w(-e),
|
63
|
+
:required => true, :default => false,
|
64
|
+
:desc => "Environment in which to deploy this application"
|
65
|
+
method_option :app, :type => :string, :aliases => %w(-a),
|
66
|
+
:required => true, :default => '',
|
67
|
+
:desc => "Name of the application to deploy"
|
68
|
+
method_option :account, :type => :string, :aliases => %w(-c),
|
69
|
+
:required => true, :default => '',
|
70
|
+
:desc => "Name of the account in which the environment can be found"
|
71
|
+
method_option :verbose, :type => :boolean, :aliases => %w(-v),
|
72
|
+
:desc => "Be verbose"
|
73
|
+
def restart
|
74
|
+
app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
|
75
|
+
ui.info "Restarting servers on #{app_env.hierarchy_name}"
|
76
|
+
if serverside_runner(app_env, options[:verbose]).restart.call(ui.out, ui.err)
|
77
|
+
ui.info "Restart complete"
|
78
|
+
else
|
79
|
+
raise EY::Error, "Restart failed"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
52
83
|
end
|
53
84
|
end
|
54
85
|
end
|
@@ -36,8 +36,8 @@ class EY
|
|
36
36
|
deployment.successful = runner.call(out, err)
|
37
37
|
rescue Interrupt
|
38
38
|
err << "Interrupted. Deployment halted.\n"
|
39
|
-
ui.warn "Recording canceled deployment in
|
40
|
-
ui.warn "WARNING: Interrupting again may result in a never-finished deployment in the deployment history on
|
39
|
+
ui.warn "Recording canceled deployment in Engine Yard Cloud..."
|
40
|
+
ui.warn "WARNING: Interrupting again may result in a never-finished deployment in the deployment history on Engine Yard Cloud."
|
41
41
|
raise
|
42
42
|
rescue StandardError => e
|
43
43
|
deployment.err << "Error encountered during deploy.\n#{e.class} #{e}\n"
|
@@ -47,11 +47,11 @@ class EY
|
|
47
47
|
deployment.finished
|
48
48
|
|
49
49
|
if deployment.successful?
|
50
|
-
ui.info "Successful deployment recorded on
|
50
|
+
ui.info "Successful deployment recorded on Engine Yard Cloud"
|
51
51
|
ui.info "Deploy complete"
|
52
52
|
ui.info "Now you can run `ey launch' to open the application in a browser."
|
53
53
|
else
|
54
|
-
ui.info "Failed deployment recorded on
|
54
|
+
ui.info "Failed deployment recorded on Engine Yard Cloud"
|
55
55
|
raise EY::Error, "Deploy failed"
|
56
56
|
end
|
57
57
|
end
|
data/lib/engineyard/config.rb
CHANGED
@@ -90,18 +90,15 @@ module EY
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def perform_from_interaction
|
93
|
-
ui.warn "
|
94
|
-
ui.
|
95
|
-
ui.warn "Migrate can be toggled per-deploy using --migrate or --no-migrate."
|
96
|
-
ui.warn "Let's set a default migration choice."
|
97
|
-
ui.warn "**********************************************************************"
|
98
|
-
@perform = ui.agree('Migrate every deploy by default? ', true)
|
93
|
+
ui.warn "Please choose a default migration behavior for this environment."
|
94
|
+
@perform = ui.agree("Run migrations by default on #{env_config.name}? ", true)
|
99
95
|
env_config.migrate = @perform
|
100
96
|
if @perform
|
101
97
|
command_from_interaction
|
102
98
|
end
|
103
99
|
ui.say "#{env_config.path}: migrate settings saved for #{env_config.name}."
|
104
|
-
ui.
|
100
|
+
ui.say "You can override this default with --migrate or --no-migrate."
|
101
|
+
ui.info "Please git commit #{env_config.path} with these new changes."
|
105
102
|
true
|
106
103
|
rescue Timeout::Error
|
107
104
|
@perform = nil
|
data/lib/engineyard/eyrc.rb
CHANGED
@@ -2,14 +2,14 @@ module EY
|
|
2
2
|
class EYRC
|
3
3
|
attr_reader :path
|
4
4
|
|
5
|
-
DEFAULT_PATH = "
|
5
|
+
DEFAULT_PATH = "#{ENV['HOME']}/.eyrc"
|
6
6
|
|
7
7
|
def self.load
|
8
8
|
new(ENV['EYRC'] || DEFAULT_PATH)
|
9
9
|
end
|
10
10
|
|
11
11
|
def initialize(path)
|
12
|
-
|
12
|
+
@path = Pathname.new(path).expand_path
|
13
13
|
end
|
14
14
|
|
15
15
|
def exist?
|
@@ -30,10 +30,6 @@ module EY
|
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
-
def path=(p)
|
34
|
-
@path = Pathname.new(p).expand_path
|
35
|
-
end
|
36
|
-
|
37
33
|
def [](key)
|
38
34
|
read_data[key.to_s]
|
39
35
|
end
|
@@ -10,6 +10,7 @@ module EY
|
|
10
10
|
@username = environment.username
|
11
11
|
@hostname = bridge
|
12
12
|
@command = nil
|
13
|
+
@acc_env_name = "#{app.account.name}/#{environment.name}"
|
13
14
|
end
|
14
15
|
|
15
16
|
def deploy(&block)
|
@@ -22,6 +23,11 @@ module EY
|
|
22
23
|
self
|
23
24
|
end
|
24
25
|
|
26
|
+
def restart(&block)
|
27
|
+
@command = @adapter.restart(&block)
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
25
31
|
def put_up_maintenance_page(&block)
|
26
32
|
@command = @adapter.enable_maintenance(&block)
|
27
33
|
self
|
@@ -89,16 +95,30 @@ module EY
|
|
89
95
|
begin
|
90
96
|
ssh(cmd, @hostname, @username, out, err)
|
91
97
|
rescue Net::SSH::AuthenticationFailed
|
92
|
-
raise EY::Error,
|
98
|
+
raise EY::Error, <<-ERROR
|
99
|
+
Authentication Failed. Things to fix:
|
100
|
+
1. Add your SSH key to your local SSH agent with `ssh-add path/to/key`.
|
101
|
+
2. Add your SSH key to #{@acc_env_name} on Engine Yard Cloud and apply the changes.
|
102
|
+
(https://support.cloud.engineyard.com/entries/20996846-set-up-ssh-keys)
|
103
|
+
ERROR
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def net_ssh_options
|
109
|
+
level = :fatal # default in Net::SSH
|
110
|
+
if debug = ENV["DEBUG"]
|
111
|
+
level = :info
|
112
|
+
if %w[debug info warn error fatal].include?(debug.downcase)
|
113
|
+
level = debug.downcase.to_sym
|
93
114
|
end
|
94
115
|
end
|
116
|
+
{:paranoid => false, :verbose => level}
|
95
117
|
end
|
96
118
|
|
97
119
|
def ssh(cmd, hostname, username, out, err)
|
98
120
|
exit_code = 1
|
99
|
-
|
100
|
-
options_for_ssh[:verbose] = ENV["DEBUG"].downcase.to_sym if ENV["DEBUG"]
|
101
|
-
Net::SSH.start(hostname, username, options_for_ssh) do |net_ssh|
|
121
|
+
Net::SSH.start(hostname, username, net_ssh_options) do |net_ssh|
|
102
122
|
net_ssh.open_channel do |channel|
|
103
123
|
channel.exec cmd do |_, success|
|
104
124
|
unless success
|
data/lib/engineyard/thor.rb
CHANGED
@@ -40,7 +40,7 @@ module EY
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def fetch_environment(environment_name, account_name)
|
43
|
-
ui.info "Loading application data from
|
43
|
+
ui.info "Loading application data from Engine Yard Cloud..."
|
44
44
|
|
45
45
|
environment_name ||= use_default_environment
|
46
46
|
remotes = repo.remotes if in_repo?
|
@@ -72,7 +72,7 @@ module EY
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def fetch_app_environment(app_name, environment_name, account_name)
|
75
|
-
ui.info "Loading application data from
|
75
|
+
ui.info "Loading application data from Engine Yard Cloud..."
|
76
76
|
|
77
77
|
environment_name ||= use_default_environment
|
78
78
|
remotes = repo.remotes if in_repo?
|
data/lib/engineyard/version.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "ey web restart" do
|
4
|
+
given "integration"
|
5
|
+
|
6
|
+
def command_to_run(opts)
|
7
|
+
cmd = %w[web restart]
|
8
|
+
cmd << "-e" << opts[:environment] if opts[:environment]
|
9
|
+
cmd << "-a" << opts[:app] if opts[:app]
|
10
|
+
cmd << "-c" << opts[:account] if opts[:account]
|
11
|
+
cmd << "--verbose" if opts[:verbose]
|
12
|
+
cmd
|
13
|
+
end
|
14
|
+
|
15
|
+
def verify_ran(scenario)
|
16
|
+
@ssh_commands.should have_command_like(/engineyard-serverside.*restart.*--app #{scenario[:application]}/)
|
17
|
+
end
|
18
|
+
|
19
|
+
include_examples "it takes an environment name and an app name and an account name"
|
20
|
+
include_examples "it invokes engineyard-serverside"
|
21
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/helpers.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: engineyard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0
|
5
|
-
prerelease:
|
4
|
+
version: 2.0.0
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
|
-
-
|
8
|
+
- Engine Yard Cloud Team
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-08-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|
@@ -98,7 +98,7 @@ dependencies:
|
|
98
98
|
requirements:
|
99
99
|
- - '='
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version: 2.0.0
|
101
|
+
version: 2.0.0
|
102
102
|
type: :runtime
|
103
103
|
prerelease: false
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -106,7 +106,7 @@ dependencies:
|
|
106
106
|
requirements:
|
107
107
|
- - '='
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version: 2.0.0
|
109
|
+
version: 2.0.0
|
110
110
|
- !ruby/object:Gem::Dependency
|
111
111
|
name: engineyard-cloud-client
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,7 +114,7 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - ~>
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 1.0.
|
117
|
+
version: 1.0.5
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -122,7 +122,7 @@ dependencies:
|
|
122
122
|
requirements:
|
123
123
|
- - ~>
|
124
124
|
- !ruby/object:Gem::Version
|
125
|
-
version: 1.0.
|
125
|
+
version: 1.0.5
|
126
126
|
- !ruby/object:Gem::Dependency
|
127
127
|
name: net-ssh
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|
@@ -144,17 +144,17 @@ dependencies:
|
|
144
144
|
requirement: !ruby/object:Gem::Requirement
|
145
145
|
none: false
|
146
146
|
requirements:
|
147
|
-
- -
|
147
|
+
- - '='
|
148
148
|
- !ruby/object:Gem::Version
|
149
|
-
version:
|
149
|
+
version: 2.1.0
|
150
150
|
type: :runtime
|
151
151
|
prerelease: false
|
152
152
|
version_requirements: !ruby/object:Gem::Requirement
|
153
153
|
none: false
|
154
154
|
requirements:
|
155
|
-
- -
|
155
|
+
- - '='
|
156
156
|
- !ruby/object:Gem::Version
|
157
|
-
version:
|
157
|
+
version: 2.1.0
|
158
158
|
- !ruby/object:Gem::Dependency
|
159
159
|
name: rspec
|
160
160
|
requirement: !ruby/object:Gem::Requirement
|
@@ -366,6 +366,7 @@ files:
|
|
366
366
|
- spec/ey/status_spec.rb
|
367
367
|
- spec/ey/web/disable_spec.rb
|
368
368
|
- spec/ey/web/enable_spec.rb
|
369
|
+
- spec/ey/web/restart_spec.rb
|
369
370
|
- spec/ey/whoami_spec.rb
|
370
371
|
- spec/spec_helper.rb
|
371
372
|
- spec/support/bundled_ey
|
@@ -377,11 +378,26 @@ files:
|
|
377
378
|
- spec/support/shared_behavior.rb
|
378
379
|
homepage: http://github.com/engineyard/engineyard
|
379
380
|
licenses: []
|
380
|
-
post_install_message: ! "
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
381
|
+
post_install_message: ! "Thank you for downloading engineyard gem version 2!\n\n############
|
382
|
+
NOTICE when upgrading from 1.x to 2.x ############\n\nFirst, if you have any trouble,
|
383
|
+
you can uninstall this version to downgrade.\nThis version of the CLI does not affect
|
384
|
+
the web deploy system.\nPlease file a support ticket if you have any issues at:
|
385
|
+
support.cloud.engineyard.com\n\n* IMPORTANT: eydeploy.rb modifications MUST be verified
|
386
|
+
in staging!\n Many internals have changed that could break eydeploy.rb!\n* The
|
387
|
+
order of compile_assets has *changed*. It now runs before\n the maintenance page
|
388
|
+
is enabled, to reduce downtime.\n* ey.yml files should be checked in to your repository.\n*
|
389
|
+
Default bundler version is now 1.1.5.\n\nA few other nice changes:\n\n* All CLI
|
390
|
+
commands are significantly faster. Woo!\n* Deploy hooks now have access to `account_name`
|
391
|
+
and `environment_name`.\n* Deploy hooks can now use run! and sudo! to abort on failure.\n*
|
392
|
+
Supports new ey.yml options when ey.yml is committed:\n * maintenance_on_restart:
|
393
|
+
true/false (default: false except for glassfish and mongrel)\n * maintenance_on_migrate:
|
394
|
+
true/false (default: true)\n * precompile_assets: true/false (default: inferred
|
395
|
+
using app/assets and config/application.rb)\n * ignore_database_adapter_warning:
|
396
|
+
true (hides adapter warning)\n* Don't remove maintenance pages that weren't put
|
397
|
+
up during this deploy if maintenance options (above) are set to false.\n* ey web
|
398
|
+
restart - restarts web servers without deploying.\n\nDeploying for the first time?
|
399
|
+
The Engine Yard Pandas want to help you!\nEmail pandas@engineyard.com with your
|
400
|
+
questions.\n"
|
385
401
|
rdoc_options: []
|
386
402
|
require_paths:
|
387
403
|
- lib
|
@@ -391,12 +407,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
391
407
|
- - ! '>='
|
392
408
|
- !ruby/object:Gem::Version
|
393
409
|
version: '0'
|
410
|
+
segments:
|
411
|
+
- 0
|
412
|
+
hash: -4522439113898007272
|
394
413
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
395
414
|
none: false
|
396
415
|
requirements:
|
397
|
-
- - ! '
|
416
|
+
- - ! '>='
|
398
417
|
- !ruby/object:Gem::Version
|
399
|
-
version:
|
418
|
+
version: '0'
|
419
|
+
segments:
|
420
|
+
- 0
|
421
|
+
hash: -4522439113898007272
|
400
422
|
requirements: []
|
401
423
|
rubyforge_project:
|
402
424
|
rubygems_version: 1.8.24
|
@@ -426,6 +448,7 @@ test_files:
|
|
426
448
|
- spec/ey/status_spec.rb
|
427
449
|
- spec/ey/web/disable_spec.rb
|
428
450
|
- spec/ey/web/enable_spec.rb
|
451
|
+
- spec/ey/web/restart_spec.rb
|
429
452
|
- spec/ey/whoami_spec.rb
|
430
453
|
- spec/spec_helper.rb
|
431
454
|
- spec/support/bundled_ey
|