bosh_cli 1.2831.0 → 1.2839.0
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/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
|