bosh_cli 1.3215.4.0 → 1.3232.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cli.rb +1 -0
- data/lib/cli/base_command.rb +1 -1
- data/lib/cli/basic_login_strategy.rb +3 -3
- data/lib/cli/blob_manager.rb +14 -14
- data/lib/cli/client/director.rb +16 -5
- data/lib/cli/commands/backup.rb +2 -2
- data/lib/cli/commands/cloudcheck.rb +2 -2
- data/lib/cli/commands/deployment.rb +22 -22
- data/lib/cli/commands/deployment_diff.rb +7 -3
- data/lib/cli/commands/disks.rb +1 -1
- data/lib/cli/commands/errand.rb +2 -2
- data/lib/cli/commands/events.rb +55 -0
- data/lib/cli/commands/help.rb +1 -1
- data/lib/cli/commands/job.rb +3 -3
- data/lib/cli/commands/job_management.rb +1 -1
- data/lib/cli/commands/locks.rb +6 -4
- data/lib/cli/commands/login.rb +1 -1
- data/lib/cli/commands/misc.rb +4 -4
- data/lib/cli/commands/package.rb +3 -3
- data/lib/cli/commands/property_management.rb +8 -8
- data/lib/cli/commands/release/delete_release.rb +3 -3
- data/lib/cli/commands/release/export_release.rb +2 -2
- data/lib/cli/commands/release/finalize_release.rb +26 -12
- data/lib/cli/commands/release/upload_release.rb +2 -2
- data/lib/cli/commands/release/verify_release.rb +2 -2
- data/lib/cli/commands/restore.rb +3 -3
- data/lib/cli/commands/snapshot.rb +5 -5
- data/lib/cli/commands/ssh.rb +2 -2
- data/lib/cli/commands/stemcell.rb +8 -8
- data/lib/cli/commands/task.rb +4 -4
- data/lib/cli/commands/user.rb +3 -3
- data/lib/cli/commands/vms.rb +1 -1
- data/lib/cli/config.rb +1 -1
- data/lib/cli/core_ext.rb +4 -4
- data/lib/cli/deployment_manifest_compiler.rb +1 -1
- data/lib/cli/file_with_progress_bar.rb +2 -0
- data/lib/cli/job_property_collection.rb +2 -2
- data/lib/cli/job_state.rb +2 -2
- data/lib/cli/logs_downloader.rb +1 -1
- data/lib/cli/manifest.rb +3 -3
- data/lib/cli/public_stemcell_presenter.rb +3 -3
- data/lib/cli/release.rb +3 -3
- data/lib/cli/release_compiler.rb +2 -2
- data/lib/cli/release_tarball.rb +2 -1
- data/lib/cli/runner.rb +1 -1
- data/lib/cli/sorted_release_archiver.rb +1 -12
- data/lib/cli/uaa_login_strategy.rb +1 -1
- data/lib/cli/version.rb +1 -1
- data/lib/cli/versions/local_artifact_storage.rb +2 -2
- data/lib/cli/versions/releases_dir_migrator.rb +3 -3
- data/lib/cli/versions/version_file_resolver.rb +1 -1
- data/lib/cli/versions/versions_index.rb +10 -11
- metadata +9 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4e190149bfe9bd224bbe10c2be32ad11eeeef44
|
4
|
+
data.tar.gz: a43e138fbea15832c7233b3f41dbf7949478a2e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b48f84f6cb4c4659978f95d039616b4bf21f8e04288e17fc9da4b4de4cdb6c506b0af956254698ee9e142c49bd6ec529c4fcc8983a6ed1447b86e18ce068ce4a
|
7
|
+
data.tar.gz: 7d53af233d4ecf0320865ca91b6217ebfb51d42e45c5870cfded371d81d5122aaf3833a3e3be6ae2ef28b9aea720f62e5103c321b231e9faec9e5616b1cd7c62
|
data/lib/cli.rb
CHANGED
data/lib/cli/base_command.rb
CHANGED
@@ -22,7 +22,7 @@ module Bosh
|
|
22
22
|
end
|
23
23
|
|
24
24
|
if @director.login(username, password)
|
25
|
-
@terminal.say_green("Logged in as
|
25
|
+
@terminal.say_green("Logged in as '#{username}'")
|
26
26
|
@config.set_credentials(target, {
|
27
27
|
"username" => username,
|
28
28
|
"password" => password
|
@@ -30,10 +30,10 @@ module Bosh
|
|
30
30
|
@config.save
|
31
31
|
else
|
32
32
|
if @interactive
|
33
|
-
@terminal.say_red("Cannot log in as
|
33
|
+
@terminal.say_red("Cannot log in as '#{username}', please try again")
|
34
34
|
login(target, username, '')
|
35
35
|
else
|
36
|
-
err("Cannot log in as
|
36
|
+
err("Cannot log in as '#{username}'")
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
data/lib/cli/blob_manager.rb
CHANGED
@@ -36,7 +36,7 @@ module Bosh::Cli
|
|
36
36
|
|
37
37
|
@src_dir = File.join(@release.dir, "src")
|
38
38
|
unless File.directory?(@src_dir)
|
39
|
-
err("
|
39
|
+
err("'src' directory is missing")
|
40
40
|
end
|
41
41
|
|
42
42
|
@storage_dir = File.join(@release.dir, ".blobs")
|
@@ -96,7 +96,7 @@ module Bosh::Cli
|
|
96
96
|
end
|
97
97
|
|
98
98
|
nl
|
99
|
-
say("When ready please run
|
99
|
+
say("When ready please run '#{"bosh upload blobs".make_green}'")
|
100
100
|
end
|
101
101
|
|
102
102
|
# Registers a file as BOSH blob
|
@@ -105,11 +105,11 @@ module Bosh::Cli
|
|
105
105
|
# @return [void]
|
106
106
|
def add_blob(local_path, blob_path)
|
107
107
|
unless File.exists?(local_path)
|
108
|
-
err("File
|
108
|
+
err("File '#{local_path}' not found")
|
109
109
|
end
|
110
110
|
|
111
111
|
if File.directory?(local_path)
|
112
|
-
err("
|
112
|
+
err("'#{local_path}' is a directory")
|
113
113
|
end
|
114
114
|
|
115
115
|
if blob_path[0..0] == "/"
|
@@ -117,19 +117,19 @@ module Bosh::Cli
|
|
117
117
|
end
|
118
118
|
|
119
119
|
if blob_path[0..5] == "blobs/"
|
120
|
-
err("Blob path should not start with
|
120
|
+
err("Blob path should not start with 'blobs/'")
|
121
121
|
end
|
122
122
|
|
123
123
|
blob_dst = File.join(@blobs_dir, blob_path)
|
124
124
|
|
125
125
|
if File.directory?(blob_dst)
|
126
|
-
err("
|
126
|
+
err("'#{blob_dst}' is a directory, please pick a different path")
|
127
127
|
end
|
128
128
|
|
129
129
|
update = false
|
130
130
|
if File.exists?(blob_dst)
|
131
131
|
if file_checksum(blob_dst) == file_checksum(local_path)
|
132
|
-
err("Already tracking the same version of
|
132
|
+
err("Already tracking the same version of '#{blob_path}'")
|
133
133
|
end
|
134
134
|
update = true
|
135
135
|
FileUtils.rm(blob_dst)
|
@@ -145,7 +145,7 @@ module Bosh::Cli
|
|
145
145
|
end
|
146
146
|
|
147
147
|
say("When you are done testing the new blob, please run\n" +
|
148
|
-
"
|
148
|
+
"'#{"bosh upload blobs".make_green}' and commit changes.")
|
149
149
|
end
|
150
150
|
|
151
151
|
# Synchronizes the contents of blobs directory with blobs index.
|
@@ -173,7 +173,7 @@ module Bosh::Cli
|
|
173
173
|
path = strip_blobs_dir(file)
|
174
174
|
|
175
175
|
if File.exists?(File.join(@src_dir, path))
|
176
|
-
err("File
|
176
|
+
err("File '#{path}' is in both 'blobs' and 'src' directory.\n" +
|
177
177
|
"Please fix release repo before proceeding")
|
178
178
|
end
|
179
179
|
|
@@ -213,7 +213,7 @@ module Bosh::Cli
|
|
213
213
|
missing_blobs = []
|
214
214
|
@index.each_pair do |path, entry|
|
215
215
|
if File.exists?(File.join(@src_dir, path))
|
216
|
-
err("File
|
216
|
+
err("File '#{path}' is in both blob index and src directory.\n" +
|
217
217
|
"Please fix release repo before proceeding")
|
218
218
|
end
|
219
219
|
|
@@ -256,11 +256,11 @@ module Bosh::Cli
|
|
256
256
|
blob_path = File.join(@blobs_dir, path)
|
257
257
|
|
258
258
|
unless File.exists?(blob_path)
|
259
|
-
err("Cannot upload blob, local file
|
259
|
+
err("Cannot upload blob, local file '#{blob_path}' doesn't exist")
|
260
260
|
end
|
261
261
|
|
262
262
|
if File.symlink?(blob_path)
|
263
|
-
err("
|
263
|
+
err("'#{blob_path}' is a symlink")
|
264
264
|
end
|
265
265
|
|
266
266
|
checksum = file_checksum(blob_path)
|
@@ -288,7 +288,7 @@ module Bosh::Cli
|
|
288
288
|
end
|
289
289
|
|
290
290
|
unless @index.has_key?(path)
|
291
|
-
err("Unknown blob path
|
291
|
+
err("Unknown blob path '#{path}'")
|
292
292
|
end
|
293
293
|
|
294
294
|
blob = @index[path]
|
@@ -355,7 +355,7 @@ module Bosh::Cli
|
|
355
355
|
if blob_path[0..blobs_dir.size] == blobs_dir + "/"
|
356
356
|
blob_path[blobs_dir.size+1..-1]
|
357
357
|
else
|
358
|
-
err("File
|
358
|
+
err("File '#{blob_path}' is not under 'blobs' directory")
|
359
359
|
end
|
360
360
|
end
|
361
361
|
|
data/lib/cli/client/director.rb
CHANGED
@@ -124,6 +124,18 @@ module Bosh
|
|
124
124
|
get_json('/deployments')
|
125
125
|
end
|
126
126
|
|
127
|
+
def list_events(options={})
|
128
|
+
query_string = "/events"
|
129
|
+
delimeter = "?"
|
130
|
+
[:before_id, :deployment, :instance, :task].each do |param|
|
131
|
+
if options[param]
|
132
|
+
query_string += "#{delimeter}#{ param.to_s}=#{options[param]}"
|
133
|
+
delimeter = "&"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
get_json(query_string)
|
137
|
+
end
|
138
|
+
|
127
139
|
def list_errands(deployment_name)
|
128
140
|
get_json("/deployments/#{deployment_name}/errands")
|
129
141
|
end
|
@@ -275,15 +287,14 @@ module Bosh
|
|
275
287
|
request_and_track(:post, add_query_string(url, extras), options)
|
276
288
|
end
|
277
289
|
|
278
|
-
def diff_deployment(name, manifest_yaml,
|
279
|
-
redact_param =
|
290
|
+
def diff_deployment(name, manifest_yaml, redact_diff = true)
|
291
|
+
redact_param = redact_diff ? '' : '?redact=false'
|
280
292
|
uri = "/deployments/#{name}/diff#{redact_param}"
|
281
293
|
status, body = post(uri, 'text/yaml', manifest_yaml)
|
282
|
-
|
283
294
|
if status == 200
|
284
295
|
JSON.parse(body)
|
285
296
|
else
|
286
|
-
err(parse_error_message(status, body
|
297
|
+
err(parse_error_message(status, body))
|
287
298
|
end
|
288
299
|
end
|
289
300
|
|
@@ -411,7 +422,7 @@ module Bosh
|
|
411
422
|
tmp_file
|
412
423
|
else
|
413
424
|
raise DirectorError,
|
414
|
-
"Cannot download resource
|
425
|
+
"Cannot download resource '#{id}': HTTP status #{status}"
|
415
426
|
end
|
416
427
|
end
|
417
428
|
|
data/lib/cli/commands/backup.rb
CHANGED
@@ -15,7 +15,7 @@ module Bosh::Cli::Command
|
|
15
15
|
if status == :done
|
16
16
|
tmp_path = director.fetch_backup
|
17
17
|
FileUtils.mv(tmp_path, path)
|
18
|
-
say("Backup of BOSH director was put in
|
18
|
+
say("Backup of BOSH director was put in '#{path.make_green}'.")
|
19
19
|
else
|
20
20
|
[status, task_id]
|
21
21
|
end
|
@@ -31,7 +31,7 @@ module Bosh::Cli::Command
|
|
31
31
|
path = Bosh::Cli::BackupDestinationPath.new(director).create_from_path(dest_path)
|
32
32
|
|
33
33
|
if File.exists?(path) && !force?
|
34
|
-
err("There is already an existing file at
|
34
|
+
err("There is already an existing file at '#{path}'. " +
|
35
35
|
'To overwrite it use the --force option.')
|
36
36
|
end
|
37
37
|
|
@@ -21,8 +21,8 @@ module Bosh::Cli::Command
|
|
21
21
|
|
22
22
|
if non_interactive? && !(@report_mode || @auto_mode)
|
23
23
|
err ("Cloudcheck cannot be run in non-interactive mode\n" +
|
24
|
-
"Please use
|
25
|
-
"or
|
24
|
+
"Please use '--auto' flag if you want automated resolutions " +
|
25
|
+
"or '--report' if you just want a report of the errors")
|
26
26
|
end
|
27
27
|
|
28
28
|
if @auto_mode && @report_mode
|
@@ -13,7 +13,7 @@ module Bosh::Cli::Command
|
|
13
13
|
manifest_filename = find_deployment(filename)
|
14
14
|
|
15
15
|
unless File.exists?(manifest_filename)
|
16
|
-
err("Missing manifest for
|
16
|
+
err("Missing manifest for '#{filename}'")
|
17
17
|
end
|
18
18
|
|
19
19
|
manifest = load_yaml_file(manifest_filename)
|
@@ -60,10 +60,10 @@ module Bosh::Cli::Command
|
|
60
60
|
config.target_version = status['version']
|
61
61
|
config.target_uuid = status['uuid']
|
62
62
|
say("#{'WARNING!'.make_red} Your target has been " +
|
63
|
-
"changed to
|
63
|
+
"changed to '#{target.make_red}'!")
|
64
64
|
end
|
65
65
|
|
66
|
-
say("Deployment set to
|
66
|
+
say("Deployment set to '#{manifest_filename.make_green}'")
|
67
67
|
config.set_deployment(manifest_filename)
|
68
68
|
config.save
|
69
69
|
end
|
@@ -81,21 +81,21 @@ module Bosh::Cli::Command
|
|
81
81
|
usage 'deploy'
|
82
82
|
desc 'Deploy according to the currently selected deployment manifest'
|
83
83
|
option '--recreate', 'Recreate all VMs in deployment'
|
84
|
-
option '--redact
|
85
|
-
option '--no-redact', 'do not redact'
|
84
|
+
option '--no-redact', 'Redact manifest value changes in deployment'
|
86
85
|
option '--skip-drain [job1,job2]', String, 'Skip drain script for either specific or all jobs'
|
87
86
|
def perform
|
88
87
|
auth_required
|
88
|
+
|
89
89
|
recreate = !!options[:recreate]
|
90
|
-
redact_diff = !!options[:
|
91
|
-
|
90
|
+
redact_diff = !!options[:no_redact].nil?
|
91
|
+
|
92
92
|
manifest = build_manifest
|
93
93
|
|
94
94
|
if manifest.hash['releases']
|
95
95
|
manifest.hash['releases'].each do |release|
|
96
96
|
if release['url'].blank?
|
97
97
|
if release['version'] == 'create'
|
98
|
-
err("Expected URL when specifying release version
|
98
|
+
err("Expected URL when specifying release version 'create'")
|
99
99
|
end
|
100
100
|
else
|
101
101
|
parsed_uri = URI.parse(release['url'])
|
@@ -110,11 +110,11 @@ module Bosh::Cli::Command
|
|
110
110
|
run_nested_command "upload", "release", parsed_uri.path, '--name', release['name'], '--version', release['version'].to_s
|
111
111
|
end
|
112
112
|
when 'http', 'https'
|
113
|
-
err(
|
114
|
-
err("Expected SHA1 when specifying remote URL for release
|
113
|
+
err("Path must be a local release directory when version is 'create'") if release['version'] == 'create'
|
114
|
+
err("Expected SHA1 when specifying remote URL for release '#{release["name"]}'") if release['sha1'].blank?
|
115
115
|
run_nested_command "upload", "release", release['url'], "--sha1", release['sha1'], "--name", release['name'], "--version", release['version'].to_s
|
116
116
|
else
|
117
|
-
err("Invalid URL format for release
|
117
|
+
err("Invalid URL format for release '#{release['name']}' with URL '#{release['url']}'. Supported schemes: file, http, https.")
|
118
118
|
end
|
119
119
|
end
|
120
120
|
end
|
@@ -128,11 +128,11 @@ module Bosh::Cli::Command
|
|
128
128
|
when 'file'
|
129
129
|
run_nested_command "upload", "stemcell", parsed_uri.path, "--name", resource_pool['stemcell']['name'], "--version", resource_pool['stemcell']['version'].to_s, "--skip-if-exists"
|
130
130
|
when 'http', 'https'
|
131
|
-
err("Expected SHA1 when specifying remote URL for stemcell
|
131
|
+
err("Expected SHA1 when specifying remote URL for stemcell '#{resource_pool['stemcell']['name']}'") if resource_pool['stemcell']['sha1'].blank?
|
132
132
|
run_nested_command "upload", "stemcell", resource_pool['stemcell']['url'], "--sha1", resource_pool['stemcell']['sha1'],
|
133
133
|
"--name", resource_pool['stemcell']['name'], "--version", resource_pool['stemcell']['version'].to_s, "--skip-if-exists"
|
134
134
|
else
|
135
|
-
err("Invalid URL format for stemcell
|
135
|
+
err("Invalid URL format for stemcell '#{resource_pool['stemcell']['name']}' with URL '#{resource_pool['stemcell']['url']}'. Supported schemes: file, http, https.")
|
136
136
|
end
|
137
137
|
end
|
138
138
|
end
|
@@ -140,7 +140,7 @@ module Bosh::Cli::Command
|
|
140
140
|
|
141
141
|
manifest = prepare_deployment_manifest(resolve_properties: true, show_state: true)
|
142
142
|
|
143
|
-
context = DeploymentDiff.new(director, manifest).print({redact_diff: redact_diff
|
143
|
+
context = DeploymentDiff.new(director, manifest).print({redact_diff: redact_diff})
|
144
144
|
say('Please review all changes carefully'.make_yellow) if interactive?
|
145
145
|
|
146
146
|
header('Deploying')
|
@@ -160,7 +160,7 @@ module Bosh::Cli::Command
|
|
160
160
|
|
161
161
|
status, task_id = director.deploy(manifest.yaml, deploy_options)
|
162
162
|
|
163
|
-
task_report(status, task_id, "Deployed
|
163
|
+
task_report(status, task_id, "Deployed '#{manifest.name.make_green}' to '#{target_name.make_green}'")
|
164
164
|
end
|
165
165
|
|
166
166
|
# bosh delete deployment
|
@@ -173,7 +173,7 @@ module Bosh::Cli::Command
|
|
173
173
|
|
174
174
|
force = !!options[:force]
|
175
175
|
|
176
|
-
say("\nYou are going to delete deployment
|
176
|
+
say("\nYou are going to delete deployment '#{deployment_name}'.".make_red)
|
177
177
|
nl
|
178
178
|
say("THIS IS A VERY DESTRUCTIVE OPERATION AND IT CANNOT BE UNDONE!\n".make_red)
|
179
179
|
|
@@ -184,9 +184,9 @@ module Bosh::Cli::Command
|
|
184
184
|
|
185
185
|
begin
|
186
186
|
status, result = director.delete_deployment(deployment_name, :force => force)
|
187
|
-
task_report(status, result, "Deleted deployment
|
187
|
+
task_report(status, result, "Deleted deployment '#{deployment_name}'")
|
188
188
|
rescue Bosh::Cli::ResourceNotFound
|
189
|
-
task_report(:done, nil, "Skipped delete of missing deployment
|
189
|
+
task_report(:done, nil, "Skipped delete of missing deployment '#{deployment_name}'")
|
190
190
|
end
|
191
191
|
end
|
192
192
|
|
@@ -223,7 +223,7 @@ module Bosh::Cli::Command
|
|
223
223
|
show_current_state(deployment_name)
|
224
224
|
|
225
225
|
if save_as && File.exists?(save_as) &&
|
226
|
-
!confirmed?("Overwrite
|
226
|
+
!confirmed?("Overwrite '#{save_as}'?")
|
227
227
|
err('Please choose another file to save the manifest to')
|
228
228
|
end
|
229
229
|
|
@@ -233,7 +233,7 @@ module Bosh::Cli::Command
|
|
233
233
|
File.open(save_as, 'w') do |f|
|
234
234
|
f.write(deployment['manifest'])
|
235
235
|
end
|
236
|
-
say("Deployment manifest saved to
|
236
|
+
say("Deployment manifest saved to '#{save_as}'".make_green)
|
237
237
|
else
|
238
238
|
say(deployment['manifest'])
|
239
239
|
end
|
@@ -244,7 +244,7 @@ module Bosh::Cli::Command
|
|
244
244
|
config.target = target
|
245
245
|
if config.deployment
|
246
246
|
if interactive?
|
247
|
-
say("Current deployment is
|
247
|
+
say("Current deployment is '#{config.deployment.make_green}'")
|
248
248
|
else
|
249
249
|
say(config.deployment)
|
250
250
|
end
|
@@ -269,7 +269,7 @@ module Bosh::Cli::Command
|
|
269
269
|
def path_is_reasonable!(path)
|
270
270
|
#path is actually to a directory, not a file
|
271
271
|
unless File.directory?(path)
|
272
|
-
err "Path must be a release directory when version is
|
272
|
+
err "Path must be a release directory when version is 'create'"
|
273
273
|
end
|
274
274
|
end
|
275
275
|
|
@@ -6,10 +6,12 @@ module Bosh::Cli::Command
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def print(options)
|
9
|
+
redact_diff = options[:redact_diff]
|
10
|
+
|
9
11
|
begin
|
10
|
-
|
11
|
-
changes = @director.diff_deployment(@manifest.name, @manifest.yaml, no_redact)
|
12
|
+
changes = @director.diff_deployment(@manifest.name, @manifest.yaml, redact_diff)
|
12
13
|
diff = changes['diff']
|
14
|
+
error = changes['error']
|
13
15
|
|
14
16
|
header('Detecting deployment changes')
|
15
17
|
|
@@ -39,11 +41,13 @@ module Bosh::Cli::Command
|
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
44
|
+
say(error) if error
|
45
|
+
|
42
46
|
changes['context']
|
43
47
|
rescue Bosh::Cli::ResourceNotFound
|
44
48
|
inspect_deployment_changes(
|
45
49
|
@manifest,
|
46
|
-
redact_diff:
|
50
|
+
redact_diff: redact_diff
|
47
51
|
)
|
48
52
|
|
49
53
|
nil
|
data/lib/cli/commands/disks.rb
CHANGED
data/lib/cli/commands/errand.rb
CHANGED
@@ -44,7 +44,7 @@ module Bosh::Cli::Command
|
|
44
44
|
status, task_id, errand_result = errands_client.run_errand(deployment_name, errand_name, options[:keep_alive] || FALSE)
|
45
45
|
|
46
46
|
unless errand_result
|
47
|
-
task_report(status, task_id, nil, "Errand
|
47
|
+
task_report(status, task_id, nil, "Errand '#{errand_name}' did not complete")
|
48
48
|
return
|
49
49
|
end
|
50
50
|
|
@@ -69,7 +69,7 @@ module Bosh::Cli::Command
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
title_prefix = "Errand
|
72
|
+
title_prefix = "Errand '#{errand_name}'"
|
73
73
|
exit_code_suffix = "(exit code #{errand_result.exit_code})"
|
74
74
|
|
75
75
|
if errand_result.exit_code == 0
|