bosh_cli 1.2831.0 → 1.2839.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 +7 -3
- data/lib/cli/archive_builder.rb +119 -0
- data/lib/cli/archive_repository.rb +108 -0
- data/lib/cli/archive_repository_provider.rb +12 -0
- data/lib/cli/base_command.rb +1 -1
- data/lib/cli/build_artifact.rb +75 -0
- data/lib/cli/commands/deployment.rb +4 -9
- data/lib/cli/commands/release/create_release.rb +36 -39
- data/lib/cli/glob_match.rb +32 -0
- data/lib/cli/job_property_collection.rb +5 -5
- data/lib/cli/job_property_validator.rb +8 -8
- data/lib/cli/release_builder.rb +43 -44
- data/lib/cli/release_compiler.rb +11 -11
- data/lib/cli/resources/job.rb +190 -0
- data/lib/cli/resources/package.rb +210 -0
- data/lib/cli/version.rb +1 -1
- data/lib/cli/versions/local_version_storage.rb +4 -4
- data/lib/cli/versions/version_file_resolver.rb +1 -3
- metadata +16 -12
- data/lib/cli/job_builder.rb +0 -277
- data/lib/cli/package_builder.rb +0 -316
- data/lib/cli/packaging_helper.rb +0 -217
@@ -0,0 +1,32 @@
|
|
1
|
+
module Bosh::Cli
|
2
|
+
class GlobMatch
|
3
|
+
# Helper class encapsulating the data we know about the glob. We need
|
4
|
+
# both directory and file path, as we match the same path in several
|
5
|
+
# directories (src, src_alt, blobs)
|
6
|
+
attr_reader :dir
|
7
|
+
attr_reader :path
|
8
|
+
|
9
|
+
def initialize(dir, path)
|
10
|
+
@dir = dir
|
11
|
+
@path = path
|
12
|
+
end
|
13
|
+
|
14
|
+
def full_path
|
15
|
+
File.join(dir, path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def <=>(other)
|
19
|
+
@path <=> other.path
|
20
|
+
end
|
21
|
+
|
22
|
+
# GlobMatch will be used as Hash key (as implied by using Set),
|
23
|
+
# hence we need to define both eql? and hash
|
24
|
+
def eql?(other)
|
25
|
+
@path == other.path
|
26
|
+
end
|
27
|
+
|
28
|
+
def hash
|
29
|
+
@path.hash
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -8,12 +8,12 @@ module Bosh::Cli
|
|
8
8
|
include Enumerable
|
9
9
|
include Bosh::Template::PropertyHelper
|
10
10
|
|
11
|
-
# @param [
|
11
|
+
# @param [Bosh::Cli::Resources::Job] job Which job this property collection is for
|
12
12
|
# @param [Hash] global_properties Globally defined properties
|
13
13
|
# @param [Hash] job_properties Properties defined for this job only
|
14
14
|
# @param [Hash] mappings Property mappings for this job
|
15
|
-
def initialize(
|
16
|
-
@
|
15
|
+
def initialize(job, global_properties, job_properties, mappings)
|
16
|
+
@job = job
|
17
17
|
|
18
18
|
@job_properties = Bosh::Common::DeepCopy.copy(job_properties || {})
|
19
19
|
merge(@job_properties, Bosh::Common::DeepCopy.copy(global_properties))
|
@@ -52,7 +52,7 @@ module Bosh::Cli
|
|
52
52
|
|
53
53
|
# @return [void] Modifies @properties
|
54
54
|
def filter_properties
|
55
|
-
if @
|
55
|
+
if @job.properties.empty?
|
56
56
|
# If at least one template doesn't have properties defined, we
|
57
57
|
# need all properties to be available to job (backward-compatibility)
|
58
58
|
@properties = @job_properties
|
@@ -61,7 +61,7 @@ module Bosh::Cli
|
|
61
61
|
|
62
62
|
@properties = {}
|
63
63
|
|
64
|
-
@
|
64
|
+
@job.properties.each_pair do |name, definition|
|
65
65
|
copy_property(
|
66
66
|
@properties, @job_properties, name, definition["default"])
|
67
67
|
end
|
@@ -7,7 +7,7 @@ module Bosh::Cli
|
|
7
7
|
attr_reader :template_errors
|
8
8
|
attr_reader :jobs_without_properties
|
9
9
|
|
10
|
-
# @param [Array<
|
10
|
+
# @param [Array<Bosh::Cli::Resources::Job>] built_jobs Built job templates
|
11
11
|
# @param [Hash] manifest Deployment manifest
|
12
12
|
def initialize(built_jobs, manifest)
|
13
13
|
@built_jobs = {}
|
@@ -44,7 +44,7 @@ module Bosh::Cli
|
|
44
44
|
end
|
45
45
|
|
46
46
|
if job["template"].nil?
|
47
|
-
bad_manifest("Job
|
47
|
+
bad_manifest("Job '#{job_name}' doesn't have a template")
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
@@ -68,7 +68,7 @@ module Bosh::Cli
|
|
68
68
|
built_job = @built_jobs[job_spec["template"]]
|
69
69
|
|
70
70
|
if built_job.nil?
|
71
|
-
raise CliError, "Job
|
71
|
+
raise CliError, "Job '#{job_spec["template"]}' has not been built"
|
72
72
|
end
|
73
73
|
|
74
74
|
collection = JobPropertyCollection.new(
|
@@ -86,8 +86,8 @@ module Bosh::Cli
|
|
86
86
|
'properties' => collection.to_hash
|
87
87
|
}
|
88
88
|
|
89
|
-
built_job.
|
90
|
-
evaluate_template(built_job,
|
89
|
+
built_job.files.each do |file_tuple|
|
90
|
+
evaluate_template(built_job, file_tuple.first, spec)
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
@@ -104,7 +104,7 @@ module Bosh::Cli
|
|
104
104
|
|
105
105
|
private
|
106
106
|
|
107
|
-
# @param [
|
107
|
+
# @param [Bosh::Cli::Resources::Job] job Job builder
|
108
108
|
# @param [String] template_path Template path
|
109
109
|
# @param [Hash] spec Fake instance spec
|
110
110
|
def evaluate_template(job, template_path, spec)
|
@@ -127,7 +127,7 @@ module Bosh::Cli
|
|
127
127
|
attr_reader :exception
|
128
128
|
attr_reader :line
|
129
129
|
|
130
|
-
# @param [
|
130
|
+
# @param [Bosh::Cli::Resources::Job] job
|
131
131
|
# @param [String] template_path
|
132
132
|
# @param [Exception] exception
|
133
133
|
def initialize(job, template_path, exception)
|
@@ -138,4 +138,4 @@ module Bosh::Cli
|
|
138
138
|
end
|
139
139
|
end
|
140
140
|
end
|
141
|
-
end
|
141
|
+
end
|
data/lib/cli/release_builder.rb
CHANGED
@@ -5,16 +5,16 @@ module Bosh::Cli
|
|
5
5
|
attr_reader :release, :packages, :jobs, :name, :version, :build_dir, :commit_hash, :uncommitted_changes
|
6
6
|
|
7
7
|
# @param [Bosh::Cli::Release] release Current release
|
8
|
-
# @param [Array<Bosh::Cli::
|
9
|
-
# @param [Array<Bosh::Cli::
|
8
|
+
# @param [Array<Bosh::Cli::BuildArtifact>] package_artifacts Built packages
|
9
|
+
# @param [Array<Bosh::Cli::BuildArtifact>] job_artifacts Built jobs
|
10
10
|
# @param [Hash] options Release build options
|
11
|
-
def initialize(release,
|
11
|
+
def initialize(release, package_artifacts, job_artifacts, name, options = { })
|
12
12
|
@release = release
|
13
13
|
@final = options.has_key?(:final) ? !!options[:final] : false
|
14
14
|
@commit_hash = options.fetch(:commit_hash, '00000000')
|
15
15
|
@uncommitted_changes = options.fetch(:uncommitted_changes, true)
|
16
|
-
@packages =
|
17
|
-
@jobs =
|
16
|
+
@packages = package_artifacts # todo
|
17
|
+
@jobs = job_artifacts # todo
|
18
18
|
@name = name
|
19
19
|
raise 'Release name is blank' if name.blank?
|
20
20
|
|
@@ -49,19 +49,18 @@ module Bosh::Cli
|
|
49
49
|
@final
|
50
50
|
end
|
51
51
|
|
52
|
-
# @return [Array] List of
|
53
|
-
# to the previous one.
|
52
|
+
# @return [Array<Bosh::Cli::BuildArtifact>] List of job artifacts
|
53
|
+
# affected by this release compared to the previous one.
|
54
54
|
def affected_jobs
|
55
|
-
result = Set.new(@jobs.select { |
|
56
|
-
return result if @packages.empty?
|
55
|
+
result = Set.new(@jobs.select { |job_artifact| job_artifact.new_version? })
|
56
|
+
return result.to_a if @packages.empty?
|
57
57
|
|
58
|
-
new_package_names = @packages.
|
59
|
-
|
60
|
-
|
61
|
-
end
|
58
|
+
new_package_names = @packages.map do |package_artifact|
|
59
|
+
package_artifact.name if package_artifact.new_version?
|
60
|
+
end.compact
|
62
61
|
|
63
62
|
@jobs.each do |job|
|
64
|
-
result << job if (new_package_names & job.
|
63
|
+
result << job if (new_package_names & job.dependencies).size > 0
|
65
64
|
end
|
66
65
|
|
67
66
|
result.to_a
|
@@ -83,22 +82,25 @@ module Bosh::Cli
|
|
83
82
|
|
84
83
|
# Copies packages into release
|
85
84
|
def copy_packages
|
86
|
-
packages.each do |
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
85
|
+
packages.each do |package_artifact|
|
86
|
+
name = package_artifact.name
|
87
|
+
tarball_path = package_artifact.tarball_path
|
88
|
+
say("%-40s %s" % [name.make_green, pretty_size(tarball_path)])
|
89
|
+
FileUtils.cp(tarball_path,
|
90
|
+
File.join(build_dir, "packages", "#{name}.tgz"),
|
91
91
|
:preserve => true)
|
92
92
|
end
|
93
93
|
@packages_copied = true
|
94
94
|
end
|
95
95
|
|
96
|
-
# Copies jobs into release
|
96
|
+
# Copies jobs into release todo DRY vs copy_packages
|
97
97
|
def copy_jobs
|
98
|
-
jobs.each do |
|
99
|
-
|
100
|
-
|
101
|
-
|
98
|
+
jobs.each do |job_artifact|
|
99
|
+
name = job_artifact.name
|
100
|
+
tarball_path = job_artifact.tarball_path
|
101
|
+
say("%-40s %s" % [name.make_green, pretty_size(tarball_path)])
|
102
|
+
FileUtils.cp(tarball_path,
|
103
|
+
File.join(build_dir, "jobs", "#{name}.tgz"),
|
102
104
|
:preserve => true)
|
103
105
|
end
|
104
106
|
@jobs_copied = true
|
@@ -107,40 +109,37 @@ module Bosh::Cli
|
|
107
109
|
# Generates release manifest
|
108
110
|
def generate_manifest
|
109
111
|
manifest = {}
|
110
|
-
manifest[
|
111
|
-
|
112
|
-
manifest["packages"] = packages.map do |package|
|
112
|
+
manifest['packages'] = packages.map do |build_artifact|
|
113
113
|
{
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
114
|
+
'name' => build_artifact.name,
|
115
|
+
'version' => build_artifact.version,
|
116
|
+
'fingerprint' => build_artifact.fingerprint,
|
117
|
+
'sha1' => build_artifact.sha1,
|
118
|
+
'dependencies' => build_artifact.dependencies,
|
119
119
|
}
|
120
120
|
end
|
121
|
-
|
122
|
-
manifest["jobs"] = jobs.map do |job|
|
121
|
+
manifest['jobs'] = jobs.map do |build_artifact|
|
123
122
|
{
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
123
|
+
'name' => build_artifact.name,
|
124
|
+
'version' => build_artifact.version,
|
125
|
+
'fingerprint' => build_artifact.fingerprint,
|
126
|
+
'sha1' => build_artifact.sha1,
|
128
127
|
}
|
129
128
|
end
|
130
129
|
|
131
|
-
manifest[
|
132
|
-
manifest[
|
130
|
+
manifest['commit_hash'] = commit_hash
|
131
|
+
manifest['uncommitted_changes'] = uncommitted_changes
|
133
132
|
|
134
133
|
unless @name.bosh_valid_id?
|
135
|
-
raise InvalidRelease, "Release name
|
134
|
+
raise InvalidRelease, "Release name '#{@name}' is not a valid BOSH identifier"
|
136
135
|
end
|
137
|
-
manifest[
|
136
|
+
manifest['name'] = @name
|
138
137
|
|
139
138
|
# New release versions are allowed to have the same fingerprint as old versions.
|
140
139
|
# For reverse compatibility, random uuids are stored instead.
|
141
|
-
@index.add_version(SecureRandom.uuid, {
|
140
|
+
@index.add_version(SecureRandom.uuid, { 'version' => version })
|
142
141
|
|
143
|
-
manifest[
|
142
|
+
manifest['version'] = version
|
144
143
|
manifest_yaml = Psych.dump(manifest)
|
145
144
|
|
146
145
|
say("Writing manifest...")
|
data/lib/cli/release_compiler.rb
CHANGED
@@ -14,13 +14,13 @@ module Bosh::Cli
|
|
14
14
|
# @param [Bosh::Blobstore::Client] blobstore Blobstore client
|
15
15
|
# @param [Array] package_matches List of package checksums that director
|
16
16
|
# can match
|
17
|
-
# @param [String]
|
17
|
+
# @param [String] release_source Release directory
|
18
18
|
def initialize(manifest_file, blobstore,
|
19
|
-
package_matches = [],
|
19
|
+
package_matches = [], release_source = nil)
|
20
20
|
|
21
21
|
@blobstore = blobstore
|
22
|
-
@
|
23
|
-
@manifest_file = File.expand_path(manifest_file, @
|
22
|
+
@release_source = release_source || Dir.pwd
|
23
|
+
@manifest_file = File.expand_path(manifest_file, @release_source)
|
24
24
|
@tarball_path = nil
|
25
25
|
|
26
26
|
@build_dir = Dir.mktmpdir
|
@@ -95,18 +95,18 @@ module Bosh::Cli
|
|
95
95
|
|
96
96
|
def find_package(package)
|
97
97
|
name = package.name
|
98
|
-
final_package_dir = File.join(@
|
98
|
+
final_package_dir = File.join(@release_source, '.final_builds', 'packages', name)
|
99
99
|
final_index = Versions::VersionsIndex.new(final_package_dir)
|
100
|
-
dev_package_dir = File.join(@
|
100
|
+
dev_package_dir = File.join(@release_source, '.dev_builds', 'packages', name)
|
101
101
|
dev_index = Versions::VersionsIndex.new(dev_package_dir)
|
102
102
|
find_in_indices(final_index, dev_index, package, 'package')
|
103
103
|
end
|
104
104
|
|
105
105
|
def find_job(job)
|
106
106
|
name = job.name
|
107
|
-
final_jobs_dir = File.join(@
|
107
|
+
final_jobs_dir = File.join(@release_source, '.final_builds', 'jobs', name)
|
108
108
|
final_index = Versions::VersionsIndex.new(final_jobs_dir)
|
109
|
-
dev_jobs_dir = File.join(@
|
109
|
+
dev_jobs_dir = File.join(@release_source, '.dev_builds', 'jobs', name)
|
110
110
|
dev_index = Versions::VersionsIndex.new(dev_jobs_dir)
|
111
111
|
find_in_indices(final_index, dev_index, job, 'job')
|
112
112
|
end
|
@@ -148,9 +148,9 @@ module Bosh::Cli
|
|
148
148
|
# @return [Boolean]
|
149
149
|
def remote_package_exists?(local_package)
|
150
150
|
# If checksum is known to director we can always match it
|
151
|
-
@package_matches.include?(local_package.sha1) ||
|
152
|
-
(local_package.fingerprint &&
|
153
|
-
@package_matches.include?(local_package.fingerprint))
|
151
|
+
@package_matches.include?(local_package.sha1) || # !!! Needs test coverage
|
152
|
+
(local_package.fingerprint && # !!! Needs test coverage
|
153
|
+
@package_matches.include?(local_package.fingerprint)) # !!! Needs test coverage
|
154
154
|
end
|
155
155
|
|
156
156
|
# Checks if local job is already known remotely
|
@@ -0,0 +1,190 @@
|
|
1
|
+
module Bosh::Cli::Resources
|
2
|
+
class Job
|
3
|
+
BUILD_HOOK_FILES = ['prepare']
|
4
|
+
|
5
|
+
# @param [String] directory base Release directory
|
6
|
+
def self.discover(release_base, packages)
|
7
|
+
Dir[File.join(release_base, 'jobs', '*')].inject([]) do |jobs, job_base|
|
8
|
+
next unless File.directory?(job_base)
|
9
|
+
jobs << new(job_base, release_base, packages)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :job_base, :release_base, :package_dependencies
|
14
|
+
|
15
|
+
def initialize(job_base, release_base, packages)
|
16
|
+
@release_base = Pathname.new(release_base)
|
17
|
+
@job_base = Pathname.new(job_base)
|
18
|
+
@package_dependencies = packages
|
19
|
+
end
|
20
|
+
|
21
|
+
def spec
|
22
|
+
@spec ||= load_yaml_file(job_base.join('spec'))
|
23
|
+
rescue
|
24
|
+
raise Bosh::Cli::InvalidJob, 'Job spec is missing'
|
25
|
+
end
|
26
|
+
|
27
|
+
def name
|
28
|
+
spec['name']
|
29
|
+
end
|
30
|
+
|
31
|
+
def dependencies
|
32
|
+
package_dependencies #TODO: should this be packages or package_dependencies?
|
33
|
+
end
|
34
|
+
|
35
|
+
def singular_type
|
36
|
+
'job'
|
37
|
+
end
|
38
|
+
|
39
|
+
def plural_type
|
40
|
+
'jobs'
|
41
|
+
end
|
42
|
+
|
43
|
+
def files
|
44
|
+
validate!
|
45
|
+
|
46
|
+
files = (templates_files + monit_files).map { |absolute_path| [absolute_path, relative_path(absolute_path)] }
|
47
|
+
files << [File.join(job_base, 'spec'), 'job.MF']
|
48
|
+
files
|
49
|
+
end
|
50
|
+
|
51
|
+
# TODO: check dependency packages
|
52
|
+
def validate!
|
53
|
+
if name.blank?
|
54
|
+
raise Bosh::Cli::InvalidJob, 'Job name is missing'
|
55
|
+
end
|
56
|
+
|
57
|
+
unless name.bosh_valid_id?
|
58
|
+
raise Bosh::Cli::InvalidJob, "'#{name}' is not a valid BOSH identifier"
|
59
|
+
end
|
60
|
+
|
61
|
+
unless spec['templates'].is_a?(Hash)
|
62
|
+
raise Bosh::Cli::InvalidJob, "Incorrect templates section in '#{name}' job spec (Hash expected, #{spec['templates'].class} given)"
|
63
|
+
end
|
64
|
+
|
65
|
+
if extra_templates.size > 0
|
66
|
+
raise Bosh::Cli::InvalidJob, "There are unused template files for job '#{name}': #{extra_templates.join(", ")}"
|
67
|
+
end
|
68
|
+
|
69
|
+
if missing_templates.size > 0
|
70
|
+
raise Bosh::Cli::InvalidJob, "Some template files required by '#{name}' job are missing: #{missing_templates.join(", ")}"
|
71
|
+
end
|
72
|
+
|
73
|
+
if missing_packages.size > 0
|
74
|
+
raise Bosh::Cli::InvalidJob, "Some packages required by '#{name}' job are missing: #{missing_packages.join(", ")}"
|
75
|
+
end
|
76
|
+
|
77
|
+
if spec.has_key?('properties')
|
78
|
+
unless spec['properties'].is_a?(Hash)
|
79
|
+
raise Bosh::Cli::InvalidJob, "Incorrect properties section in '#{name}' job spec (Hash expected, #{spec['properties'].class} given)"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
unless monit_files.size > 0
|
84
|
+
raise Bosh::Cli::InvalidJob, "Cannot find monit file for '#{name}'"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def additional_fingerprints
|
89
|
+
[]
|
90
|
+
end
|
91
|
+
|
92
|
+
def format_fingerprint(digest, filename, name, file_mode)
|
93
|
+
"%s%s%s" % [File.basename(filename), digest, file_mode]
|
94
|
+
end
|
95
|
+
|
96
|
+
def run_script(script_name, *args)
|
97
|
+
if BUILD_HOOK_FILES.include?(script_name.to_s)
|
98
|
+
send(:"run_script_#{script_name}", *args)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def properties
|
103
|
+
spec['properties'] || {}
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def extra_templates
|
109
|
+
return [] if !File.directory?(templates_dir)
|
110
|
+
|
111
|
+
Dir.chdir(templates_dir) do
|
112
|
+
Dir["**/*"].reject do |file|
|
113
|
+
File.directory?(file) || templates.include?(file)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def missing_packages
|
119
|
+
@missing_packages ||= (packages - package_dependencies)
|
120
|
+
end
|
121
|
+
|
122
|
+
def missing_templates
|
123
|
+
templates.select do |template|
|
124
|
+
!File.exists?(File.join(templates_dir, template))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def monit_files
|
129
|
+
monit = File.join(job_base, 'monit')
|
130
|
+
files = Dir.glob(File.join(job_base, '*.monit'))
|
131
|
+
files << monit if File.exist?(monit)
|
132
|
+
files
|
133
|
+
end
|
134
|
+
|
135
|
+
def packages
|
136
|
+
spec['packages'] || []
|
137
|
+
end
|
138
|
+
|
139
|
+
def relative_path(path)
|
140
|
+
Pathname.new(path).relative_path_from(job_base).to_s
|
141
|
+
end
|
142
|
+
|
143
|
+
def templates
|
144
|
+
spec['templates'].keys
|
145
|
+
end
|
146
|
+
|
147
|
+
def templates_dir
|
148
|
+
@templates_dir ||= job_base.join('templates')
|
149
|
+
end
|
150
|
+
|
151
|
+
def templates_files
|
152
|
+
templates.map { |file| File.join(templates_dir, file) }
|
153
|
+
end
|
154
|
+
|
155
|
+
def run_script_prepare
|
156
|
+
script_path = File.join(job_base, 'prepare')
|
157
|
+
|
158
|
+
return nil unless File.exists?(script_path)
|
159
|
+
|
160
|
+
unless File.executable?(script_path)
|
161
|
+
raise Bosh::Cli::InvalidJob, "Prepare script at '#{script_path}' is not executable"
|
162
|
+
end
|
163
|
+
|
164
|
+
old_env = ENV
|
165
|
+
script_dir = File.dirname(script_path)
|
166
|
+
script_name = File.basename(script_path)
|
167
|
+
|
168
|
+
begin
|
169
|
+
# We need to temporarily delete some rubygems related artefacts
|
170
|
+
# because preparation scripts shouldn't share any assumptions
|
171
|
+
# with CLI itself
|
172
|
+
%w{ BUNDLE_GEMFILE RUBYOPT }.each { |key| ENV.delete(key) }
|
173
|
+
|
174
|
+
output = nil
|
175
|
+
Dir.chdir(script_dir) do
|
176
|
+
cmd = "./#{script_name} 2>&1"
|
177
|
+
output = `#{cmd}`
|
178
|
+
end
|
179
|
+
|
180
|
+
unless $?.exitstatus == 0
|
181
|
+
raise Bosh::Cli::InvalidJob, "'#{script_path}' script failed: #{output}"
|
182
|
+
end
|
183
|
+
|
184
|
+
output
|
185
|
+
ensure
|
186
|
+
ENV.each_pair { |k, v| ENV[k] = old_env[k] }
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|