aptible-cli 0.26.6 → 0.26.7
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +1 -0
- data/lib/aptible/cli/helpers/app.rb +11 -0
- data/lib/aptible/cli/resource_formatter.rb +18 -6
- data/lib/aptible/cli/subcommands/apps.rb +28 -1
- data/lib/aptible/cli/version.rb +1 -1
- data/spec/aptible/cli/subcommands/apps_spec.rb +202 -82
- data/spec/fabricators/app_fabricator.rb +6 -0
- data/spec/fabricators/setting_fabricator.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d11060c760d01bcedf1163619f8412205f95dec58b2fe1f40f58378854e7aefb
|
|
4
|
+
data.tar.gz: a1b8645bb717f084310500ac86e8e1d92c14ac78cc5883019c7de29a737fc1be
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: edc9d96e02224c1d9520bc382a5415c9425bdc108ff045d6f55d87aca2376985c99749a82d2496c37a18c9066da704bb2625edde5bb00ef870e95e2bd4e3a948
|
|
7
|
+
data.tar.gz: 91c07735699ab92a1ca5f0facb86835a0271e3a5486f8965b45d2dd4f9579268edb240ba3be6f0682c3f341db31fb3e207500ef87284704fedcde301786b839a
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -31,6 +31,7 @@ Commands:
|
|
|
31
31
|
aptible apps:deprovision [--app APP] # Deprovision an app
|
|
32
32
|
aptible apps:rename OLD_HANDLE NEW_HANDLE [--environment ENVIRONMENT_HANDLE] # Rename an app handle. In order for the new app handle to appear in log drain and metric drain destinations, you must restart the app.
|
|
33
33
|
aptible apps:scale [--app APP] SERVICE [--container-count COUNT] [--container-size SIZE_MB] [--container-profile PROFILE] # Scale a service
|
|
34
|
+
aptible apps:settings HANDLE # Display deployment related settings for an app
|
|
34
35
|
aptible backup:list DB_HANDLE # List backups for a database
|
|
35
36
|
aptible backup:orphaned # List backups associated with deprovisioned databases
|
|
36
37
|
aptible backup:purge BACKUP_ID # Permanently delete a backup and any copies of it
|
|
@@ -209,6 +209,17 @@ module Aptible
|
|
|
209
209
|
)
|
|
210
210
|
end
|
|
211
211
|
|
|
212
|
+
def current_setting(app)
|
|
213
|
+
setting_link = app.links['current_setting']
|
|
214
|
+
return unless setting_link
|
|
215
|
+
|
|
216
|
+
Aptible::Api::Setting.find_by_url(
|
|
217
|
+
setting_link.href,
|
|
218
|
+
token: fetch_token,
|
|
219
|
+
headers: { 'Prefer' => 'no_sensitive_extras=false' }
|
|
220
|
+
)
|
|
221
|
+
end
|
|
222
|
+
|
|
212
223
|
private
|
|
213
224
|
|
|
214
225
|
def handle_strategies
|
|
@@ -95,11 +95,14 @@ module Aptible
|
|
|
95
95
|
node.value('created_at', operation.created_at)
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
-
def inject_app(node, app, account
|
|
98
|
+
def inject_app(node, app, account, setting = nil,
|
|
99
|
+
include_services: true)
|
|
99
100
|
node.value('id', app.id)
|
|
100
101
|
node.value('handle', app.handle)
|
|
101
102
|
node.value('created_at', app.created_at)
|
|
102
103
|
|
|
104
|
+
attach_account(node, account)
|
|
105
|
+
|
|
103
106
|
node.value('status', app.status)
|
|
104
107
|
node.value('git_remote', app.git_repo)
|
|
105
108
|
|
|
@@ -109,15 +112,24 @@ module Aptible
|
|
|
109
112
|
end
|
|
110
113
|
end
|
|
111
114
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
if include_services
|
|
116
|
+
node.list('services') do |services_list|
|
|
117
|
+
app.each_service do |service|
|
|
118
|
+
services_list.object do |n|
|
|
119
|
+
inject_service(n, service, NO_NESTING)
|
|
120
|
+
end
|
|
116
121
|
end
|
|
117
122
|
end
|
|
118
123
|
end
|
|
119
124
|
|
|
120
|
-
|
|
125
|
+
unless setting.nil?
|
|
126
|
+
node.value('docker_image',
|
|
127
|
+
setting.settings['APTIBLE_DOCKER_IMAGE'])
|
|
128
|
+
node.value('private_registry_username',
|
|
129
|
+
setting.sensitive_settings['APTIBLE_PRIVATE_REGISTRY_USERNAME'])
|
|
130
|
+
node.value('private_registry_password',
|
|
131
|
+
setting.sensitive_settings['APTIBLE_PRIVATE_REGISTRY_PASSWORD'])
|
|
132
|
+
end
|
|
121
133
|
end
|
|
122
134
|
|
|
123
135
|
def inject_database_minimal(node, database, account)
|
|
@@ -26,7 +26,8 @@ module Aptible
|
|
|
26
26
|
accounts.each do |account|
|
|
27
27
|
account.each_app do |app|
|
|
28
28
|
node.object do |n|
|
|
29
|
-
|
|
29
|
+
setting = current_setting(app)
|
|
30
|
+
ResourceFormatter.inject_app(n, app, account, setting)
|
|
30
31
|
end
|
|
31
32
|
end
|
|
32
33
|
end
|
|
@@ -125,6 +126,32 @@ module Aptible
|
|
|
125
126
|
end
|
|
126
127
|
end
|
|
127
128
|
|
|
129
|
+
desc 'apps:settings HANDLE', 'Display deployment related settings for an app'
|
|
130
|
+
option :environment, aliases: '--env'
|
|
131
|
+
define_method 'apps:settings' do |handle|
|
|
132
|
+
telemetry(__method__, options.merge(handle: handle))
|
|
133
|
+
|
|
134
|
+
environment = nil
|
|
135
|
+
if options[:environment]
|
|
136
|
+
environment = environment_from_handle(options[:environment])
|
|
137
|
+
end
|
|
138
|
+
app = app_from_handle(handle, environment)
|
|
139
|
+
|
|
140
|
+
raise Thor::Error, "Could not find app #{handle}" if app.nil?
|
|
141
|
+
|
|
142
|
+
app = with_sensitive(app)
|
|
143
|
+
setting = current_setting(app)
|
|
144
|
+
|
|
145
|
+
Formatter.render(Renderer.current) do |root|
|
|
146
|
+
root.object do |node|
|
|
147
|
+
ResourceFormatter.inject_app(
|
|
148
|
+
node, app, app.account, setting,
|
|
149
|
+
include_services: false
|
|
150
|
+
)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
128
155
|
desc 'apps:rename OLD_HANDLE NEW_HANDLE [--environment'\
|
|
129
156
|
' ENVIRONMENT_HANDLE]', 'Rename an app handle. In order'\
|
|
130
157
|
' for the new app handle to appear in log drain and metric'\
|
data/lib/aptible/cli/version.rb
CHANGED
|
@@ -101,98 +101,218 @@ describe Aptible::CLI::Agent do
|
|
|
101
101
|
.to eq("=== #{account2.handle}\n#{app2.handle}\n")
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
allow(Aptible::Api::App).to receive(:all).and_return([app])
|
|
104
|
+
context 'with JSON output format' do
|
|
105
|
+
around do |example|
|
|
106
|
+
ClimateControl.modify(APTIBLE_OUTPUT_FORMAT: 'json') { example.run }
|
|
107
|
+
end
|
|
109
108
|
|
|
110
|
-
|
|
111
|
-
:
|
|
112
|
-
app:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
109
|
+
it 'includes services in JSON' do
|
|
110
|
+
account = Fabricate(:account, handle: 'account')
|
|
111
|
+
app = Fabricate(:app, account: account, handle: 'app')
|
|
112
|
+
allow(Aptible::Api::Account).to receive(:all).and_return([account])
|
|
113
|
+
allow(Aptible::Api::App).to receive(:all).and_return([app])
|
|
114
|
+
|
|
115
|
+
s1 = Fabricate(
|
|
116
|
+
:service,
|
|
117
|
+
app: app, process_type: 's1', command: 'true', container_count: 2,
|
|
118
|
+
instance_class: 'm5'
|
|
119
|
+
)
|
|
120
|
+
s2 = Fabricate(
|
|
121
|
+
:service,
|
|
122
|
+
app: app, process_type: 's2', container_memory_limit_mb: 2048,
|
|
123
|
+
instance_class: 'r5'
|
|
124
|
+
)
|
|
120
125
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
},
|
|
128
|
-
'handle' => app.handle,
|
|
129
|
-
'id' => app.id,
|
|
130
|
-
'status' => app.status,
|
|
131
|
-
'git_remote' => app.git_repo,
|
|
132
|
-
'created_at' => fmt_time(app.created_at),
|
|
133
|
-
'services' => [
|
|
134
|
-
{
|
|
135
|
-
'service' => s1.process_type,
|
|
136
|
-
'id' => s1.id,
|
|
137
|
-
'command' => s1.command,
|
|
138
|
-
'container_count' => s1.container_count,
|
|
139
|
-
'container_profile' => 'm',
|
|
140
|
-
'container_size' => s1.container_memory_limit_mb,
|
|
141
|
-
'created_at' => fmt_time(s1.created_at)
|
|
126
|
+
expected_json = [
|
|
127
|
+
{
|
|
128
|
+
'environment' => {
|
|
129
|
+
'id' => account.id,
|
|
130
|
+
'handle' => account.handle,
|
|
131
|
+
'created_at' => fmt_time(account.created_at)
|
|
142
132
|
},
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
133
|
+
'handle' => app.handle,
|
|
134
|
+
'id' => app.id,
|
|
135
|
+
'status' => app.status,
|
|
136
|
+
'git_remote' => app.git_repo,
|
|
137
|
+
'created_at' => fmt_time(app.created_at),
|
|
138
|
+
'services' => [
|
|
139
|
+
{
|
|
140
|
+
'service' => s1.process_type,
|
|
141
|
+
'id' => s1.id,
|
|
142
|
+
'command' => s1.command,
|
|
143
|
+
'container_count' => s1.container_count,
|
|
144
|
+
'container_profile' => 'm',
|
|
145
|
+
'container_size' => s1.container_memory_limit_mb,
|
|
146
|
+
'created_at' => fmt_time(s1.created_at)
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
'service' => s2.process_type,
|
|
150
|
+
'id' => s2.id,
|
|
151
|
+
'command' => 'CMD',
|
|
152
|
+
'container_count' => s2.container_count,
|
|
153
|
+
'container_profile' => 'r',
|
|
154
|
+
'container_size' => s2.container_memory_limit_mb,
|
|
155
|
+
'created_at' => fmt_time(s2.created_at)
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
]
|
|
155
160
|
|
|
156
|
-
|
|
161
|
+
subject.send('apps')
|
|
157
162
|
|
|
158
|
-
|
|
159
|
-
|
|
163
|
+
expect(captured_output_json).to eq(expected_json)
|
|
164
|
+
end
|
|
160
165
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
166
|
+
it 'includes the last deploy operation in JSON' do
|
|
167
|
+
account = Fabricate(:account, handle: 'account')
|
|
168
|
+
op = Fabricate(:operation, type: 'deploy', status: 'succeeded')
|
|
169
|
+
app = Fabricate(:app, account: account, handle: 'app',
|
|
170
|
+
last_deploy_operation: op)
|
|
171
|
+
allow(Aptible::Api::Account).to receive(:all).and_return([account])
|
|
172
|
+
allow(Aptible::Api::App).to receive(:all).and_return([app])
|
|
173
|
+
|
|
174
|
+
expected_json = [
|
|
175
|
+
{
|
|
176
|
+
'environment' => {
|
|
177
|
+
'id' => account.id,
|
|
178
|
+
'handle' => account.handle,
|
|
179
|
+
'created_at' => fmt_time(account.created_at)
|
|
180
|
+
},
|
|
181
|
+
'handle' => app.handle,
|
|
182
|
+
'id' => app.id,
|
|
183
|
+
'status' => app.status,
|
|
184
|
+
'git_remote' => app.git_repo,
|
|
185
|
+
'created_at' => fmt_time(app.created_at),
|
|
186
|
+
'last_deploy_operation' =>
|
|
187
|
+
{
|
|
188
|
+
'id' => op.id,
|
|
189
|
+
'status' => op.status,
|
|
190
|
+
'git_ref' => op.git_ref,
|
|
191
|
+
'user_email' => op.user_email,
|
|
192
|
+
'created_at' => op.created_at
|
|
193
|
+
},
|
|
194
|
+
'services' => []
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
subject.send('apps')
|
|
199
|
+
|
|
200
|
+
expect(captured_output_json).to eq(expected_json)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it 'includes docker image and registry settings' do
|
|
204
|
+
account = Fabricate(:account, handle: 'account')
|
|
205
|
+
setting = Fabricate(
|
|
206
|
+
:setting,
|
|
207
|
+
settings: { 'APTIBLE_DOCKER_IMAGE' => 'quay.io/myorg/myapp:latest' },
|
|
208
|
+
sensitive_settings: {
|
|
209
|
+
'APTIBLE_PRIVATE_REGISTRY_USERNAME' => 'registryuser',
|
|
210
|
+
'APTIBLE_PRIVATE_REGISTRY_PASSWORD' => 'registrypass'
|
|
211
|
+
}
|
|
212
|
+
)
|
|
213
|
+
app = Fabricate(:app, account: account, handle: 'app')
|
|
214
|
+
allow(Aptible::Api::Account).to receive(:all).and_return([account])
|
|
215
|
+
allow(subject).to receive(:current_setting).with(app)
|
|
216
|
+
.and_return(setting)
|
|
217
|
+
|
|
218
|
+
expected_json = [
|
|
219
|
+
{
|
|
220
|
+
'environment' => {
|
|
221
|
+
'id' => account.id,
|
|
222
|
+
'handle' => account.handle,
|
|
223
|
+
'created_at' => fmt_time(account.created_at)
|
|
188
224
|
},
|
|
189
|
-
|
|
225
|
+
'handle' => app.handle,
|
|
226
|
+
'id' => app.id,
|
|
227
|
+
'status' => app.status,
|
|
228
|
+
'git_remote' => app.git_repo,
|
|
229
|
+
'created_at' => fmt_time(app.created_at),
|
|
230
|
+
'services' => [],
|
|
231
|
+
'docker_image' => 'quay.io/myorg/myapp:latest',
|
|
232
|
+
'private_registry_username' => 'registryuser',
|
|
233
|
+
'private_registry_password' => 'registrypass'
|
|
234
|
+
}
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
subject.send('apps')
|
|
238
|
+
|
|
239
|
+
expect(captured_output_json).to eq(expected_json)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it 'omits docker image and registry settings when no current_setting' do
|
|
243
|
+
account = Fabricate(:account, handle: 'account')
|
|
244
|
+
app = Fabricate(:app, account: account, handle: 'app')
|
|
245
|
+
allow(Aptible::Api::Account).to receive(:all).and_return([account])
|
|
246
|
+
allow(subject).to receive(:current_setting).with(app)
|
|
247
|
+
.and_return(nil)
|
|
248
|
+
|
|
249
|
+
subject.send('apps')
|
|
250
|
+
|
|
251
|
+
json = captured_output_json
|
|
252
|
+
expect(json.first).not_to have_key('docker_image')
|
|
253
|
+
expect(json.first).not_to have_key('private_registry_username')
|
|
254
|
+
expect(json.first).not_to have_key('private_registry_password')
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
describe '#apps:settings' do
|
|
260
|
+
it 'displays app settings in JSON' do
|
|
261
|
+
ClimateControl.modify(APTIBLE_OUTPUT_FORMAT: 'json') do
|
|
262
|
+
setting = Fabricate(
|
|
263
|
+
:setting,
|
|
264
|
+
settings: { 'APTIBLE_DOCKER_IMAGE' => 'quay.io/myorg/myapp:latest' },
|
|
265
|
+
sensitive_settings: {
|
|
266
|
+
'APTIBLE_PRIVATE_REGISTRY_USERNAME' => 'registryuser',
|
|
267
|
+
'APTIBLE_PRIVATE_REGISTRY_PASSWORD' => 'registrypass'
|
|
268
|
+
}
|
|
269
|
+
)
|
|
270
|
+
allow(subject).to receive(:app_from_handle)
|
|
271
|
+
.with('hello', nil).and_return(app)
|
|
272
|
+
allow(subject).to receive(:current_setting).with(app)
|
|
273
|
+
.and_return(setting)
|
|
274
|
+
|
|
275
|
+
subject.send('apps:settings', 'hello')
|
|
276
|
+
|
|
277
|
+
json = captured_output_json
|
|
278
|
+
expect(json['handle']).to eq('hello')
|
|
279
|
+
expect(json['docker_image']).to eq('quay.io/myorg/myapp:latest')
|
|
280
|
+
expect(json['private_registry_username']).to eq('registryuser')
|
|
281
|
+
expect(json['private_registry_password']).to eq('registrypass')
|
|
282
|
+
expect(json).not_to have_key('services')
|
|
283
|
+
expect(json).not_to have_key('last_deploy_operation')
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
it 'displays app settings in text' do
|
|
288
|
+
setting = Fabricate(
|
|
289
|
+
:setting,
|
|
290
|
+
settings: { 'APTIBLE_DOCKER_IMAGE' => 'quay.io/myorg/myapp:latest' },
|
|
291
|
+
sensitive_settings: {
|
|
292
|
+
'APTIBLE_PRIVATE_REGISTRY_USERNAME' => 'registryuser',
|
|
293
|
+
'APTIBLE_PRIVATE_REGISTRY_PASSWORD' => 'registrypass'
|
|
190
294
|
}
|
|
191
|
-
|
|
295
|
+
)
|
|
296
|
+
allow(subject).to receive(:app_from_handle)
|
|
297
|
+
.with('hello', nil).and_return(app)
|
|
298
|
+
allow(subject).to receive(:current_setting).with(app)
|
|
299
|
+
.and_return(setting)
|
|
300
|
+
|
|
301
|
+
subject.send('apps:settings', 'hello')
|
|
302
|
+
|
|
303
|
+
output = captured_output_text
|
|
304
|
+
expect(output).to include('quay.io/myorg/myapp:latest')
|
|
305
|
+
expect(output).to include('registryuser')
|
|
306
|
+
expect(output).to include('registrypass')
|
|
307
|
+
expect(output).not_to include('services')
|
|
308
|
+
end
|
|
192
309
|
|
|
193
|
-
|
|
310
|
+
it 'raises an error when app is not found' do
|
|
311
|
+
allow(subject).to receive(:app_from_handle)
|
|
312
|
+
.with('nope', nil).and_return(nil)
|
|
194
313
|
|
|
195
|
-
expect(
|
|
314
|
+
expect { subject.send('apps:settings', 'nope') }
|
|
315
|
+
.to raise_error(Thor::Error, /Could not find app nope/)
|
|
196
316
|
end
|
|
197
317
|
end
|
|
198
318
|
|
|
@@ -28,6 +28,7 @@ Fabricator(:app, from: :stub_app) do
|
|
|
28
28
|
services { [] }
|
|
29
29
|
configurations { [] }
|
|
30
30
|
current_configuration { nil }
|
|
31
|
+
current_setting { nil }
|
|
31
32
|
errors { Aptible::Resource::Errors.new }
|
|
32
33
|
created_at { Time.now }
|
|
33
34
|
|
|
@@ -37,6 +38,11 @@ Fabricator(:app, from: :stub_app) do
|
|
|
37
38
|
href: "/accounts/#{attrs[:account].id}"
|
|
38
39
|
)
|
|
39
40
|
}
|
|
41
|
+
if attrs[:current_setting]
|
|
42
|
+
hash[:current_setting] = OpenStruct.new(
|
|
43
|
+
href: "/settings/#{attrs[:current_setting].id}"
|
|
44
|
+
)
|
|
45
|
+
end
|
|
40
46
|
OpenStruct.new(hash)
|
|
41
47
|
end
|
|
42
48
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: aptible-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.26.
|
|
4
|
+
version: 0.26.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Frank Macreery
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|