From d46c11b3a8b05aac1c4b2e75a1d9d468faa22d82 Mon Sep 17 00:00:00 2001 From: p2vman Date: Sat, 26 Apr 2025 16:14:03 +0300 Subject: [PATCH] init --- ckg.min.lua | 12 +++ packages.json | 3 + src/ckg.lua | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 271 insertions(+) create mode 100644 ckg.min.lua create mode 100644 packages.json create mode 100644 src/ckg.lua diff --git a/ckg.min.lua b/ckg.min.lua new file mode 100644 index 0000000..41c20d4 --- /dev/null +++ b/ckg.min.lua @@ -0,0 +1,12 @@ +local a={}local b="/ckg"local c=fs.combine(b,"cache")local d=fs.combine(c,"repos")local e=fs.combine(b,"config.txt")local f={"https://example.com/repo/packages.json"}local function g()if not fs.exists(e)then return{repos=f}end;local h=fs.open(e,"r")local i=textutils.unserialize(h.readAll())h.close()return i end;local function j(i)local h=fs.open(e,"w")h.write(textutils.serialize(i))h.close()end;local function k(i)if not fs.exists(d)then fs.makeDir(d)end;local l={}for m,n in ipairs(i.repos)do local o=fs.combine(d,"repo_"..m..".json")if fs.exists(o)then local h=fs.open(o,"r")local p=textutils.unserializeJSON(h.readAll())h.close()for q,r in pairs(p)do l[q]=r end else if http.checkURL(n)then print("Fetching index from "..n)local s=".pkg_tmp_repo.json"if fs.exists(s)then fs.delete(s)end;shell.run("wget",n,s)local h=fs.open(s,"r")local p=textutils.unserializeJSON(h.readAll())h.close()fs.delete(s)local t=fs.open(o,"w")t.write(textutils.serializeJSON(p))t.close()for q,r in pairs(p)do l[q]=r end else print("Warning: cannot reach repo "..n)end end end;return l end;local function u(v,w,x)for y,z in ipairs(w)do local n=v.."/"..z;local A=fs.combine(x,z)local B=fs.getDir(A)if not fs.exists(B)then fs.makeDir(B)end;if fs.exists(A)then fs.delete(A)end;shell.run("wget",n,A)end end;local function C(q,D,E)if E[q]then return end;local F=D[q]if not F then error("Package not found: "..q)end;if F.dependencies then for y,G in ipairs(F.dependencies)do C(G,D,E)end end;local x=fs.combine(b,q)if fs.exists(x)then print("Already installed: "..q)E[q]=true;return end;if not fs.exists(c)then fs.makeDir(c)end;local H=fs.combine(c,q)if fs.exists(H)then print("Installing "..q.." from cache...")fs.copy(H,x)else print("Downloading "..q.."...")fs.makeDir(H)u(F.base_url,F.files,H)fs.copy(H,x)end;print("Installed "..q)E[q]=true end;function CKG.install(q)local i=g()local D=k(i)C(q,D,{})end;function CKG.fetch(q)local i=g()local D=k(i)local F=D[q]if not F then error("Package not found: "..q)end;if not fs.exists(c)then fs.makeDir(c)end;local H=fs.combine(c,q)if fs.exists(H)then fs.delete(H)end;fs.makeDir(H)u(F.base_url,F.files,H)print("Fetched "..q.." to cache")end;function CKG.remove(q)local x=fs.combine(b,q)if fs.exists(x)then fs.delete(x)print("Removed "..q)else print("Package not installed: "..q)end end;function CKG.list()if not fs.exists(b)then print("No packages installed.")return end;local D=fs.list(b)for y,F in ipairs(D)do if F~="cache"and F~="config.txt"then print(F)end end end;function CKG.add_repo(n)local i=g()table.insert(i.repos,n)j(i)print("Repository added: "..n)end;function CKG.list_repos()local i=g()for y,n in ipairs(i.repos)do print(n)end end;function CKG.clear_repos()j({repos=f})print("Repositories reset to default")end;function CKG.clear_cache()if fs.exists(c)then fs.delete(c)print("Cache cleared")else print("Cache is already empty")end end;function CKG.update()if fs.exists(d)then fs.delete(d)end;fs.makeDir(d)local i=g()for m,n in ipairs(i.repos)do if http.checkURL(n)then print("Updating index from "..n)local s=".pkg_tmp_repo.json"if fs.exists(s)then fs.delete(s)end;shell.run("wget",n,s)local h=fs.open(s,"r")local p=textutils.unserializeJSON(h.readAll())h.close()fs.delete(s)local o=fs.combine(d,"repo_"..m..".json")local t=fs.open(o,"w")t.write(textutils.serializeJSON(p))t.close()else print("Warning: cannot reach repo "..n)end end;print("Repository indexes updated!")end;local I={...}local J=I[1]if J=="install"then CKG.install(I[2])elseif J=="remove"then CKG.remove(I[2])elseif J=="list"then CKG.list()elseif J=="fetch"then CKG.fetch(I[2])elseif J=="clear-cache"then CKG.clear_cache()elseif J=="add-repo"then CKG.add_repo(I[2])elseif J=="list-repos"then CKG.list_repos()elseif J=="clear-repos"then CKG.clear_repos()elseif J=="update"then CKG.update()else print([[ +Usage: + ckg install Install a package + ckg remove Remove a package + ckg list List installed packages + ckg fetch Download package to cache + ckg clear-cache Clear downloaded packages cache + ckg add-repo Add a new repository + ckg list-repos List repositories + ckg clear-repos Reset repositories to default + ckg update Refresh package indexes +]])end \ No newline at end of file diff --git a/packages.json b/packages.json new file mode 100644 index 0000000..544b7b4 --- /dev/null +++ b/packages.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/src/ckg.lua b/src/ckg.lua new file mode 100644 index 0000000..d59388a --- /dev/null +++ b/src/ckg.lua @@ -0,0 +1,256 @@ +--[[ + My ComputerCraft Package Manager + Supports multiple repositories, dependency resolution, index and package caching. +]] + +local PKG = {} + +local INSTALL_DIR = "/ckg" +local CACHE_DIR = fs.combine(INSTALL_DIR, "cache") +local REPO_CACHE_DIR = fs.combine(CACHE_DIR, "repos") +local CONFIG_FILE = fs.combine(INSTALL_DIR, "config.txt") +local DEFAULT_REPOS = { + "https://example.com/repo/packages.json" +} + +local function load_config() + if not fs.exists(CONFIG_FILE) then + return { repos = DEFAULT_REPOS } + end + local f = fs.open(CONFIG_FILE, "r") + local cfg = textutils.unserialize(f.readAll()) + f.close() + return cfg +end + +local function save_config(cfg) + local f = fs.open(CONFIG_FILE, "w") + f.write(textutils.serialize(cfg)) + f.close() +end + +local function fetch_package_lists(cfg) + if not fs.exists(REPO_CACHE_DIR) then fs.makeDir(REPO_CACHE_DIR) end + local all = {} + + for i, url in ipairs(cfg.repos) do + local cached_file = fs.combine(REPO_CACHE_DIR, "repo_" .. i .. ".json") + + if fs.exists(cached_file) then + local f = fs.open(cached_file, "r") + local data = textutils.unserializeJSON(f.readAll()) + f.close() + for name, info in pairs(data) do + all[name] = info + end + else + if http.checkURL(url) then + print("Fetching index from " .. url) + local tmp = ".pkg_tmp_repo.json" + if fs.exists(tmp) then fs.delete(tmp) end + shell.run("wget", url, tmp) + local f = fs.open(tmp, "r") + local data = textutils.unserializeJSON(f.readAll()) + f.close() + fs.delete(tmp) + + local cache = fs.open(cached_file, "w") + cache.write(textutils.serializeJSON(data)) + cache.close() + + for name, info in pairs(data) do + all[name] = info + end + else + print("Warning: cannot reach repo " .. url) + end + end + end + + return all +end + +local function install_files(base_url, files, install_path) + for _, file in ipairs(files) do + local url = base_url .. "/" .. file + local path = fs.combine(install_path, file) + local dir = fs.getDir(path) + if not fs.exists(dir) then fs.makeDir(dir) end + if fs.exists(path) then fs.delete(path) end + shell.run("wget", url, path) + end +end + +local function install_single(name, list, installed) + if installed[name] then return end + local pkg = list[name] + if not pkg then + error("Package not found: " .. name) + end + + if pkg.dependencies then + for _, dep in ipairs(pkg.dependencies) do + install_single(dep, list, installed) + end + end + + local install_path = fs.combine(INSTALL_DIR, name) + if fs.exists(install_path) then + print("Already installed: " .. name) + installed[name] = true + return + end + + if not fs.exists(CACHE_DIR) then fs.makeDir(CACHE_DIR) end + local cache_path = fs.combine(CACHE_DIR, name) + + if fs.exists(cache_path) then + print("Installing " .. name .. " from cache...") + fs.copy(cache_path, install_path) + else + print("Downloading " .. name .. "...") + fs.makeDir(cache_path) + install_files(pkg.base_url, pkg.files, cache_path) + fs.copy(cache_path, install_path) + end + + print("Installed " .. name) + installed[name] = true +end + +function CKG.install(name) + local cfg = load_config() + local list = fetch_package_lists(cfg) + install_single(name, list, {}) +end + +function CKG.fetch(name) + local cfg = load_config() + local list = fetch_package_lists(cfg) + local pkg = list[name] + if not pkg then + error("Package not found: " .. name) + end + if not fs.exists(CACHE_DIR) then fs.makeDir(CACHE_DIR) end + local cache_path = fs.combine(CACHE_DIR, name) + if fs.exists(cache_path) then fs.delete(cache_path) end + fs.makeDir(cache_path) + install_files(pkg.base_url, pkg.files, cache_path) + print("Fetched " .. name .. " to cache") +end + +function CKG.remove(name) + local install_path = fs.combine(INSTALL_DIR, name) + if fs.exists(install_path) then + fs.delete(install_path) + print("Removed " .. name) + else + print("Package not installed: " .. name) + end +end + +function CKG.list() + if not fs.exists(INSTALL_DIR) then + print("No packages installed.") + return + end + local list = fs.list(INSTALL_DIR) + for _, pkg in ipairs(list) do + if pkg ~= "cache" and pkg ~= "config.txt" then + print(pkg) + end + end +end + +function CKG.add_repo(url) + local cfg = load_config() + table.insert(cfg.repos, url) + save_config(cfg) + print("Repository added: " .. url) +end + +function CKG.list_repos() + local cfg = load_config() + for _, url in ipairs(cfg.repos) do + print(url) + end +end + +function CKG.clear_repos() + save_config({ repos = DEFAULT_REPOS }) + print("Repositories reset to default") +end + +function CKG.clear_cache() + if fs.exists(CACHE_DIR) then + fs.delete(CACHE_DIR) + print("Cache cleared") + else + print("Cache is already empty") + end +end + +function CKG.update() + if fs.exists(REPO_CACHE_DIR) then fs.delete(REPO_CACHE_DIR) end + fs.makeDir(REPO_CACHE_DIR) + + local cfg = load_config() + for i, url in ipairs(cfg.repos) do + if http.checkURL(url) then + print("Updating index from " .. url) + local tmp = ".pkg_tmp_repo.json" + if fs.exists(tmp) then fs.delete(tmp) end + shell.run("wget", url, tmp) + local f = fs.open(tmp, "r") + local data = textutils.unserializeJSON(f.readAll()) + f.close() + fs.delete(tmp) + + local cached_file = fs.combine(REPO_CACHE_DIR, "repo_" .. i .. ".json") + local cache = fs.open(cached_file, "w") + cache.write(textutils.serializeJSON(data)) + cache.close() + else + print("Warning: cannot reach repo " .. url) + end + end + + print("Repository indexes updated!") +end + +-- CLI interface +local args = { ... } +local cmd = args[1] + +if cmd == "install" then + CKG.install(args[2]) +elseif cmd == "remove" then + CKG.remove(args[2]) +elseif cmd == "list" then + CKG.list() +elseif cmd == "fetch" then + CKG.fetch(args[2]) +elseif cmd == "clear-cache" then + CKG.clear_cache() +elseif cmd == "add-repo" then + CKG.add_repo(args[2]) +elseif cmd == "list-repos" then + CKG.list_repos() +elseif cmd == "clear-repos" then + CKG.clear_repos() +elseif cmd == "update" then + CKG.update() +else + print([[ +Usage: + ckg install Install a package + ckg remove Remove a package + ckg list List installed packages + ckg fetch Download package to cache + ckg clear-cache Clear downloaded packages cache + ckg add-repo Add a new repository + ckg list-repos List repositories + ckg clear-repos Reset repositories to default + ckg update Refresh package indexes +]]) +end