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
data/lib/cli/package_builder.rb
DELETED
@@ -1,316 +0,0 @@
|
|
1
|
-
module Bosh::Cli
|
2
|
-
class PackageBuilder
|
3
|
-
include PackagingHelper
|
4
|
-
|
5
|
-
class GlobMatch
|
6
|
-
# Helper class encapsulating the data we know about the glob. We need
|
7
|
-
# both directory and file path, as we match the same path in several
|
8
|
-
# directories (src, src_alt, blobs)
|
9
|
-
attr_reader :dir
|
10
|
-
attr_reader :path
|
11
|
-
|
12
|
-
def initialize(dir, path)
|
13
|
-
@dir = dir
|
14
|
-
@path = path
|
15
|
-
end
|
16
|
-
|
17
|
-
def full_path
|
18
|
-
File.join(dir, path)
|
19
|
-
end
|
20
|
-
|
21
|
-
def <=>(other)
|
22
|
-
@path <=> other.path
|
23
|
-
end
|
24
|
-
|
25
|
-
# GlobMatch will be used as Hash key (as implied by using Set),
|
26
|
-
# hence we need to define both eql? and hash
|
27
|
-
def eql?(other)
|
28
|
-
@path == other.path
|
29
|
-
end
|
30
|
-
|
31
|
-
def hash
|
32
|
-
@path.hash
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
attr_reader :name, :globs, :version, :dependencies, :tarball_path
|
37
|
-
# We have two ways of getting/storing a package:
|
38
|
-
# development versions of packages, kept in release directory
|
39
|
-
# final versions of packages, kept in blobstore
|
40
|
-
# development packages and their metadata should always be gitignored
|
41
|
-
# final build tarballs should be ignored as well
|
42
|
-
# final builds metadata should be checked in
|
43
|
-
|
44
|
-
# @param [String] directory Release directory
|
45
|
-
# @param [Hash] options Package build options
|
46
|
-
def self.discover(directory, options = {})
|
47
|
-
builders = []
|
48
|
-
|
49
|
-
Dir[File.join(directory, "packages", "*")].each do |package_dir|
|
50
|
-
next unless File.directory?(package_dir)
|
51
|
-
package_dirname = File.basename(package_dir)
|
52
|
-
package_spec = load_yaml_file(File.join(package_dir, "spec"))
|
53
|
-
|
54
|
-
if package_spec["name"] != package_dirname
|
55
|
-
raise InvalidPackage,
|
56
|
-
"Found `#{package_spec["name"]}' package in " +
|
57
|
-
"`#{package_dirname}' directory, please fix it"
|
58
|
-
end
|
59
|
-
|
60
|
-
is_final = options[:final]
|
61
|
-
blobstore = options[:blobstore]
|
62
|
-
dry_run = options[:dry_run]
|
63
|
-
|
64
|
-
builder = new(package_spec, directory, is_final, blobstore)
|
65
|
-
builder.dry_run = true if dry_run
|
66
|
-
|
67
|
-
builders << builder
|
68
|
-
end
|
69
|
-
|
70
|
-
builders
|
71
|
-
end
|
72
|
-
|
73
|
-
def initialize(spec, release_dir, final, blobstore,
|
74
|
-
sources_dir = nil, blobs_dir = nil, alt_src_dir = nil)
|
75
|
-
spec = load_yaml_file(spec) if spec.is_a?(String) && File.file?(spec)
|
76
|
-
|
77
|
-
@name = spec["name"]
|
78
|
-
@globs = spec["files"]
|
79
|
-
@excluded_globs = spec["excluded_files"] || []
|
80
|
-
@dependencies = Array(spec["dependencies"])
|
81
|
-
|
82
|
-
@release_dir = release_dir
|
83
|
-
@sources_dir = sources_dir || File.join(@release_dir, "src")
|
84
|
-
@alt_sources_dir = alt_src_dir || File.join(@release_dir, "src_alt")
|
85
|
-
@blobs_dir = blobs_dir || File.join(@release_dir, "blobs")
|
86
|
-
|
87
|
-
@final = final
|
88
|
-
@blobstore = blobstore
|
89
|
-
@artefact_type = "package"
|
90
|
-
|
91
|
-
@metadata_files = %w(packaging pre_packaging)
|
92
|
-
|
93
|
-
if @final && File.exists?(@alt_sources_dir)
|
94
|
-
err("Please remove `#{File.basename(@alt_sources_dir)}' first")
|
95
|
-
end
|
96
|
-
|
97
|
-
if @name.blank?
|
98
|
-
raise InvalidPackage, "Package name is missing"
|
99
|
-
end
|
100
|
-
|
101
|
-
unless @name.bosh_valid_id?
|
102
|
-
raise InvalidPackage, "Package name should be a valid BOSH identifier"
|
103
|
-
end
|
104
|
-
|
105
|
-
unless @globs.is_a?(Array) && @globs.size > 0
|
106
|
-
raise InvalidPackage, "Package '#{@name}' doesn't include any files"
|
107
|
-
end
|
108
|
-
|
109
|
-
@dev_builds_dir = File.join(@release_dir, ".dev_builds",
|
110
|
-
"packages", @name)
|
111
|
-
@final_builds_dir = File.join(@release_dir, ".final_builds",
|
112
|
-
"packages", @name)
|
113
|
-
|
114
|
-
FileUtils.mkdir_p(package_dir)
|
115
|
-
FileUtils.mkdir_p(@dev_builds_dir)
|
116
|
-
FileUtils.mkdir_p(@final_builds_dir)
|
117
|
-
|
118
|
-
init_indices
|
119
|
-
end
|
120
|
-
|
121
|
-
def reload # Mostly for tests
|
122
|
-
@fingerprint = nil
|
123
|
-
@resolved_globs = nil
|
124
|
-
init_indices
|
125
|
-
self
|
126
|
-
end
|
127
|
-
|
128
|
-
def fingerprint
|
129
|
-
@fingerprint ||= make_fingerprint
|
130
|
-
end
|
131
|
-
|
132
|
-
def glob_matches
|
133
|
-
@resolved_globs ||= resolve_globs
|
134
|
-
end
|
135
|
-
|
136
|
-
def build_dir
|
137
|
-
@build_dir ||= Dir.mktmpdir
|
138
|
-
end
|
139
|
-
|
140
|
-
def package_dir
|
141
|
-
File.join(@release_dir, "packages", name)
|
142
|
-
end
|
143
|
-
|
144
|
-
def copy_files
|
145
|
-
copied = 0
|
146
|
-
|
147
|
-
glob_matches.each do |match|
|
148
|
-
destination = File.join(build_dir, match.path)
|
149
|
-
|
150
|
-
if File.directory?(match.full_path)
|
151
|
-
FileUtils.mkdir_p(destination)
|
152
|
-
else
|
153
|
-
FileUtils.mkdir_p(File.dirname(destination))
|
154
|
-
FileUtils.cp(match.full_path, destination, :preserve => true)
|
155
|
-
copied += 1
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
in_package_dir do
|
160
|
-
@metadata_files.each do |filename|
|
161
|
-
destination = File.join(build_dir, filename)
|
162
|
-
next unless File.exists?(filename)
|
163
|
-
if File.exists?(destination)
|
164
|
-
raise InvalidPackage, "Package '#{name}' has '#{filename}' file " +
|
165
|
-
"which conflicts with BOSH packaging"
|
166
|
-
end
|
167
|
-
FileUtils.cp(filename, destination, :preserve => true)
|
168
|
-
copied += 1
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
pre_package
|
173
|
-
copied
|
174
|
-
end
|
175
|
-
|
176
|
-
def pre_package
|
177
|
-
pre_packaging_script = File.join(package_dir, "pre_packaging")
|
178
|
-
|
179
|
-
if File.exists?(pre_packaging_script)
|
180
|
-
|
181
|
-
say("Pre-packaging...")
|
182
|
-
FileUtils.cp(pre_packaging_script, build_dir, :preserve => true)
|
183
|
-
|
184
|
-
old_env = ENV
|
185
|
-
|
186
|
-
begin
|
187
|
-
ENV.delete_if { |key, _| key[0, 7] == "BUNDLE_" }
|
188
|
-
if ENV["RUBYOPT"]
|
189
|
-
ENV["RUBYOPT"] = ENV["RUBYOPT"].sub("-rbundler/setup", "")
|
190
|
-
end
|
191
|
-
ENV["BUILD_DIR"] = build_dir
|
192
|
-
ENV["RELEASE_DIR"] = @release_dir
|
193
|
-
in_build_dir do
|
194
|
-
pre_packaging_out = `bash -x pre_packaging 2>&1`
|
195
|
-
unless $?.exitstatus == 0
|
196
|
-
pre_packaging_out.split("\n").each do |line|
|
197
|
-
say("> #{line}")
|
198
|
-
end
|
199
|
-
raise InvalidPackage, "`#{name}' pre-packaging failed"
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
ensure
|
204
|
-
ENV.delete("BUILD_DIR")
|
205
|
-
old_env.each { |k, v| ENV[k] = old_env[k] }
|
206
|
-
end
|
207
|
-
|
208
|
-
FileUtils.rm(File.join(build_dir, "pre_packaging"))
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
private
|
213
|
-
|
214
|
-
def make_fingerprint
|
215
|
-
versioning_scheme = 2
|
216
|
-
contents = "v#{versioning_scheme}"
|
217
|
-
|
218
|
-
signatures = glob_matches.map do |match|
|
219
|
-
file_digest = nil
|
220
|
-
|
221
|
-
unless File.directory?(match.full_path)
|
222
|
-
file_digest = Digest::SHA1.file(match.full_path).hexdigest
|
223
|
-
end
|
224
|
-
|
225
|
-
"%s%s%s" % [match.path, file_digest,
|
226
|
-
tracked_permissions(match.full_path)]
|
227
|
-
end
|
228
|
-
contents << signatures.join("")
|
229
|
-
|
230
|
-
in_package_dir do
|
231
|
-
@metadata_files.each do |file|
|
232
|
-
if File.file?(file)
|
233
|
-
file_digest = Digest::SHA1.file(file).hexdigest
|
234
|
-
contents << "%s%s" % [file, file_digest]
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
contents << @dependencies.sort.join(",")
|
240
|
-
|
241
|
-
Digest::SHA1.hexdigest(contents)
|
242
|
-
end
|
243
|
-
|
244
|
-
# @return Array<GlobMatch>
|
245
|
-
def resolve_globs
|
246
|
-
all_matches = Set.new
|
247
|
-
|
248
|
-
@globs.each do |glob|
|
249
|
-
matches = Set.new
|
250
|
-
|
251
|
-
src_matches = resolve_glob_in_dir(glob, @sources_dir)
|
252
|
-
src_alt_matches = []
|
253
|
-
if File.directory?(@alt_sources_dir)
|
254
|
-
src_alt_matches = resolve_glob_in_dir(glob, @alt_sources_dir)
|
255
|
-
end
|
256
|
-
|
257
|
-
# Glob like core/dea/**/* might not yield anything in alt source even
|
258
|
-
# when `src_alt/core' exists. That's error prone, so we don't lookup
|
259
|
-
# in `src' if `src_alt' contains any part of the glob hierarchy.
|
260
|
-
top_dir = glob.split(File::SEPARATOR)[0]
|
261
|
-
top_dir_in_src_alt_exists = top_dir && File.exists?(File.join(@alt_sources_dir, top_dir))
|
262
|
-
|
263
|
-
if top_dir_in_src_alt_exists && src_alt_matches.empty? && src_matches.any?
|
264
|
-
raise InvalidPackage, "Package `#{name}' has a glob that " +
|
265
|
-
"doesn't match in `#{File.basename(@alt_sources_dir)}' " +
|
266
|
-
"but matches in `#{File.basename(@sources_dir)}'. " +
|
267
|
-
"However `#{File.basename(@alt_sources_dir)}/#{top_dir}' " +
|
268
|
-
"exists, so this might be an error."
|
269
|
-
end
|
270
|
-
|
271
|
-
# First add src_alt matches since src_alt takes priority over src matches
|
272
|
-
matches += src_alt_matches.map { |path| GlobMatch.new(@alt_sources_dir, path) }
|
273
|
-
|
274
|
-
# Only add if top-level-dir does not exist in src_alt. No partial matches.
|
275
|
-
if !top_dir_in_src_alt_exists
|
276
|
-
matches += src_matches.map { |path| GlobMatch.new(@sources_dir, path) }
|
277
|
-
end
|
278
|
-
|
279
|
-
# Blobs directory is a little bit different: whatever matches a blob
|
280
|
-
# will complement already found matches, unless this particular path
|
281
|
-
# has already been matched.
|
282
|
-
if File.directory?(File.join(@blobs_dir))
|
283
|
-
resolve_glob_in_dir(glob, @blobs_dir).each { |path| matches << GlobMatch.new(@blobs_dir, path) }
|
284
|
-
end
|
285
|
-
|
286
|
-
if matches.empty?
|
287
|
-
raise InvalidPackage, "Package `#{name}' has a glob that resolves to an empty file list: #{glob}"
|
288
|
-
end
|
289
|
-
|
290
|
-
all_matches += matches
|
291
|
-
end
|
292
|
-
|
293
|
-
all_matches.reject! do |match|
|
294
|
-
@excluded_globs.detect { |excluded_glob| File.fnmatch(excluded_glob, match.path) }
|
295
|
-
end
|
296
|
-
all_matches.sort
|
297
|
-
end
|
298
|
-
|
299
|
-
def resolve_glob_in_dir(glob, dir)
|
300
|
-
Dir.chdir(dir) do
|
301
|
-
Dir.glob(glob, File::FNM_DOTMATCH).reject do |fn|
|
302
|
-
%w(. ..).include?(File.basename(fn))
|
303
|
-
end
|
304
|
-
end
|
305
|
-
end
|
306
|
-
|
307
|
-
def in_build_dir(&block)
|
308
|
-
Dir.chdir(build_dir) { yield }
|
309
|
-
end
|
310
|
-
|
311
|
-
def in_package_dir(&block)
|
312
|
-
Dir.chdir(package_dir) { yield }
|
313
|
-
end
|
314
|
-
|
315
|
-
end
|
316
|
-
end
|
data/lib/cli/packaging_helper.rb
DELETED
@@ -1,217 +0,0 @@
|
|
1
|
-
# This relies on having the following instance variables in a host class:
|
2
|
-
# @dev_builds_dir, @final_builds_dir, @blobstore,
|
3
|
-
# @name, @version, @tarball_path, @final, @artefact_type
|
4
|
-
|
5
|
-
module Bosh::Cli
|
6
|
-
module PackagingHelper
|
7
|
-
attr_accessor :dry_run
|
8
|
-
|
9
|
-
def init_indices
|
10
|
-
@dev_index = Versions::VersionsIndex.new(@dev_builds_dir)
|
11
|
-
@dev_storage = Versions::LocalVersionStorage.new(@dev_builds_dir)
|
12
|
-
|
13
|
-
@final_index = Versions::VersionsIndex.new(@final_builds_dir)
|
14
|
-
@final_storage = Versions::LocalVersionStorage.new(@final_builds_dir)
|
15
|
-
|
16
|
-
@final_resolver = Versions::VersionFileResolver.new(@final_storage, @blobstore)
|
17
|
-
end
|
18
|
-
|
19
|
-
def final?
|
20
|
-
@final
|
21
|
-
end
|
22
|
-
|
23
|
-
def dry_run?
|
24
|
-
@dry_run
|
25
|
-
end
|
26
|
-
|
27
|
-
def new_version?
|
28
|
-
@tarball_generated || @promoted || @will_be_promoted
|
29
|
-
end
|
30
|
-
|
31
|
-
def notes
|
32
|
-
notes = []
|
33
|
-
|
34
|
-
if @will_be_promoted
|
35
|
-
new_final_version = @version
|
36
|
-
notes << "new final version #{new_final_version}"
|
37
|
-
elsif new_version?
|
38
|
-
notes << 'new version'
|
39
|
-
end
|
40
|
-
|
41
|
-
notes
|
42
|
-
end
|
43
|
-
|
44
|
-
def build
|
45
|
-
with_indent(' ') do
|
46
|
-
use_final_version || use_dev_version || generate_tarball
|
47
|
-
end
|
48
|
-
upload_tarball(@tarball_path) if final? && !dry_run?
|
49
|
-
@will_be_promoted = true if final? && dry_run? && @used_dev_version
|
50
|
-
end
|
51
|
-
|
52
|
-
def use_final_version
|
53
|
-
say('Final version:', ' ')
|
54
|
-
|
55
|
-
item = @final_index[fingerprint]
|
56
|
-
|
57
|
-
if item.nil?
|
58
|
-
say('NOT FOUND'.make_red)
|
59
|
-
return nil
|
60
|
-
end
|
61
|
-
|
62
|
-
blobstore_id = item['blobstore_id']
|
63
|
-
version = item['version'] || fingerprint
|
64
|
-
sha1 = item['sha1']
|
65
|
-
|
66
|
-
if blobstore_id.nil?
|
67
|
-
say('No blobstore id'.make_red)
|
68
|
-
return nil
|
69
|
-
end
|
70
|
-
|
71
|
-
desc = "#{name} (#{version})"
|
72
|
-
|
73
|
-
@tarball_path = @final_resolver.find_file(blobstore_id, sha1, version, "package #{desc}")
|
74
|
-
|
75
|
-
@version = version
|
76
|
-
@used_final_version = true
|
77
|
-
true
|
78
|
-
rescue Bosh::Blobstore::NotFound
|
79
|
-
raise BlobstoreError, "Final version of `#{name}' not found in blobstore"
|
80
|
-
rescue Bosh::Blobstore::BlobstoreError => e
|
81
|
-
raise BlobstoreError, "Blobstore error: #{e}"
|
82
|
-
end
|
83
|
-
|
84
|
-
def use_dev_version
|
85
|
-
say('Dev version:', ' ')
|
86
|
-
item = @dev_index[fingerprint]
|
87
|
-
|
88
|
-
if item.nil?
|
89
|
-
say('NOT FOUND'.make_red)
|
90
|
-
return nil
|
91
|
-
end
|
92
|
-
|
93
|
-
version = @dev_index['version'] || fingerprint
|
94
|
-
|
95
|
-
if !@dev_storage.has_file?(version)
|
96
|
-
say('TARBALL MISSING'.make_red)
|
97
|
-
return nil
|
98
|
-
end
|
99
|
-
|
100
|
-
say('FOUND LOCAL'.make_green)
|
101
|
-
@tarball_path = @dev_storage.get_file(version)
|
102
|
-
|
103
|
-
if file_checksum(@tarball_path) != item['sha1']
|
104
|
-
say("`#{name} (#{version})' tarball corrupted".make_red)
|
105
|
-
return nil
|
106
|
-
end
|
107
|
-
|
108
|
-
if final? && !dry_run?
|
109
|
-
# copy from dev index/storage to final index/storage
|
110
|
-
@final_index.add_version(fingerprint, item)
|
111
|
-
@tarball_path = @final_storage.put_file(version, @tarball_path)
|
112
|
-
item['sha1'] = Digest::SHA1.file(@tarball_path).hexdigest
|
113
|
-
@final_index.update_version(fingerprint, item)
|
114
|
-
end
|
115
|
-
|
116
|
-
@version = version
|
117
|
-
@used_dev_version = true
|
118
|
-
end
|
119
|
-
|
120
|
-
def generate_tarball
|
121
|
-
version = fingerprint
|
122
|
-
tmp_file = Tempfile.new(name)
|
123
|
-
|
124
|
-
say('Generating...')
|
125
|
-
|
126
|
-
copy_files
|
127
|
-
|
128
|
-
in_build_dir do
|
129
|
-
tar_out = `tar -chzf #{tmp_file.path} . 2>&1`
|
130
|
-
unless $?.exitstatus == 0
|
131
|
-
raise PackagingError, "Cannot create tarball: #{tar_out}"
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
item = {
|
136
|
-
'version' => version
|
137
|
-
}
|
138
|
-
|
139
|
-
if final?
|
140
|
-
# add version (with its validation) before adding sha1
|
141
|
-
@final_index.add_version(fingerprint, item)
|
142
|
-
@tarball_path = @final_storage.put_file(fingerprint, tmp_file.path)
|
143
|
-
item['sha1'] = file_checksum(@tarball_path)
|
144
|
-
@final_index.update_version(fingerprint, item)
|
145
|
-
elsif dry_run?
|
146
|
-
else
|
147
|
-
# add version (with its validation) before adding sha1
|
148
|
-
@dev_index.add_version(fingerprint, item)
|
149
|
-
@tarball_path = @dev_storage.put_file(fingerprint, tmp_file.path)
|
150
|
-
item['sha1'] = file_checksum(@tarball_path)
|
151
|
-
@dev_index.update_version(fingerprint, item)
|
152
|
-
end
|
153
|
-
|
154
|
-
@version = version
|
155
|
-
@tarball_generated = true
|
156
|
-
say("Generated version #{version}".make_green)
|
157
|
-
true
|
158
|
-
end
|
159
|
-
|
160
|
-
def upload_tarball(path)
|
161
|
-
item = @final_index[fingerprint]
|
162
|
-
|
163
|
-
unless item
|
164
|
-
say("Failed to find entry `#{fingerprint}' in index, check local storage")
|
165
|
-
return
|
166
|
-
end
|
167
|
-
|
168
|
-
if item['blobstore_id']
|
169
|
-
return
|
170
|
-
end
|
171
|
-
|
172
|
-
say("Uploading final version `#{version}'...")
|
173
|
-
|
174
|
-
blobstore_id = nil
|
175
|
-
File.open(path, 'r') do |f|
|
176
|
-
blobstore_id = @blobstore.create(f)
|
177
|
-
end
|
178
|
-
|
179
|
-
say("Uploaded, blobstore id `#{blobstore_id}'")
|
180
|
-
item['blobstore_id'] = blobstore_id
|
181
|
-
@final_index.update_version(fingerprint, item)
|
182
|
-
@promoted = true
|
183
|
-
true
|
184
|
-
rescue Bosh::Blobstore::BlobstoreError => e
|
185
|
-
raise BlobstoreError, "Blobstore error: #{e}"
|
186
|
-
end
|
187
|
-
|
188
|
-
def file_checksum(path)
|
189
|
-
Digest::SHA1.file(path).hexdigest
|
190
|
-
end
|
191
|
-
|
192
|
-
def checksum
|
193
|
-
if @tarball_path && File.exists?(@tarball_path)
|
194
|
-
file_checksum(@tarball_path)
|
195
|
-
else
|
196
|
-
raise RuntimeError,
|
197
|
-
'cannot read checksum for not yet generated package/job'
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
# Git doesn't really track file permissions, it just looks at executable
|
202
|
-
# bit and uses 0755 if it's set or 0644 if not. We have to mimic that
|
203
|
-
# behavior in the fingerprint calculation to avoid the situation where
|
204
|
-
# seemingly clean working copy would trigger new fingerprints for
|
205
|
-
# artifacts with changed permissions. Also we don't want current
|
206
|
-
# fingerprints to change, hence the exact values below.
|
207
|
-
def tracked_permissions(path)
|
208
|
-
if File.directory?(path)
|
209
|
-
'40755'
|
210
|
-
elsif File.executable?(path)
|
211
|
-
'100755'
|
212
|
-
else
|
213
|
-
'100644'
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|