summaryrefslogtreecommitdiff
path: root/app/services/packages/debian/generate_distribution_service.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/services/packages/debian/generate_distribution_service.rb')
-rw-r--r--app/services/packages/debian/generate_distribution_service.rb207
1 files changed, 207 insertions, 0 deletions
diff --git a/app/services/packages/debian/generate_distribution_service.rb b/app/services/packages/debian/generate_distribution_service.rb
new file mode 100644
index 00000000000..67348af1a49
--- /dev/null
+++ b/app/services/packages/debian/generate_distribution_service.rb
@@ -0,0 +1,207 @@
+# frozen_string_literal: true
+
+module Packages
+ module Debian
+ class GenerateDistributionService
+ include Gitlab::Utils::StrongMemoize
+ include ExclusiveLeaseGuard
+
+ # used by ExclusiveLeaseGuard
+ DEFAULT_LEASE_TIMEOUT = 1.hour.to_i.freeze
+
+ # From https://salsa.debian.org/ftp-team/dak/-/blob/991aaa27a7f7aa773bb9c0cf2d516e383d9cffa0/setup/core-init.d/080_metadatakeys#L9
+ BINARIES_METADATA = %w(
+ Package
+ Source
+ Binary
+ Version
+ Essential
+ Installed-Size
+ Maintainer
+ Uploaders
+ Original-Maintainer
+ Build-Depends
+ Build-Depends-Indep
+ Build-Conflicts
+ Build-Conflicts-Indep
+ Architecture
+ Standards-Version
+ Format
+ Files
+ Dm-Upload-Allowed
+ Vcs-Browse
+ Vcs-Hg
+ Vcs-Darcs
+ Vcs-Svn
+ Vcs-Git
+ Vcs-Browser
+ Vcs-Arch
+ Vcs-Bzr
+ Vcs-Mtn
+ Vcs-Cvs
+ Checksums-Sha256
+ Checksums-Sha1
+ Replaces
+ Provides
+ Depends
+ Pre-Depends
+ Recommends
+ Suggests
+ Enhances
+ Conflicts
+ Breaks
+ Description
+ Origin
+ Bugs
+ Multi-Arch
+ Homepage
+ Tag
+ Package-Type
+ Installer-Menu-Item
+ ).freeze
+
+ def initialize(distribution)
+ @distribution = distribution
+ @last_generated_at = nil
+ @md5sum = []
+ @sha256 = []
+ end
+
+ def execute
+ try_obtain_lease do
+ @distribution.transaction do
+ @last_generated_at = @distribution.component_files.maximum(:created_at)
+ generate_component_files
+ generate_release
+ destroy_old_component_files
+ end
+ end
+ end
+
+ private
+
+ def generate_component_files
+ @distribution.components.ordered_by_name.each do |component|
+ @distribution.architectures.ordered_by_name.each do |architecture|
+ generate_component_file(component, :packages, architecture, :deb)
+ end
+ end
+ end
+
+ def generate_component_file(component, component_file_type, architecture, package_file_type)
+ paragraphs = @distribution.package_files
+ .preload_debian_file_metadata
+ .with_debian_component_name(component.name)
+ .with_debian_architecture_name(architecture.name)
+ .with_debian_file_type(package_file_type)
+ .find_each
+ .map(&method(:package_stanza_from_fields))
+ create_component_file(component, component_file_type, architecture, package_file_type, paragraphs.join("\n"))
+ end
+
+ def package_stanza_from_fields(package_file)
+ [
+ BINARIES_METADATA.map do |metadata_key|
+ rfc822_field(metadata_key, package_file.debian_fields[metadata_key])
+ end,
+ rfc822_field('Section', package_file.debian_fields['Section'] || 'misc'),
+ rfc822_field('Priority', package_file.debian_fields['Priority'] || 'extra'),
+ rfc822_field('Filename', package_filename(package_file)),
+ rfc822_field('Size', package_file.size),
+ rfc822_field('MD5sum', package_file.file_md5),
+ rfc822_field('SHA256', package_file.file_sha256)
+ ].flatten.compact.join('')
+ end
+
+ def package_filename(package_file)
+ letter = package_file.package.name.start_with?('lib') ? package_file.package.name[0..3] : package_file.package.name[0]
+ "#{pool_prefix(package_file)}/#{letter}/#{package_file.package.name}/#{package_file.file_name}"
+ end
+
+ def pool_prefix(package_file)
+ case @distribution
+ when ::Packages::Debian::GroupDistribution
+ "pool/#{@distribution.codename}/#{package_file.package.project_id}"
+ else
+ "pool/#{@distribution.codename}/#{@distribution.container_id}"
+ end
+ end
+
+ def create_component_file(component, component_file_type, architecture, package_file_type, content)
+ component_file = component.files.create!(
+ file_type: component_file_type,
+ architecture: architecture,
+ compression_type: nil,
+ file: CarrierWaveStringFile.new(content),
+ file_md5: Digest::MD5.hexdigest(content),
+ file_sha256: Digest::SHA256.hexdigest(content)
+ )
+ @md5sum.append(" #{component_file.file_md5} #{component_file.size.to_s.rjust(8)} #{component_file.relative_path}")
+ @sha256.append(" #{component_file.file_sha256} #{component_file.size.to_s.rjust(8)} #{component_file.relative_path}")
+ end
+
+ def generate_release
+ @distribution.file = CarrierWaveStringFile.new(release_header + release_sums)
+ @distribution.updated_at = release_date
+ @distribution.save!
+ end
+
+ def release_header
+ strong_memoize(:release_header) do
+ [
+ %w[origin label suite version codename].map do |attribute|
+ rfc822_field(attribute.capitalize, @distribution.attributes[attribute])
+ end,
+ rfc822_field('Date', release_date.to_formatted_s(:rfc822)),
+ valid_until_field,
+ rfc822_field('NotAutomatic', !@distribution.automatic, !@distribution.automatic),
+ rfc822_field('ButAutomaticUpgrades', @distribution.automatic_upgrades, !@distribution.automatic && @distribution.automatic_upgrades),
+ rfc822_field('Architectures', @distribution.architectures.map { |architecture| architecture.name }.sort.join(' ')),
+ rfc822_field('Components', @distribution.components.map { |component| component.name }.sort.join(' ')),
+ rfc822_field('Description', @distribution.description)
+ ].flatten.compact.join('')
+ end
+ end
+
+ def release_date
+ strong_memoize(:release_date) do
+ Time.now.utc
+ end
+ end
+
+ def release_sums
+ ["MD5Sum:", @md5sum, "SHA256:", @sha256].flatten.compact.join("\n") + "\n"
+ end
+
+ def rfc822_field(name, value, condition = true)
+ return unless condition
+ return if value.blank?
+
+ "#{name}: #{value.to_s.gsub("\n\n", "\n.\n").gsub("\n", "\n ")}\n"
+ end
+
+ def valid_until_field
+ return unless @distribution.valid_time_duration_seconds
+
+ rfc822_field('Valid-Until', release_date.since(@distribution.valid_time_duration_seconds).to_formatted_s(:rfc822))
+ end
+
+ def destroy_old_component_files
+ # Only keep the last generation and one hour before
+ return if @last_generated_at.nil?
+
+ @distribution.component_files.created_before(@last_generated_at - 1.hour).destroy_all # rubocop:disable Cop/DestroyAll
+ end
+
+ # used by ExclusiveLeaseGuard
+ def lease_key
+ "packages:debian:generate_distribution_service:distribution:#{@distribution.id}"
+ end
+
+ # used by ExclusiveLeaseGuard
+ def lease_timeout
+ DEFAULT_LEASE_TIMEOUT
+ end
+ end
+ end
+end