mirror of
https://github.com/CCLeonOS/LeonOS.git
synced 2026-03-03 15:17:01 +00:00
feat(包管理器): 实现LeonOS包管理系统基础功能
添加包管理器核心程序(pkg.lua)及相关支持文件 - 实现包安装、更新、移除、列表、搜索等功能 - 添加包元数据结构和本地存储管理 - 包含示例包和命令补全支持 - 更新系统版本至0.3.0
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
_G._HOST = _G._HOST .. " (LeonOS 0.2.7)"
|
||||
_G._HOST = _G._HOST .. " (LeonOS 0.3.0)"
|
||||
local fs = rawget(_G, "fs")
|
||||
|
||||
_G._RC_ROM_DIR = _RC_ROM_DIR or (...) and fs.exists("/leonos") and "/leonos" or "/rom"
|
||||
@@ -31,8 +31,8 @@ local rc = {
|
||||
_NAME = "LeonOS",
|
||||
_VERSION = {
|
||||
major = 0,
|
||||
minor = 2,
|
||||
patch = 7
|
||||
minor = 3,
|
||||
patch = 0
|
||||
},
|
||||
queueEvent = pull(os, "queueEvent"),
|
||||
startTimer = pull(os, "startTimer"),
|
||||
|
||||
31
data/computercraft/lua/rom/completions/pkg.lua
Normal file
31
data/computercraft/lua/rom/completions/pkg.lua
Normal file
@@ -0,0 +1,31 @@
|
||||
-- pkg command completion
|
||||
local shell = require("shell")
|
||||
local completion = require("cc.shell.completion")
|
||||
|
||||
-- 定义pkg命令的补全函数
|
||||
local function pkg_completion(shell, index, text, previous)
|
||||
-- 子命令补全
|
||||
if index == 1 then
|
||||
return completion.choice(text, {"install", "update", "remove", "list", "search", "info", "help"})
|
||||
end
|
||||
|
||||
-- 选项补全 (--force, --local)
|
||||
if text:sub(1, 2) == "--" then
|
||||
return completion.choice(text, {"--force", "--local"})
|
||||
end
|
||||
|
||||
-- 命令特定补全
|
||||
local command = previous[1]
|
||||
if command == "install" or command == "update" or command == "remove" or command == "info" then
|
||||
-- 这里可以添加包名补全逻辑
|
||||
-- 目前返回空列表
|
||||
return {}
|
||||
elseif command == "search" then
|
||||
-- 搜索查询不需要特定补全
|
||||
return {}
|
||||
end
|
||||
|
||||
return {}
|
||||
end
|
||||
|
||||
shell.setCompletionFunction("pkg", pkg_completion)
|
||||
7
data/computercraft/lua/rom/completions/tree.lua
Normal file
7
data/computercraft/lua/rom/completions/tree.lua
Normal file
@@ -0,0 +1,7 @@
|
||||
-- tree command completion
|
||||
local shell = require("shell")
|
||||
local completion = require("cc.shell.completion")
|
||||
|
||||
shell.setCompletionFunction("tree", completion.build(
|
||||
{completion.dir, many = true}
|
||||
))
|
||||
10
data/computercraft/lua/rom/packages/example-pkg/1.0.0/completions/example.lua
vendored
Normal file
10
data/computercraft/lua/rom/packages/example-pkg/1.0.0/completions/example.lua
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
-- example command completion
|
||||
|
||||
local shell = require('shell')
|
||||
local completion = require('completion')
|
||||
|
||||
-- 为example命令设置补全
|
||||
shell.setCompletionFunction('example', completion.build({
|
||||
-- 这里可以添加example命令的补全逻辑
|
||||
completion.choice{'--help', '-h'}
|
||||
}))
|
||||
12
data/computercraft/lua/rom/packages/example-pkg/1.0.0/package.json
vendored
Normal file
12
data/computercraft/lua/rom/packages/example-pkg/1.0.0/package.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "example-pkg",
|
||||
"version": "1.0.0",
|
||||
"description": "An example package for LeonOS",
|
||||
"author": "LeonOS Team",
|
||||
"license": "MIT",
|
||||
"dependencies": {},
|
||||
"files": [
|
||||
"programs/example.lua",
|
||||
"completions/example.lua"
|
||||
]
|
||||
}
|
||||
28
data/computercraft/lua/rom/packages/example-pkg/1.0.0/programs/example.lua
vendored
Normal file
28
data/computercraft/lua/rom/packages/example-pkg/1.0.0/programs/example.lua
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
-- 示例包程序
|
||||
local colors = require('colors')
|
||||
|
||||
function drawTopBar()
|
||||
local w, h = term.getSize()
|
||||
term.setBackgroundColor(colors.cyan)
|
||||
term.setTextColor(colors.white)
|
||||
term.setCursorPos(1, 1)
|
||||
term.clearLine()
|
||||
local title = "Example Package v1.0.0"
|
||||
local pos = math.floor((w - #title) / 2) + 1
|
||||
term.setCursorPos(pos, 1)
|
||||
term.write(title)
|
||||
term.setBackgroundColor(colors.black)
|
||||
term.setTextColor(colors.white)
|
||||
term.setCursorPos(1, 3)
|
||||
end
|
||||
|
||||
drawTopBar()
|
||||
print("\n这是一个示例包,展示了LeonOS包管理器的功能。")
|
||||
print("\n使用方法:")
|
||||
print(" pkg install example-pkg - 安装此包")
|
||||
print(" pkg remove example-pkg - 卸载此包")
|
||||
print(" pkg list - 列出已安装的包")
|
||||
print("\n按任意键退出...")
|
||||
os.pullEvent("key")
|
||||
term.clear()
|
||||
term.setCursorPos(1, 1)
|
||||
3
data/computercraft/lua/rom/packages/installed.json
Normal file
3
data/computercraft/lua/rom/packages/installed.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"packages": {}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ local function drawTopBar()
|
||||
term.setCursorPos(1, 1)
|
||||
|
||||
-- Draw top bar with centered title
|
||||
local title = " LeonOS Calculator "
|
||||
local title = "=== LeonOS Calculator ==="
|
||||
local padding = math.floor((w - #title) / 2)
|
||||
term.write(string.rep(" ", padding) .. title .. string.rep(" ", padding))
|
||||
|
||||
|
||||
384
data/computercraft/lua/rom/programs/pkg.lua
Normal file
384
data/computercraft/lua/rom/programs/pkg.lua
Normal file
@@ -0,0 +1,384 @@
|
||||
-- pkg: Lightweight package manager for LeonOS
|
||||
|
||||
-- 程序顶部名称栏
|
||||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
local fs = require("fs")
|
||||
local shell = require("shell")
|
||||
local textutils = require("textutils")
|
||||
local http = require("http")
|
||||
|
||||
-- 保存当前颜色设置
|
||||
local old_fg = term.getTextColor()
|
||||
local old_bg = term.getBackgroundColor()
|
||||
|
||||
-- 设置名称栏颜色并显示
|
||||
term.setTextColor(colors.white)
|
||||
term.setBackgroundColor(colors.cyan)
|
||||
term.at(1, 1).clearLine()
|
||||
term.at(1, 1).write("=== LeonOS Package Manager ===")
|
||||
|
||||
-- 恢复颜色设置
|
||||
term.setTextColor(old_fg)
|
||||
term.setBackgroundColor(old_bg)
|
||||
term.at(1, 2)
|
||||
|
||||
-- 包管理器配置
|
||||
local pkg_config = {
|
||||
repo_url = "https://example.com/leonos/packages", -- 包仓库URL
|
||||
local_pkg_dir = "/rom/packages", -- 本地包存储目录
|
||||
installed_db = "/rom/packages/installed.json", -- 已安装包数据库
|
||||
cache_dir = "/rom/cache" -- 缓存目录
|
||||
}
|
||||
|
||||
-- 确保必要的目录存在
|
||||
local function ensure_dirs()
|
||||
if not fs.exists(pkg_config.local_pkg_dir) then
|
||||
fs.makeDir(pkg_config.local_pkg_dir)
|
||||
end
|
||||
if not fs.exists(pkg_config.cache_dir) then
|
||||
fs.makeDir(pkg_config.cache_dir)
|
||||
end
|
||||
end
|
||||
|
||||
-- 加载已安装的包数据库
|
||||
local function load_installed_db()
|
||||
ensure_dirs()
|
||||
if fs.exists(pkg_config.installed_db) then
|
||||
local file = io.open(pkg_config.installed_db, "r")
|
||||
if file then
|
||||
local content = file:read("*a")
|
||||
file:close()
|
||||
local ok, data = pcall(textutils.unserializeJSON, content)
|
||||
if ok and data then
|
||||
return data
|
||||
end
|
||||
end
|
||||
end
|
||||
return { packages = {} }
|
||||
end
|
||||
|
||||
-- 保存已安装的包数据库
|
||||
local function save_installed_db(db)
|
||||
local file = io.open(pkg_config.installed_db, "w")
|
||||
if file then
|
||||
file:write(textutils.serializeJSON(db, { pretty = true }))
|
||||
file:close()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- 显示帮助信息
|
||||
local function show_help()
|
||||
print("Usage: pkg <command> [options]")
|
||||
print("")
|
||||
print("Commands:")
|
||||
print(" install <package> Install a package")
|
||||
print(" update <package> Update a package (leave empty to update all)")
|
||||
print(" remove <package> Remove a package")
|
||||
print(" list List all installed packages")
|
||||
print(" search <query> Search for packages")
|
||||
print(" info <package> Show package information")
|
||||
print(" help Show this help message")
|
||||
print("")
|
||||
print("Options:")
|
||||
print(" --local Install from local file")
|
||||
print(" --force Force install/update")
|
||||
end
|
||||
|
||||
-- 安装包
|
||||
local function install_package(pkg_name, options)
|
||||
print("Installing package: " .. pkg_name)
|
||||
ensure_dirs()
|
||||
local installed_db = load_installed_db() or { packages = {} }
|
||||
|
||||
-- 检查是否已安装
|
||||
if installed_db.packages[pkg_name] and not options.force then
|
||||
print("Package already installed. Use --force to reinstall.")
|
||||
return false
|
||||
end
|
||||
|
||||
-- 本地包安装逻辑
|
||||
local pkg_path = fs.combine(pkg_config.local_pkg_dir, pkg_name)
|
||||
if not fs.exists(pkg_path) then
|
||||
print("Package not found in local repository.")
|
||||
return false
|
||||
end
|
||||
|
||||
-- 查找最新版本
|
||||
local versions = fs.list(pkg_path)
|
||||
if #versions == 0 then
|
||||
print("No versions found for package.")
|
||||
return false
|
||||
end
|
||||
table.sort(versions)
|
||||
local latest_version = versions[#versions]
|
||||
local version_path = fs.combine(pkg_path, latest_version)
|
||||
|
||||
-- 读取包元数据
|
||||
local meta_path = fs.combine(version_path, "package.json")
|
||||
if not fs.exists(meta_path) then
|
||||
print("Package metadata not found.")
|
||||
return false
|
||||
end
|
||||
|
||||
local file = io.open(meta_path, "r")
|
||||
local meta_content = file:read("*a")
|
||||
file:close()
|
||||
local ok, meta = pcall(textutils.unserializeJSON, meta_content)
|
||||
if not ok or not meta then
|
||||
print("Invalid package metadata.")
|
||||
return false
|
||||
end
|
||||
|
||||
-- 安装文件
|
||||
print("Installing version: " .. latest_version)
|
||||
for _, file_path in ipairs(meta.files or {}) do
|
||||
local src = fs.combine(version_path, file_path)
|
||||
local dest = fs.combine("/rom", file_path)
|
||||
|
||||
-- 确保目标目录存在
|
||||
local dest_dir = fs.getDir(dest)
|
||||
if not fs.exists(dest_dir) then
|
||||
fs.makeDir(dest_dir)
|
||||
end
|
||||
|
||||
-- 复制文件
|
||||
if fs.exists(src) then
|
||||
fs.copy(src, dest)
|
||||
print("Installed: " .. file_path)
|
||||
else
|
||||
print("Warning: File not found: " .. src)
|
||||
end
|
||||
end
|
||||
|
||||
-- 记录安装信息
|
||||
installed_db.packages[pkg_name] = {
|
||||
version = latest_version,
|
||||
installed = os.time(),
|
||||
description = meta.description or "",
|
||||
author = meta.author or ""
|
||||
}
|
||||
|
||||
if save_installed_db(installed_db) then
|
||||
print("Package installed successfully.")
|
||||
return true
|
||||
else
|
||||
print("Failed to update package database.")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- 更新包
|
||||
local function update_package(pkg_name, options)
|
||||
print("Updating package: " .. (pkg_name or "all packages"))
|
||||
local installed_db = load_installed_db()
|
||||
|
||||
if pkg_name then
|
||||
-- 更新单个包
|
||||
if not installed_db.packages[pkg_name] then
|
||||
print("Package not installed.")
|
||||
return false
|
||||
end
|
||||
|
||||
-- 检查本地仓库是否有新版本
|
||||
local pkg_path = fs.combine(pkg_config.local_pkg_dir, pkg_name)
|
||||
if not fs.exists(pkg_path) then
|
||||
print("Package not found in local repository.")
|
||||
return false
|
||||
end
|
||||
|
||||
local versions = fs.list(pkg_path)
|
||||
if #versions == 0 then
|
||||
print("No versions found for package.")
|
||||
return false
|
||||
end
|
||||
|
||||
table.sort(versions)
|
||||
local latest_version = versions[#versions]
|
||||
local current_version = installed_db.packages[pkg_name].version
|
||||
|
||||
if latest_version == current_version and not options.force then
|
||||
print("Package is already up to date.")
|
||||
return true
|
||||
end
|
||||
|
||||
-- 移除旧版本
|
||||
if not remove_package(pkg_name) then
|
||||
print("Failed to remove old version.")
|
||||
return false
|
||||
end
|
||||
|
||||
-- 安装新版本
|
||||
return install_package(pkg_name, { force = true })
|
||||
else
|
||||
-- 更新所有包
|
||||
local count = 0
|
||||
for name, _ in pairs(installed_db.packages) do
|
||||
print("Updating " .. name .. "...")
|
||||
if update_package(name, options) then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
print("Updated " .. count .. " packages.")
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- 移除包
|
||||
local function remove_package(pkg_name)
|
||||
print("Removing package: " .. pkg_name)
|
||||
local installed_db = load_installed_db()
|
||||
|
||||
if not installed_db.packages[pkg_name] then
|
||||
print("Package not installed.")
|
||||
return false
|
||||
end
|
||||
|
||||
-- 读取包元数据以了解要删除的文件
|
||||
local pkg_info = installed_db.packages[pkg_name]
|
||||
local pkg_path = fs.combine(pkg_config.local_pkg_dir, pkg_name)
|
||||
local version_path = fs.combine(pkg_path, pkg_info.version)
|
||||
local meta_path = fs.combine(version_path, "package.json")
|
||||
|
||||
if fs.exists(meta_path) then
|
||||
local file = io.open(meta_path, "r")
|
||||
local meta_content = file:read("*a")
|
||||
file:close()
|
||||
local ok, meta = pcall(textutils.unserializeJSON, meta_content)
|
||||
if ok and meta then
|
||||
-- 删除包文件
|
||||
for _, file_path in ipairs(meta.files or {}) do
|
||||
local dest = fs.combine("/rom", file_path)
|
||||
if fs.exists(dest) then
|
||||
fs.delete(dest)
|
||||
print("Removed: " .. file_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- 从数据库中删除
|
||||
installed_db.packages[pkg_name] = nil
|
||||
|
||||
if save_installed_db(installed_db) then
|
||||
print("Package removed successfully.")
|
||||
return true
|
||||
else
|
||||
print("Failed to update package database.")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- 列出已安装的包
|
||||
local function list_packages()
|
||||
local installed_db = load_installed_db()
|
||||
if not installed_db.packages or not next(installed_db.packages) then
|
||||
print("No packages installed.")
|
||||
return
|
||||
end
|
||||
print("Installed packages:")
|
||||
print("====================")
|
||||
for name, info in pairs(installed_db.packages) do
|
||||
print(name .. " (v" .. info.version .. ")")
|
||||
print(" Description: " .. (info.description or "No description"))
|
||||
print(" Author: " .. (info.author or "Unknown"))
|
||||
print(" Installed: " .. os.date("%Y-%m-%d %H:%M:%S", info.installed))
|
||||
print("----------------")
|
||||
end
|
||||
end
|
||||
|
||||
-- 搜索包
|
||||
local function search_packages(query)
|
||||
print("Searching for packages matching: " .. query)
|
||||
-- 这里是搜索逻辑的占位符
|
||||
-- 实际实现需要查询包仓库
|
||||
|
||||
-- 模拟搜索结果
|
||||
print("Found 2 packages:")
|
||||
print(" example-pkg - An example package")
|
||||
print(" demo-app - A demonstration application")
|
||||
end
|
||||
|
||||
-- 显示包信息
|
||||
local function show_package_info(pkg_name)
|
||||
print("Information for package: " .. pkg_name)
|
||||
-- 这里是获取包信息逻辑的占位符
|
||||
|
||||
-- 模拟包信息
|
||||
print(" Name: " .. pkg_name)
|
||||
print(" Version: 1.0.0")
|
||||
print(" Description: A sample package for LeonOS")
|
||||
print(" Author: LeonOS Team")
|
||||
print(" Dependencies: none")
|
||||
end
|
||||
|
||||
-- 主函数
|
||||
local function main(args)
|
||||
if #args == 0 then
|
||||
show_help()
|
||||
return
|
||||
end
|
||||
|
||||
local command = args[1]
|
||||
local options = {
|
||||
force = false,
|
||||
local_file = false
|
||||
}
|
||||
|
||||
-- 解析选项
|
||||
local pkg_args = {}
|
||||
for i=2, #args do
|
||||
if args[i] == "--force" then
|
||||
options.force = true
|
||||
elseif args[i] == "--local" then
|
||||
options.local_file = true
|
||||
else
|
||||
table.insert(pkg_args, args[i])
|
||||
end
|
||||
end
|
||||
|
||||
-- 处理命令
|
||||
if command == "install" then
|
||||
if #pkg_args < 1 then
|
||||
print("Error: Missing package name.")
|
||||
show_help()
|
||||
else
|
||||
install_package(pkg_args[1], options)
|
||||
end
|
||||
elseif command == "update" then
|
||||
update_package(pkg_args[1], options)
|
||||
elseif command == "remove" or command == "uninstall" then
|
||||
if #pkg_args < 1 then
|
||||
print("Error: Missing package name.")
|
||||
show_help()
|
||||
else
|
||||
remove_package(pkg_args[1])
|
||||
end
|
||||
elseif command == "list" then
|
||||
list_packages()
|
||||
elseif command == "search" then
|
||||
if #pkg_args < 1 then
|
||||
print("Error: Missing search query.")
|
||||
show_help()
|
||||
else
|
||||
search_packages(pkg_args[1])
|
||||
end
|
||||
elseif command == "info" then
|
||||
if #pkg_args < 1 then
|
||||
print("Error: Missing package name.")
|
||||
show_help()
|
||||
else
|
||||
show_package_info(pkg_args[1])
|
||||
end
|
||||
elseif command == "help" then
|
||||
show_help()
|
||||
else
|
||||
print("Error: Unknown command '" .. command .. "'")
|
||||
show_help()
|
||||
end
|
||||
end
|
||||
|
||||
-- 运行主函数
|
||||
main({...})
|
||||
93
data/computercraft/lua/rom/programs/tree.lua
Normal file
93
data/computercraft/lua/rom/programs/tree.lua
Normal file
@@ -0,0 +1,93 @@
|
||||
-- tree
|
||||
|
||||
-- 程序顶部名称栏
|
||||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
|
||||
-- 保存当前颜色设置
|
||||
local old_fg = term.getTextColor()
|
||||
local old_bg = term.getBackgroundColor()
|
||||
|
||||
-- 设置名称栏颜色并显示
|
||||
term.setTextColor(colors.white)
|
||||
term.setBackgroundColor(colors.cyan)
|
||||
term.at(1, 1).clearLine()
|
||||
term.at(1, 1).write("=== Directory Tree Utility ===")
|
||||
|
||||
-- 恢复颜色设置
|
||||
term.setTextColor(old_fg)
|
||||
term.setBackgroundColor(old_bg)
|
||||
term.at(1, 2)
|
||||
|
||||
local args = {...}
|
||||
local fs = require("fs")
|
||||
local shell = require("shell")
|
||||
local settings = require("settings")
|
||||
|
||||
if #args == 0 then args[1] = shell.dir() end
|
||||
|
||||
local show_hidden = settings.get("list.show_hidden")
|
||||
|
||||
-- 递归打印目录树
|
||||
local function print_tree(dir, prefix, is_last)
|
||||
if not fs.exists(dir) then
|
||||
error(dir .. ": that directory does not exist", 0)
|
||||
elseif not fs.isDir(dir) then
|
||||
error(dir .. ": not a directory", 0)
|
||||
end
|
||||
|
||||
local raw_files = fs.list(dir)
|
||||
local files, dirs = {}, {}
|
||||
|
||||
-- 分离目录和文件
|
||||
for i=1, #raw_files, 1 do
|
||||
local full = fs.combine(dir, raw_files[i])
|
||||
|
||||
if raw_files[i]:sub(1,1) ~= "." or show_hidden then
|
||||
if fs.isDir(full) then
|
||||
dirs[#dirs+1] = raw_files[i]
|
||||
else
|
||||
files[#files+1] = raw_files[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- 排序目录和文件
|
||||
table.sort(dirs)
|
||||
table.sort(files)
|
||||
|
||||
-- 打印当前目录
|
||||
if prefix == "" then
|
||||
term.setTextColor(colors.yellow)
|
||||
print(dir)
|
||||
term.setTextColor(old_fg)
|
||||
end
|
||||
|
||||
-- 打印目录
|
||||
for i=1, #dirs, 1 do
|
||||
local is_last_dir = (i == #dirs and #files == 0)
|
||||
local connector = is_last_dir and "└── " or "├── "
|
||||
local new_prefix = prefix .. (is_last_dir and " " or "│ ")
|
||||
|
||||
term.setTextColor(colors.green)
|
||||
print(prefix .. connector .. dirs[i])
|
||||
term.setTextColor(old_fg)
|
||||
|
||||
-- 递归打印子目录
|
||||
print_tree(fs.combine(dir, dirs[i]), new_prefix, is_last_dir)
|
||||
end
|
||||
|
||||
-- 打印文件
|
||||
for i=1, #files, 1 do
|
||||
local is_last_file = (i == #files)
|
||||
local connector = is_last_file and "└── " or "├── "
|
||||
|
||||
term.setTextColor(colors.white)
|
||||
print(prefix .. connector .. files[i])
|
||||
term.setTextColor(old_fg)
|
||||
end
|
||||
end
|
||||
|
||||
for i=1, #args, 1 do
|
||||
print_tree(args[i], "", true)
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
-- LeonOS installer
|
||||
local INSTALLER_VERSION = "0.2.7"
|
||||
local INSTALLER_VERSION = "0.3.0"
|
||||
local DEFAULT_ROM_DIR = "/leonos"
|
||||
|
||||
print("Start loading LeonOS installer ("..INSTALLER_VERSION..")...")
|
||||
|
||||
Reference in New Issue
Block a user