commit 4c9f07d2063becda9ce6e71821b6b105ff065236
parent 36509ffbc0a48ff70d86fd4343290b755689e5e9
Author: phoebos <ben@bvnf.space>
Date:   Tue, 18 Jul 2023 02:54:32 +0100
add install
Diffstat:
3 files changed, 129 insertions(+), 2 deletions(-)
diff --git a/bliss/init.lua b/bliss/init.lua
@@ -3,7 +3,7 @@ local cwd = (...):gsub("%.init$", "")
 local M = {}
 
 -- merge these into the toplevel bliss module
-local names = {"utils", "search", "list", "pkg", "download", "checksum", "build"}
+local names = {"utils", "search", "list", "pkg", "download", "checksum", "build", "install"}
 for _, name in ipairs(names) do
     local t = require(cwd .. "." .. name)
     for k, v in pairs(t) do
diff --git a/bliss/install.lua b/bliss/install.lua
@@ -0,0 +1,100 @@
+local archive = require "bliss.archive"
+local utils = require "bliss.utils"
+local pkg = require "bliss.pkg"
+local libgen = require "posix.libgen"
+local sys_stat = require "posix.sys.stat"
+
+local function install(env, arg)
+    if #arg == 0 then end
+
+    for _,p in ipairs(arg) do
+        local pkgname, tarfile
+        if string.match(p, "%.tar%.") then
+            -- p is a path to a tarball.
+            if not sys_stat.stat(p) then
+                utils.die("File '" .. p .. "' does not exist")
+            end
+            pkgname = string.match(p, ".*/(.-)@")
+            tarfile = p
+        else
+            local path = utils.shallowcopy(env.PATH)
+            table.insert(path, env.sys_db)
+
+            local repo_dir = pkg.find(p, path)
+            local version = pkg.find_version(p, repo_dir)
+            tarfile = pkg.iscached(env, p, version)
+            if not tarfile then
+                utils.die(p, "Not yet built")
+            end
+            pkgname = p
+        end
+
+        utils.trap_off(env)
+        utils.mkcd(env.tar_dir .. "/" .. pkgname)
+
+        archive.tar_extract(tarfile)
+        local tar_man = env.tar_dir.."/"..pkgname.."/"..env.pkg_db.."/"..pkgname.."/manifest"
+        if not sys_stat.stat(tar_man) then
+            utils.die("Not a valid KISS package (no manifest file)")
+        end
+
+        if env.FORCE ~= 1 then
+            -- check installable
+        end
+
+        -- TODO: alternatives
+
+        utils.log(pkgname, "Installing package ("..libgen.basename(tarfile)..")")
+
+        local tar_manifest = pkg.read_lines(tar_man)
+        for k,v in ipairs(tar_manifest) do tar_manifest[k] = v[1] end
+        table.sort(tar_manifest)
+
+		-- TODO: diff manifests, remove old files, verify new files
+
+        -- PWD must contain the files
+        for _, file in ipairs(tar_manifest) do
+            local _file = env.ROOT .. file
+
+            if file:sub(-1) == "/" then
+                -- Directory
+                if not sys_stat.stat(_file) then
+                    local mode = sys_stat.stat("./"..file).st_mode
+                    local c,msg = sys_stat.mkdir(_file, mode)
+                    if not c then utils.die("mkdir "..msg) end
+                end
+            else
+                if file:match("^/etc/") then
+                    -- TODO: compare checksums
+                    warn(pkgname, "saving "..file.." as "..file..".new")
+                    _file = _file .. ".new"
+                end
+
+                local dirname = libgen.dirname(_file)
+                local sb = sys_stat.stat(_file)
+                if sb and sys_stat.S_ISLNK(sb.st_mode) ~= 0 then
+                    if not utils.run("cp", {"-fP", "./"..file, dirname .. "/."}) then os.exit(false) end
+                else
+                    local _tmp_file = dirname.."/__bliss-tmp-"..pkgname.."-"..libgen.basename(file).."-"..env.PID
+
+                    if not utils.run("cp", {"-fP", "./"..file, _tmp_file}) or
+                        not utils.run("mv", {"-f", _tmp_file, _file}) then
+                        -- run pkg_clean
+                        getmetatable(env.atexit).__gc()
+
+                        utils.log(pkgname, "Failed to install package", "ERROR")
+                        utils.die(pkgname, "Filesystem now dirty, manual repair needed.")
+                    end
+                end
+            end
+        end
+
+        utils.trap_on(env)
+        utils.log(pkgname, "Installed successfully")
+    end
+end
+
+local M = {
+    install = install,
+}
+return M
diff --git a/main.lua b/main.lua
@@ -11,7 +11,7 @@ local function usage()
     bliss.log("build        Build packages")
     bliss.log("checksum     Generate checksums")
     bliss.log("download     Download sources")
-    --bliss.log("install      Install packages")
+    bliss.log("install      Install packages")
     bliss.log("list         List installed packages")
     --bliss.log("remove       Remove packages")
     bliss.log("search       Search for packages")
@@ -43,6 +43,33 @@ local function args(env, arg)
     local char = string.sub(arg[1], 1, 1)
     if arg[1] == "upgrade" then char = "U" end
 
+    if char == "i" or char == "a" or char == "r" then
+        local user = bliss.am_not_owner(env.ROOT .. "/")
+        if user then
+            local newarg = {
+                "env",
+                "LOGNAME="..user,
+                "HOME="..os.getenv("HOME"),
+                "XDG_CACHE_HOME="..(os.getenv("XDG_CACHE_HOME") or ""),
+                "KISS_COMPRESS="..env.COMPRESS,
+                "KISS_PATH="..table.concat(env.PATH, ":"),
+                "KISS_FORCE="..env.FORCE,
+                "KISS_ROOT="..env.ROOT,
+                "KISS_CHOICE="..env.CHOICE,
+                "KISS_COLOR="..env.COLOR,
+                "KISS_TMPDIR="..env.TMPDIR,
+                "KISS_PID="..env.PID,
+                "_KISS_LVL="..env._LVL,
+            }
+            table.move(arg, 0, #arg, #newarg+1, newarg)
+
+            bliss.trap_off(env)
+            bliss.as_user(env, user, newarg)
+            bliss.trap_on(env)
+            return
+        end
+    end
+
     -- shift
     table.remove(arg, 1)