208 lines
7 KiB
Ruby
208 lines
7 KiB
Ruby
|
# 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
|