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 there’s 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
|
|
|
|
|
|