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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96e4d08173fd6d471b8a0a4f625d5bc1b3cf22af4c6cf2bb71fb3da109bc27af
4
- data.tar.gz: 53ff531ac50e1a925a7c6ddc01bf5cdc7c972333e4f7ef72e5af83b0001c5ae0
3
+ metadata.gz: d11060c760d01bcedf1163619f8412205f95dec58b2fe1f40f58378854e7aefb
4
+ data.tar.gz: a1b8645bb717f084310500ac86e8e1d92c14ac78cc5883019c7de29a737fc1be
5
5
  SHA512:
6
- metadata.gz: dec6c2d62bbe07eb89627dad9c1984133e7768f4b87316ac3029a1fd94f71d22418ca6b3ad73efa445425e60cd620d75d9653b23e4da10c7f28a62b6fc460d69
7
- data.tar.gz: fd1b7ab666de7e4e13300ba4057d265edf919f40abaddcac96a90f9437b8ad23144775f4fc49124bae2af06f668aea57bdf976eeffa1fab60a7e01abb5ffd241
6
+ metadata.gz: edc9d96e02224c1d9520bc382a5415c9425bdc108ff045d6f55d87aca2376985c99749a82d2496c37a18c9066da704bb2625edde5bb00ef870e95e2bd4e3a948
7
+ data.tar.gz: 91c07735699ab92a1ca5f0facb86835a0271e3a5486f8965b45d2dd4f9579268edb240ba3be6f0682c3f341db31fb3e207500ef87284704fedcde301786b839a
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- aptible-cli (0.26.6)
4
+ aptible-cli (0.26.7)
5
5
  activesupport (>= 4.0, < 6.0)
6
6
  aptible-api (~> 1.12)
7
7
  aptible-auth (~> 1.4)
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
- node.list('services') do |services_list|
113
- app.each_service do |service|
114
- services_list.object do |n|
115
- inject_service(n, service, NO_NESTING)
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
- attach_account(node, account)
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
- ResourceFormatter.inject_app(n, app, account)
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'\
@@ -1,5 +1,5 @@
1
1
  module Aptible
2
2
  module CLI
3
- VERSION = '0.26.6'.freeze
3
+ VERSION = '0.26.7'.freeze
4
4
  end
5
5
  end
@@ -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
- it 'includes services in JSON' do
105
- account = Fabricate(:account, handle: 'account')
106
- app = Fabricate(:app, account: account, handle: 'app')
107
- allow(Aptible::Api::Account).to receive(:all).and_return([account])
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
- s1 = Fabricate(
111
- :service,
112
- app: app, process_type: 's1', command: 'true', container_count: 2,
113
- instance_class: 'm5'
114
- )
115
- s2 = Fabricate(
116
- :service,
117
- app: app, process_type: 's2', container_memory_limit_mb: 2048,
118
- instance_class: 'r5'
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
- expected_json = [
122
- {
123
- 'environment' => {
124
- 'id' => account.id,
125
- 'handle' => account.handle,
126
- 'created_at' => fmt_time(account.created_at)
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
- 'service' => s2.process_type,
145
- 'id' => s2.id,
146
- 'command' => 'CMD',
147
- 'container_count' => s2.container_count,
148
- 'container_profile' => 'r',
149
- 'container_size' => s2.container_memory_limit_mb,
150
- 'created_at' => fmt_time(s2.created_at)
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
- subject.send('apps')
161
+ subject.send('apps')
157
162
 
158
- expect(captured_output_json).to eq(expected_json)
159
- end
163
+ expect(captured_output_json).to eq(expected_json)
164
+ end
160
165
 
161
- it 'includes the last deploy operation in JSON' do
162
- account = Fabricate(:account, handle: 'account')
163
- op = Fabricate(:operation, type: 'deploy', status: 'succeeded')
164
- app = Fabricate(:app, account: account, handle: 'app',
165
- last_deploy_operation: op)
166
- allow(Aptible::Api::Account).to receive(:all).and_return([account])
167
- allow(Aptible::Api::App).to receive(:all).and_return([app])
168
-
169
- expected_json = [
170
- {
171
- 'environment' => {
172
- 'id' => account.id,
173
- 'handle' => account.handle,
174
- 'created_at' => fmt_time(account.created_at)
175
- },
176
- 'handle' => app.handle,
177
- 'id' => app.id,
178
- 'status' => app.status,
179
- 'git_remote' => app.git_repo,
180
- 'created_at' => fmt_time(app.created_at),
181
- 'last_deploy_operation' =>
182
- {
183
- 'id' => op.id,
184
- 'status' => op.status,
185
- 'git_ref' => op.git_ref,
186
- 'user_email' => op.user_email,
187
- 'created_at' => op.created_at
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
- 'services' => []
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
- subject.send('apps')
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(captured_output_json).to eq(expected_json)
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
 
@@ -4,5 +4,5 @@ Fabricator(:setting, from: :stub_setting) do
4
4
  settings { {} }
5
5
  sensitive_settings { {} }
6
6
 
7
- after_create { |setting| vhost.settings << setting }
7
+ after_create { |setting| setting.vhost.settings << setting if setting.vhost }
8
8
  end
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.6
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-05-04 00:00:00.000000000 Z
11
+ date: 2026-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport