-
Notifications
You must be signed in to change notification settings - Fork 368
Expand file tree
/
Copy pathapp_packager.rb
More file actions
119 lines (93 loc) · 3.56 KB
/
app_packager.rb
File metadata and controls
119 lines (93 loc) · 3.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
require 'find'
require 'open3'
require 'shellwords'
require 'zip'
require 'zip/filesystem'
class AppPackager
DIRECTORY_DELETE_BATCH_SIZE = 100
attr_reader :path
def initialize(zip_path, logger: nil)
@path = zip_path
@logger = logger
end
def unzip(destination_dir)
raise CloudController::Errors::ApiError.new_from_details('AppBitsUploadInvalid', 'Destination does not exist') unless File.directory?(destination_dir)
output, error, status = Open3.capture3(
%(unzip -qq -n #{Shellwords.escape(@path)} -d #{Shellwords.escape(destination_dir)})
)
return if status.success?
logger.error("Unzipping had errors\n STDOUT: \"#{output}\"\n STDERR: \"#{error}\"")
invalid_zip!(first_mapped_error(error))
end
def append_dir_contents(additional_contents_dir)
return if empty_directory?(additional_contents_dir)
output, error, status = Open3.capture3(
%(zip -q -r --symlinks #{Shellwords.escape(@path)} .),
chdir: additional_contents_dir
)
return if status.success?
logger.error("Could not zip the package\n STDOUT: \"#{output}\"\n STDERR: \"#{error}\"")
raise CloudController::Errors::ApiError.new_from_details('AppPackageInvalid', 'Error appending additional resources to package')
end
def fix_subdir_permissions(root_path, app_contents_path)
remove_dirs_from_zip(@path, get_dirs_from_zip(@path), root_path, app_contents_path)
rescue Zip::Error
invalid_zip!
end
def size
Zip::File.open(@path) do |in_zip|
in_zip.entries.sum(&:size)
end
rescue Zip::Error
invalid_zip!
end
private
def get_dirs_from_zip(zip_path)
Zip::File.open(zip_path) do |in_zip|
in_zip.entries.select(&:directory?).map(&:name)
end
end
def logger
@logger ||= Steno.logger('app_packager')
end
def remove_dirs_from_zip(zip_path, dirs_from_zip, root_path, app_contents_path)
fix_permissions_for_file_deletion(app_contents_path) unless empty_directory?(root_path)
dirs_from_zip.each_slice(DIRECTORY_DELETE_BATCH_SIZE) do |directory_slice|
remove_dir(zip_path, directory_slice)
end
end
def remove_dir(zip_path, directories)
directory_arg_list = directories.map { |dir| Shellwords.escape(dir) }.join(' ')
stdout, error, status = Open3.capture3(
%(zip -d #{Shellwords.escape(zip_path)}) + ' -- ' + directory_arg_list
)
return if status.success?
logger.error("Could not remove the directories\n STDOUT: \"#{stdout}\"\n STDERR: \"#{error}\"")
raise CloudController::Errors::ApiError.new_from_details('AppPackageInvalid', 'Error removing zip directories.')
end
def fix_permissions_for_file_deletion(destination_dir)
stdout, error, status = Open3.capture3(%(chmod -R u+rwX #{Shellwords.escape(destination_dir)}))
return if status.success?
logger.error("Cleanup of some files may have failed, error fixing zip file permissions\n STDOUT: \"#{stdout}\"\n STDERR: \"#{error}\"")
end
def empty_directory?(dir)
(Dir.entries(dir) - %w[.. .]).empty?
end
def invalid_zip!(error=nil)
message = 'Invalid zip archive'
message += " (#{error})" unless error.nil?
message += '.'
raise CloudController::Errors::ApiError.new_from_details('AppBitsUploadInvalid', message)
end
def first_mapped_error(error)
return if error.nil? || error.empty?
case error
when /end-of-central-directory\s+signature\s+not\s+found/i
'end-of-central-directory signature not found'
when /zipfile\s+is\s+empty/i
'zipfile is empty'
when /mismatching\s+"local"\s+filename/i
'mismatching local filename'
end
end
end