deb-s3-lock-fix 0.11.8.fix0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,96 @@
1
+ require "aws-sdk-dynamodb"
2
+ require "securerandom"
3
+ require "etc"
4
+ require "time"
5
+
6
+ class Deb::S3::Lock
7
+ attr_reader :user, :host_with_uuid
8
+
9
+ DYNAMODB_TABLE_NAME = 'deb-s3-lock'
10
+
11
+ def initialize(user, host_with_uuid)
12
+ @user = user
13
+ @host_with_uuid = host_with_uuid
14
+ end
15
+
16
+ def self.dynamodb
17
+ @dynamodb ||= begin
18
+ validate_environment_variables!
19
+ Aws::DynamoDB::Client.new(
20
+ access_key_id: ENV['DEB_S3_ACCESS_KEY_ID'],
21
+ secret_access_key: ENV['DEB_S3_SECRET_ACCESS_KEY'],
22
+ region: ENV['AWS_BUILDERS_REGION']
23
+ )
24
+ end
25
+ end
26
+
27
+ def self.validate_environment_variables!
28
+ %w[DEB_S3_LOCK_ACCESS_KEY_ID DEB_S3_LOCK_SECRET_ACCESS_KEY AWS_BUILDERS_REGION].each do |var|
29
+ raise "Environment variable #{var} not set." unless ENV[var]
30
+ end
31
+ end
32
+
33
+ class << self
34
+ def lock(codename, max_attempts = 60, max_wait_interval = 10)
35
+ uuid = SecureRandom.uuid
36
+ lock_body = "#{Etc.getlogin}@#{Socket.gethostname}-#{uuid}"
37
+ lock_key = codename
38
+
39
+ $stderr.puts("Current job's hostname with UUID: #{lock_body}")
40
+
41
+ max_attempts.times do |i|
42
+ wait_interval = [2**i, max_wait_interval].min
43
+
44
+ expiration_time = Time.now.to_i + 45
45
+ begin
46
+ dynamodb.put_item({
47
+ table_name: DYNAMODB_TABLE_NAME,
48
+ item: {
49
+ 'lock_key' => "52",
50
+ 'lock_body' => lock_body,
51
+ 'ttl' => expiration_time
52
+ },
53
+ condition_expression: "attribute_not_exists(lock_key)"
54
+ })
55
+ return
56
+ rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException
57
+ lock_holder = current_lock_holder(codename)
58
+ current_time = Time.now.strftime("%Y-%m-%d %H:%M:%S")
59
+ $stderr.puts("[#{current_time}] Repository is locked by another user: #{lock_holder.user} at host #{lock_holder.host_with_uuid}")
60
+ $stderr.puts("Attempting to obtain a lock after #{wait_interval} second(s).")
61
+ sleep(wait_interval)
62
+ end
63
+ end
64
+
65
+ raise "Unable to obtain a lock after #{max_attempts} attemtps, giving up."
66
+ end
67
+
68
+ def unlock(codename)
69
+ # dynamodb.delete_item({
70
+ # table_name: DYNAMODB_TABLE_NAME,
71
+ # key: {
72
+ # 'lock_key' => codename
73
+ # }
74
+ # })
75
+ end
76
+
77
+ def current_lock_holder(codename)
78
+ response = dynamodb.get_item({
79
+ table_name: DYNAMODB_TABLE_NAME,
80
+ key: {
81
+ 'lock_key' => codename
82
+ }
83
+ })
84
+
85
+ if response.item
86
+ lockdata = response.item['lock_body']
87
+ user, host_with_uuid = lockdata.split("@", 2)
88
+ Deb::S3::Lock.new(user, host_with_uuid)
89
+ else
90
+ Deb::S3::Lock.new("unknown", "unknown")
91
+ end
92
+ end
93
+
94
+ private :dynamodb, :validate_environment_variables!
95
+ end
96
+ end
@@ -0,0 +1,144 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require "tempfile"
3
+ require "zlib"
4
+ require 'deb/s3/utils'
5
+ require 'deb/s3/package'
6
+
7
+ class Deb::S3::Manifest
8
+ include Deb::S3::Utils
9
+
10
+ attr_accessor :codename
11
+ attr_accessor :component
12
+ attr_accessor :cache_control
13
+ attr_accessor :architecture
14
+ attr_accessor :fail_if_exists
15
+ attr_accessor :skip_package_upload
16
+
17
+ attr_accessor :files
18
+
19
+ attr_reader :packages
20
+ attr_reader :packages_to_be_upload
21
+
22
+ def initialize
23
+ @packages = []
24
+ @packages_to_be_upload = []
25
+ @component = nil
26
+ @architecture = nil
27
+ @files = {}
28
+ @cache_control = ""
29
+ @fail_if_exists = false
30
+ @skip_package_upload = false
31
+ end
32
+
33
+ class << self
34
+ def retrieve(codename, component, architecture, cache_control, fail_if_exists, skip_package_upload=false)
35
+ m = if s = Deb::S3::Utils.s3_read("dists/#{codename}/#{component}/binary-#{architecture}/Packages")
36
+ self.parse_packages(s)
37
+ else
38
+ self.new
39
+ end
40
+
41
+ m.codename = codename
42
+ m.component = component
43
+ m.architecture = architecture
44
+ m.cache_control = cache_control
45
+ m.fail_if_exists = fail_if_exists
46
+ m.skip_package_upload = skip_package_upload
47
+ m
48
+ end
49
+
50
+ def parse_packages(str)
51
+ m = self.new
52
+ str.split("\n\n").each do |s|
53
+ next if s.chomp.empty?
54
+ m.packages << Deb::S3::Package.parse_string(s)
55
+ end
56
+ m
57
+ end
58
+ end
59
+
60
+ def add(pkg, preserve_versions, needs_uploading=true)
61
+ if self.fail_if_exists
62
+ packages.each { |p|
63
+ next unless p.name == pkg.name && \
64
+ p.full_version == pkg.full_version && \
65
+ File.basename(p.url_filename(@codename)) != \
66
+ File.basename(pkg.url_filename(@codename))
67
+ raise AlreadyExistsError,
68
+ "package #{pkg.name}_#{pkg.full_version} already exists " \
69
+ "with different filename (#{p.url_filename(@codename)})"
70
+ }
71
+ end
72
+ if preserve_versions
73
+ packages.delete_if { |p| p.name == pkg.name && p.full_version == pkg.full_version }
74
+ else
75
+ packages.delete_if { |p| p.name == pkg.name }
76
+ end
77
+ packages << pkg
78
+ packages_to_be_upload << pkg if needs_uploading
79
+ pkg
80
+ end
81
+
82
+ def delete_package(pkg, versions=nil)
83
+ deleted = []
84
+ new_packages = @packages.select { |p|
85
+ # Include packages we didn't name
86
+ if p.name != pkg
87
+ p
88
+ # Also include the packages not matching a specified version
89
+ elsif (!versions.nil? and p.name == pkg and !versions.include?(p.version) and !versions.include?("#{p.version}-#{p.iteration}") and !versions.include?(p.full_version))
90
+ p
91
+ end
92
+ }
93
+ deleted = @packages - new_packages
94
+ @packages = new_packages
95
+ deleted
96
+ end
97
+
98
+ def generate
99
+ @packages.collect { |pkg| pkg.generate(@codename) }.join("\n")
100
+ end
101
+
102
+ def write_to_s3
103
+ manifest = self.generate
104
+
105
+ unless self.skip_package_upload
106
+ # store any packages that need to be stored
107
+ @packages_to_be_upload.each do |pkg|
108
+ yield pkg.url_filename(@codename) if block_given?
109
+ s3_store(pkg.filename, pkg.url_filename(@codename), 'application/octet-stream; charset=binary', self.cache_control, self.fail_if_exists)
110
+ end
111
+ end
112
+
113
+ # generate the Packages file
114
+ pkgs_temp = Tempfile.new("Packages")
115
+ pkgs_temp.write manifest
116
+ pkgs_temp.close
117
+ f = "dists/#{@codename}/#{@component}/binary-#{@architecture}/Packages"
118
+ yield f if block_given?
119
+ s3_store(pkgs_temp.path, f, 'text/plain; charset=utf-8', self.cache_control)
120
+ @files["#{@component}/binary-#{@architecture}/Packages"] = hashfile(pkgs_temp.path)
121
+ pkgs_temp.unlink
122
+
123
+ # generate the Packages.gz file
124
+ gztemp = Tempfile.new("Packages.gz")
125
+ gztemp.close
126
+ Zlib::GzipWriter.open(gztemp.path) { |gz| gz.write manifest }
127
+ f = "dists/#{@codename}/#{@component}/binary-#{@architecture}/Packages.gz"
128
+ yield f if block_given?
129
+ s3_store(gztemp.path, f, 'application/x-gzip; charset=binary', self.cache_control)
130
+ @files["#{@component}/binary-#{@architecture}/Packages.gz"] = hashfile(gztemp.path)
131
+ gztemp.unlink
132
+
133
+ nil
134
+ end
135
+
136
+ def hashfile(path)
137
+ {
138
+ :size => File.size(path),
139
+ :sha1 => Digest::SHA1.file(path).hexdigest,
140
+ :sha256 => Digest::SHA2.file(path).hexdigest,
141
+ :md5 => Digest::MD5.file(path).hexdigest
142
+ }
143
+ end
144
+ end
@@ -0,0 +1,309 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require "digest/sha1"
3
+ require "digest/sha2"
4
+ require "digest/md5"
5
+ require "open3"
6
+ require "socket"
7
+ require "tmpdir"
8
+ require "uri"
9
+
10
+ require 'deb/s3/utils'
11
+
12
+ class Deb::S3::Package
13
+ include Deb::S3::Utils
14
+
15
+ attr_accessor :name
16
+ attr_accessor :version
17
+ attr_accessor :epoch
18
+ attr_accessor :iteration
19
+ attr_accessor :maintainer
20
+ attr_accessor :vendor
21
+ attr_accessor :url
22
+ attr_accessor :category
23
+ attr_accessor :license
24
+ attr_accessor :architecture
25
+ attr_accessor :description
26
+
27
+ attr_accessor :dependencies
28
+
29
+ # Any other attributes specific to this package.
30
+ # This is where you'd put rpm, deb, or other specific attributes.
31
+ attr_accessor :attributes
32
+
33
+ # hashes
34
+ attr_accessor :sha1
35
+ attr_accessor :sha256
36
+ attr_accessor :md5
37
+ attr_accessor :size
38
+
39
+ attr_accessor :filename
40
+
41
+ class << self
42
+ include Deb::S3::Utils
43
+
44
+ def parse_file(package)
45
+ p = self.new
46
+ p.extract_info(extract_control(package))
47
+ p.apply_file_info(package)
48
+ p.filename = package
49
+ p
50
+ end
51
+
52
+ def parse_string(s)
53
+ p = self.new
54
+ p.extract_info(s)
55
+ p
56
+ end
57
+
58
+ def extract_control(package)
59
+ if system("which dpkg > /dev/null 2>&1")
60
+ output, status = Open3.capture2("dpkg", "-f", package)
61
+ output
62
+ else
63
+ # use ar to determine control file name (control.ext)
64
+ package_files = `ar t #{package}`
65
+ control_file = package_files.split("\n").select do |file|
66
+ file.start_with?("control.")
67
+ end.first
68
+ if control_file === "control.tar.gz"
69
+ compression = "z"
70
+ elsif control_file === "control.tar.zst"
71
+ compression = "I zstd"
72
+ else
73
+ compression = "J"
74
+ end
75
+
76
+ # ar fails to find the control.tar.gz tarball within the .deb
77
+ # on Mac OS. Try using ar to list the control file, if found,
78
+ # use ar to extract, otherwise attempt with tar which works on OS X.
79
+ extract_control_tarball_cmd = "ar p #{package} #{control_file}"
80
+
81
+ begin
82
+ safesystem("ar t #{package} #{control_file} &> /dev/null")
83
+ rescue SafeSystemError
84
+ warn "Failed to find control data in .deb with ar, trying tar."
85
+ extract_control_tarball_cmd = "tar -#{compression} -xf #{package} --to-stdout #{control_file}"
86
+ end
87
+
88
+ Dir.mktmpdir do |path|
89
+ safesystem("#{extract_control_tarball_cmd} | tar -#{compression} -xf - -C #{path}")
90
+ File.read(File.join(path, "control"))
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ def initialize
97
+ @attributes = {}
98
+
99
+ # Reference
100
+ # http://www.debian.org/doc/manuals/maint-guide/first.en.html
101
+ # http://wiki.debian.org/DeveloperConfiguration
102
+ # https://github.com/jordansissel/fpm/issues/37
103
+ if ENV.include?("DEBEMAIL") and ENV.include?("DEBFULLNAME")
104
+ # Use DEBEMAIL and DEBFULLNAME as the default maintainer if available.
105
+ @maintainer = "#{ENV["DEBFULLNAME"]} <#{ENV["DEBEMAIL"]}>"
106
+ else
107
+ # TODO(sissel): Maybe support using 'git config' for a default as well?
108
+ # git config --get user.name, etc can be useful.
109
+ #
110
+ # Otherwise default to user@currenthost
111
+ @maintainer = "<#{ENV["USER"]}@#{Socket.gethostname}>"
112
+ end
113
+
114
+ @name = nil
115
+ @architecture = "native"
116
+ @description = "no description given"
117
+ @version = nil
118
+ @epoch = nil
119
+ @iteration = nil
120
+ @url = nil
121
+ @category = "default"
122
+ @license = "unknown"
123
+ @vendor = "none"
124
+ @sha1 = nil
125
+ @sha256 = nil
126
+ @md5 = nil
127
+ @size = nil
128
+ @filename = nil
129
+ @url_filename = nil
130
+
131
+ @dependencies = []
132
+ end
133
+
134
+ def full_version
135
+ return nil if [epoch, version, iteration].all?(&:nil?)
136
+ [[epoch, version].compact.join(":"), iteration].compact.join("-")
137
+ end
138
+
139
+ def url_filename=(f)
140
+ @url_filename = f
141
+ end
142
+
143
+ def url_filename(codename)
144
+ @url_filename || "pool/#{codename}/#{self.name[0]}/#{self.name[0..1]}/#{File.basename(self.filename)}"
145
+ end
146
+
147
+ def generate(codename)
148
+ template("package.erb").result(binding)
149
+ end
150
+
151
+ # from fpm
152
+ def parse_depends(data)
153
+ return [] if data.nil? or data.empty?
154
+ # parse dependencies. Debian dependencies come in one of two forms:
155
+ # * name
156
+ # * name (op version)
157
+ # They are all on one line, separated by ", "
158
+
159
+ dep_re = /^([^ ]+)(?: \(([>=<]+) ([^)]+)\))?$/
160
+ return data.split(/, */).collect do |dep|
161
+ m = dep_re.match(dep)
162
+ if m
163
+ name, op, version = m.captures
164
+ # this is the proper form of dependency
165
+ if op && version && op != "" && version != ""
166
+ "#{name} (#{op} #{version})".strip
167
+ else
168
+ name.strip
169
+ end
170
+ else
171
+ # Assume normal form dependency, "name op version".
172
+ dep
173
+ end
174
+ end
175
+ end # def parse_depends
176
+
177
+ # from fpm
178
+ def fix_dependency(dep)
179
+ # Deb dependencies are: NAME (OP VERSION), like "zsh (> 3.0)"
180
+ # Convert anything that looks like 'NAME OP VERSION' to this format.
181
+ if dep =~ /[\(,\|]/
182
+ # Don't "fix" ones that could appear well formed already.
183
+ else
184
+ # Convert ones that appear to be 'name op version'
185
+ name, op, version = dep.split(/ +/)
186
+ if !version.nil?
187
+ # Convert strings 'foo >= bar' to 'foo (>= bar)'
188
+ dep = "#{name} (#{debianize_op(op)} #{version})"
189
+ end
190
+ end
191
+
192
+ name_re = /^[^ \(]+/
193
+ name = dep[name_re]
194
+ if name =~ /[A-Z]/
195
+ dep = dep.gsub(name_re) { |n| n.downcase }
196
+ end
197
+
198
+ if dep.include?("_")
199
+ dep = dep.gsub("_", "-")
200
+ end
201
+
202
+ # Convert gem ~> X.Y.Z to '>= X.Y.Z' and << X.Y+1.0
203
+ if dep =~ /\(~>/
204
+ name, version = dep.gsub(/[()~>]/, "").split(/ +/)[0..1]
205
+ nextversion = version.split(".").collect { |v| v.to_i }
206
+ l = nextversion.length
207
+ nextversion[l-2] += 1
208
+ nextversion[l-1] = 0
209
+ nextversion = nextversion.join(".")
210
+ return ["#{name} (>= #{version})", "#{name} (<< #{nextversion})"]
211
+ elsif (m = dep.match(/(\S+)\s+\(!= (.+)\)/))
212
+ # Append this to conflicts
213
+ self.conflicts += [dep.gsub(/!=/,"=")]
214
+ return []
215
+ elsif (m = dep.match(/(\S+)\s+\(= (.+)\)/)) and
216
+ self.attributes[:deb_ignore_iteration_in_dependencies?]
217
+ # Convert 'foo (= x)' to 'foo (>= x)' and 'foo (<< x+1)'
218
+ # but only when flag --ignore-iteration-in-dependencies is passed.
219
+ name, version = m[1..2]
220
+ nextversion = version.split('.').collect { |v| v.to_i }
221
+ nextversion[-1] += 1
222
+ nextversion = nextversion.join(".")
223
+ return ["#{name} (>= #{version})", "#{name} (<< #{nextversion})"]
224
+ else
225
+ # otherwise the dep is probably fine
226
+ return dep.rstrip
227
+ end
228
+ end # def fix_dependency
229
+
230
+ # from fpm
231
+ def extract_info(control)
232
+ fields = parse_control(control)
233
+
234
+ # Parse 'epoch:version-iteration' in the version string
235
+ full_version = fields.delete('Version')
236
+ if full_version !~ /^(?:([0-9]+):)?(.+?)(?:-(.*))?$/
237
+ raise "Unsupported version string '#{full_version}'"
238
+ end
239
+ self.epoch, self.version, self.iteration = $~.captures
240
+
241
+ self.architecture = fields.delete('Architecture')
242
+ self.category = fields.delete('Section')
243
+ self.license = fields.delete('License') || self.license
244
+ self.maintainer = fields.delete('Maintainer')
245
+ self.name = fields.delete('Package')
246
+ self.url = fields.delete('Homepage')
247
+ self.vendor = fields.delete('Vendor') || self.vendor
248
+ self.attributes[:deb_priority] = fields.delete('Priority')
249
+ self.attributes[:deb_origin] = fields.delete('Origin')
250
+ self.attributes[:deb_installed_size] = fields.delete('Installed-Size')
251
+
252
+ # Packages manifest fields
253
+ self.url_filename = fields.delete('Filename')
254
+ self.sha1 = fields.delete('SHA1')
255
+ self.sha256 = fields.delete('SHA256')
256
+ self.md5 = fields.delete('MD5sum')
257
+ self.size = fields.delete('Size')
258
+ self.description = fields.delete('Description')
259
+
260
+ #self.config_files = config_files
261
+
262
+ self.dependencies += Array(parse_depends(fields.delete('Depends')))
263
+
264
+ self.attributes[:deb_recommends] = fields.delete('Recommends')
265
+ self.attributes[:deb_suggests] = fields.delete('Suggests')
266
+ self.attributes[:deb_enhances] = fields.delete('Enhances')
267
+ self.attributes[:deb_pre_depends] = fields.delete('Pre-Depends')
268
+
269
+ self.attributes[:deb_breaks] = fields.delete('Breaks')
270
+ self.attributes[:deb_conflicts] = fields.delete('Conflicts')
271
+ self.attributes[:deb_provides] = fields.delete('Provides')
272
+ self.attributes[:deb_replaces] = fields.delete('Replaces')
273
+
274
+ self.attributes[:deb_field] = Hash[fields.map { |k, v|
275
+ [k.sub(/\AX[BCS]{0,3}-/, ''), v]
276
+ }]
277
+ end # def extract_info
278
+
279
+ def apply_file_info(file)
280
+ self.size = File.size(file)
281
+ self.sha1 = Digest::SHA1.file(file).hexdigest
282
+ self.sha256 = Digest::SHA2.file(file).hexdigest
283
+ self.md5 = Digest::MD5.file(file).hexdigest
284
+ end
285
+
286
+ def parse_control(control)
287
+ field = nil
288
+ value = ""
289
+ {}.tap do |fields|
290
+ control.each_line do |line|
291
+ if line =~ /^(\s+)(\S.*)$/
292
+ indent, rest = $1, $2
293
+ # Continuation
294
+ if indent.size == 1 && rest == "."
295
+ value << "\n"
296
+ rest = ""
297
+ elsif value.size > 0
298
+ value << "\n"
299
+ end
300
+ value << rest
301
+ elsif line =~ /^([-\w]+):(.*)$/
302
+ fields[field] = value if field
303
+ field, value = $1, $2.strip
304
+ end
305
+ end
306
+ fields[field] = value if field
307
+ end
308
+ end
309
+ end
@@ -0,0 +1,161 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require "tempfile"
3
+
4
+ class Deb::S3::Release
5
+ include Deb::S3::Utils
6
+
7
+ attr_accessor :codename
8
+ attr_accessor :origin
9
+ attr_accessor :suite
10
+ attr_accessor :architectures
11
+ attr_accessor :components
12
+ attr_accessor :cache_control
13
+
14
+ attr_accessor :files
15
+ attr_accessor :policy
16
+
17
+ def initialize
18
+ @origin = nil
19
+ @suite = nil
20
+ @codename = nil
21
+ @architectures = []
22
+ @components = []
23
+ @cache_control = ""
24
+ @files = {}
25
+ @policy = :public_read
26
+ end
27
+
28
+ class << self
29
+ def retrieve(codename, origin=nil, suite=nil, cache_control=nil)
30
+ if s = Deb::S3::Utils.s3_read("dists/#{codename}/Release")
31
+ rel = self.parse_release(s)
32
+ else
33
+ rel = self.new
34
+ end
35
+ rel.codename = codename
36
+ rel.origin = origin unless origin.nil?
37
+ rel.suite = suite unless suite.nil?
38
+ rel.cache_control = cache_control
39
+ rel
40
+ end
41
+
42
+ def parse_release(str)
43
+ rel = self.new
44
+ rel.parse(str)
45
+ rel
46
+ end
47
+ end
48
+
49
+ def filename
50
+ "dists/#{@codename}/Release"
51
+ end
52
+
53
+ def parse(str)
54
+ parse = lambda do |field|
55
+ value = str[/^#{field}: .*/]
56
+ if value.nil?
57
+ return nil
58
+ else
59
+ return value.split(": ",2).last
60
+ end
61
+ end
62
+
63
+ # grab basic fields
64
+ self.codename = parse.call("Codename")
65
+ self.origin = parse.call("Origin") || nil
66
+ self.suite = parse.call("Suite") || nil
67
+ self.architectures = (parse.call("Architectures") || "").split(/\s+/)
68
+ self.components = (parse.call("Components") || "").split(/\s+/)
69
+
70
+ # find all the hashes
71
+ str.scan(/^\s+([^\s]+)\s+(\d+)\s+(.+)$/).each do |(hash,size,name)|
72
+ self.files[name] ||= { :size => size.to_i }
73
+ case hash.length
74
+ when 32
75
+ self.files[name][:md5] = hash
76
+ when 40
77
+ self.files[name][:sha1] = hash
78
+ when 64
79
+ self.files[name][:sha256] = hash
80
+ end
81
+ end
82
+ end
83
+
84
+ def generate
85
+ template("release.erb").result(binding)
86
+ end
87
+
88
+ def write_to_s3
89
+ # validate some other files are present
90
+ if block_given?
91
+ self.validate_others { |f| yield f }
92
+ else
93
+ self.validate_others
94
+ end
95
+
96
+ # generate the Release file
97
+ release_tmp = Tempfile.new("Release")
98
+ release_tmp.puts self.generate
99
+ release_tmp.close
100
+ yield self.filename if block_given?
101
+ s3_store(release_tmp.path, self.filename, 'text/plain; charset=utf-8', self.cache_control)
102
+
103
+ # sign the file, if necessary
104
+ if Deb::S3::Utils.signing_key
105
+ key_param = Deb::S3::Utils.signing_key.any? ? "-u #{Deb::S3::Utils.signing_key.join(" -u ")}" : ""
106
+ if system("gpg -a #{key_param} --digest-algo SHA256 #{Deb::S3::Utils.gpg_options} -s --clearsign #{release_tmp.path}")
107
+ local_file = release_tmp.path+".asc"
108
+ remote_file = "dists/#{@codename}/InRelease"
109
+ yield remote_file if block_given?
110
+ raise "Unable to locate InRelease file" unless File.exist?(local_file)
111
+ s3_store(local_file, remote_file, 'application/pgp-signature; charset=us-ascii', self.cache_control)
112
+ File.unlink(local_file)
113
+ else
114
+ raise "Signing the InRelease file failed."
115
+ end
116
+ if system("gpg -a #{key_param} --digest-algo SHA256 #{Deb::S3::Utils.gpg_options} -b #{release_tmp.path}")
117
+ local_file = release_tmp.path+".asc"
118
+ remote_file = self.filename+".gpg"
119
+ yield remote_file if block_given?
120
+ raise "Unable to locate Release signature file" unless File.exist?(local_file)
121
+ s3_store(local_file, remote_file, 'application/pgp-signature; charset=us-ascii', self.cache_control)
122
+ File.unlink(local_file)
123
+ else
124
+ raise "Signing the Release file failed."
125
+ end
126
+ else
127
+ # remove an existing Release.gpg, if it was there
128
+ s3_remove(self.filename+".gpg")
129
+ end
130
+
131
+ release_tmp.unlink
132
+ end
133
+
134
+ def update_manifest(manifest)
135
+ self.components << manifest.component unless self.components.include?(manifest.component)
136
+ self.architectures << manifest.architecture unless self.architectures.include?(manifest.architecture)
137
+ self.files.merge!(manifest.files)
138
+ end
139
+
140
+ def validate_others
141
+ to_apply = []
142
+ self.components.each do |comp|
143
+ %w(amd64 i386 armhf).each do |arch|
144
+ next if self.files.has_key?("#{comp}/binary-#{arch}/Packages")
145
+
146
+ m = Deb::S3::Manifest.new
147
+ m.codename = self.codename
148
+ m.component = comp
149
+ m.architecture = arch
150
+ if block_given?
151
+ m.write_to_s3 { |f| yield f }
152
+ else
153
+ m.write_to_s3
154
+ end
155
+ to_apply << m
156
+ end
157
+ end
158
+
159
+ to_apply.each { |m| self.update_manifest(m) }
160
+ end
161
+ end