114 lines
3.5 KiB
Crystal
114 lines
3.5 KiB
Crystal
require "openssl"
|
|
|
|
require "../backends.cr"
|
|
|
|
# Baguette package backend
|
|
#
|
|
# Packages contain:
|
|
# - data.tar.zst: compressed tarball containing all installed data
|
|
# - manifest: list of directories, files and their checksum
|
|
# - control.spec: meta data about the package
|
|
#
|
|
# TODO:
|
|
# - security: add a signature file
|
|
|
|
class Package::Backend::Packaging::Baguette < Package::Backend::Packaging
|
|
def initialize
|
|
@name = "package"
|
|
end
|
|
|
|
def package(pkgdir : String, architecture : String, package : ::Package::Package) : Bool
|
|
# Fake root example: /tmp/packages/<uuid>/root-xz-dev
|
|
fake_root = package.fake_root_directory
|
|
|
|
# Temporary archive, before compression.
|
|
data_archive_path = "#{fake_root}/data.tar" # All archive data.
|
|
# Content of the final tarball.
|
|
compressed_data_archive_path = "#{fake_root}/data.tar.zst" # Compressed version (in final tarball).
|
|
control_spec_file_path = "#{fake_root}/control.spec" # Spec file, with package informations.
|
|
manifest_file_path = "#{fake_root}/manifest" # List of all the included files.
|
|
|
|
package_target = "#{pkgdir}/"
|
|
package_target += "#{architecture}/"
|
|
package_target += "#{package.name}-#{package.version}-#{package.release}.baguette"
|
|
Do.mkdir_p File.dirname package_target
|
|
|
|
::Baguette::Log.detail "Archiving package content"
|
|
Do.run fake_root, "tar", ["cvf", data_archive_path, "."]
|
|
|
|
::Baguette::Log.detail "Compressing the archive"
|
|
# produces data.tar.zst
|
|
Do.run fake_root, "zstd", ["--ultra", data_archive_path]
|
|
|
|
::Baguette::Log.detail "Generating control.spec"
|
|
generate_spec package, control_spec_file_path
|
|
|
|
::Baguette::Log.detail "Generating manifest"
|
|
generate_manifest package, manifest_file_path
|
|
|
|
::Baguette::Log.detail "Assembling '#{package_target}'"
|
|
r = Do.run fake_root, "tar", [
|
|
"cf", package_target,
|
|
# WARNING: relative paths are necessary.
|
|
"control.spec", "manifest", "data.tar.zst"
|
|
]
|
|
|
|
r.exit_status == 0
|
|
end
|
|
|
|
def generate_spec(package : ::Package::Package, file_name : String)
|
|
du = `du -sk #{package.fake_root_directory}`
|
|
size = du.sub(/[ \t].*/, "").to_u64 * 1024_u64
|
|
|
|
File.open file_name, "w" do |file|
|
|
file.puts "name: #{package.name}"
|
|
file.puts "version: #{package.version}"
|
|
file.puts "release: #{package.release}"
|
|
file.puts
|
|
file.puts "size: #{size}"
|
|
file.puts "origin: #{package.recipe.name}"
|
|
file.puts "slot: #{package.prefix}"
|
|
file.puts
|
|
file.puts "url: #{package.url}"
|
|
file.puts "description: #{package.description}"
|
|
file.puts
|
|
file.puts "dependencies: #{package.dependencies.join ", "}"
|
|
file.puts "conflicts: #{package.conflicts.join ", "}"
|
|
file.puts "provides: #{package.provides.join ", "}"
|
|
end
|
|
end
|
|
|
|
def generate_manifest(package : ::Package::Package, file_name : String)
|
|
old_pwd = Dir.current
|
|
|
|
manifest = File.open(file_name, "w").not_nil!
|
|
|
|
Do.cd package.fake_root_directory
|
|
|
|
FileUtils.find "." do |path|
|
|
file = path.lchop
|
|
|
|
if File.symlink? path
|
|
manifest.puts [file, "symlink", File.readlink(path)].join ':'
|
|
elsif File.directory? path
|
|
manifest.puts [file, "directory"].join ':'
|
|
elsif File.info?(path).try &.file?
|
|
digest = OpenSSL::Digest.new("sha256").file(path).final.hexstring
|
|
manifest.puts [file, "file", digest].join ':'
|
|
else
|
|
manifest.puts [file, "other"].join ':'
|
|
end
|
|
end
|
|
|
|
manifest.close
|
|
ensure
|
|
Do.cd old_pwd.not_nil!
|
|
end
|
|
|
|
def self.install(packages : Array(String))
|
|
# TODO: install packages through package-install
|
|
raise "call to package-install not implemented, yet"
|
|
end
|
|
end
|
|
|