package/src/package/context.cr

267 lines
5.7 KiB
Crystal
Raw Normal View History

2019-10-31 21:57:58 +01:00
require "colorize"
require "uuid"
require "file_utils"
2019-11-02 20:59:08 +01:00
require "uri"
2019-10-31 21:57:58 +01:00
2019-11-09 11:40:33 +01:00
require "weird-crystal-base"
2019-10-31 21:57:58 +01:00
require "./weird.cr"
2019-11-02 20:59:08 +01:00
require "./configuration.cr"
require "./repository.cr"
2019-10-31 21:57:58 +01:00
2019-11-09 11:40:33 +01:00
class Package::Context < Weird::Base
2019-10-31 21:57:58 +01:00
property root = "/"
# Stores informations about already installed packages, their
# manifests, and so on.
@database = Database.new
2019-11-02 20:59:08 +01:00
@configuration = Configuration.new
@cache_directory = "/var/cache/package"
2019-10-31 21:57:58 +01:00
def initialize()
2019-11-02 20:59:08 +01:00
self.root = "/"
2019-10-31 21:57:58 +01:00
end
def root=(@root)
@database.root = "#{@root}#{Database::PATH_FROM_ROOT}"
2019-11-02 20:59:08 +01:00
@configuration = Configuration.new "#{@root}#{Configuration::PATH_FROM_ROOT}"
2019-11-05 14:32:30 +01:00
@configuration.repositories.each &.import_index(self)
2019-10-31 21:57:58 +01:00
end
def install(package : Package, data_directory : String)
end
# FIXME: Should atomic upgrades be handled here?
def install(files : Array(String))
work_dirs = Hash(String, String).new
manifests = Hash(String, Manifest).new
packages = Hash(String, Package).new
files.each do |file|
work_dir = get_work_directory
data_dir = "#{work_dir}/data"
work_dirs[file] = work_dir
Weird.extract file, work_dir, data_dir
package = Package.new "#{work_dir}/control.spec"
packages[file] = package
if is_installed? package.to_atom
raise ::Package::Exception.new "Package '#{package.to_atom.to_s}' is already installed."
end
Manifest.new("#{work_dir}/manifest").tap do |manifest|
manifest.no_collisions! self
manifests[file] = manifest
end
# FIXME: Check signatures (not implemented in packages ATM).
end
files.each do |file|
info "Installing '#{file}'"
work_dir = work_dirs[file]
data_dir = "#{work_dir}/data"
package = packages[file]
manifest = manifests[file]
manifest.each do |entry|
if entry.type == "directory" && Dir.exists? entry.file
next
end
debug "++ #{entry.file}"
entry.install data_dir, @root
end
@database << {package, manifest}
FileUtils.rm_r work_dir
end
end
def install(file : String)
install [file]
end
def install(atoms : Array(Atom))
2019-11-02 20:59:08 +01:00
unchecked_atoms = Deque.new atoms
packages_to_install = Array(Tuple(Repository, Package)).new
while unchecked_atoms.size > 0
atom = unchecked_atoms.pop
tuple = get_installable atom
if tuple.nil?
2019-11-05 14:32:30 +01:00
raise Exception.new "Could not find '#{atom.to_s}'."
2019-11-02 20:59:08 +01:00
end
packages_to_install << tuple
_, package = tuple
package.dependencies.each do |atom|
unless is_installed? atom
unchecked_atoms.push atom
end
end
end
#pp! packages_to_install.map &.[1].to_atom.to_s
packages_to_install.reverse.each do |tuple|
download tuple
end
packages_to_install.reverse.each do |tuple|
repository, package = tuple
2019-11-05 14:32:30 +01:00
url = "#{repository.uri.path}/#{package.file_path}"
2019-11-02 20:59:08 +01:00
install url
end
2019-10-31 21:57:58 +01:00
end
def install(atom : Atom)
install [atom]
end
def upgrade(atom : Atom)
2019-11-02 20:59:08 +01:00
warning "(unimplemented) upgrade(Atom)"
end
def download(tuple : Tuple(Repository, Package))
repository, package = tuple
2019-11-05 14:32:30 +01:00
uri = repository.uri
2019-11-02 20:59:08 +01:00
if uri.scheme == "http" || uri.scheme == "https"
FileUtils.mkdir_p @cache_directory
2019-11-05 14:32:30 +01:00
destination = "#{get_cache_directory}/#{package.file_path}"
full_url = "#{repository.uri.to_s}/#{package.file_path}"
debug "Downloading '#{package.file_path}'"
# FIXME: Check errors, probably. :|
File.write destination, HTTP::Client.get(full_url).body
package.file_path = destination
2019-11-02 20:59:08 +01:00
end
end
def get_installable(atom : Atom)
@configuration.repositories.each do |repository|
match = repository.find &.matches?(atom)
return {repository, match} if match
end
2019-10-31 21:57:58 +01:00
end
def remove(packages : Array(Package))
# First part of this function is about extracting
# reverse-dependencies. Once we have them all, we remove
# everything one by one.
all_packages = @database.get_all_packages
packages_to_remove = [] of Package
unchecked_packages = Deque(Package).new
packages.each do |package|
unchecked_packages << package
end
while unchecked_packages.size > 0
package = unchecked_packages.pop
packages_to_remove << package
all_packages
2019-11-02 20:59:08 +01:00
.select(&.dependencies.any? do |dependency|
# FIXME: Check full atoms and not just names.
2019-10-31 21:57:58 +01:00
2019-11-02 20:59:08 +01:00
# FIXME: Checking theres no collision with world files is probably done around here.
packages_to_remove.find &.matches? dependency
end)
2019-10-31 21:57:58 +01:00
.each do |p|
if unchecked_packages.find &.==(p)
next
end
if packages_to_remove.find &.==(p)
next
end
unchecked_packages << p
end
end
packages_to_remove.reverse.each do |package|
manifest = @database.get_manifest package
2019-11-02 20:59:08 +01:00
info "Removing '#{package.to_atom.to_s}'"
2019-10-31 21:57:58 +01:00
manifest.reverse.each do |entry|
if entry.type == "directory" && (!Dir.exists?(entry.file) || !Dir.empty?(entry.file))
next
end
debug "-- #{entry.file}"
entry.remove @root
end
@database.remove package
end
end
def remove(package : Package)
remove [package]
end
def remove(atoms : Array(Atom))
remove atoms.map { |atom| @database.get_package atom }
end
def remove(atom : Atom)
remove [atom]
end
def installed_packages : Array(Package)
end
def is_installed?(atom : Atom)
@database.is_installed? atom
end
def update_repository_cache
2019-11-05 14:32:30 +01:00
@configuration.repositories.each do |repository|
repository.download_index self
end
2019-10-31 21:57:58 +01:00
end
def download_to_cache(atom : Atom, url : String)
end
def database_directory
end
def get_work_directory
directory = "/tmp/package-#{UUID.random}"
FileUtils.mkdir_p directory
directory
end
2019-11-05 14:32:30 +01:00
def get_indices_cache
directory = "#{@root}/var/cache/package"
FileUtils.mkdir_p directory
directory
end
# Should this really be the same place? :shrug:
def get_cache_directory
directory = "#{@root}/var/cache/package"
FileUtils.mkdir_p directory
directory
end
2019-10-31 21:57:58 +01:00
end