Updated the Halyde web installer to work on read-only floppies, now with a graphical environment

This commit is contained in:
Ponali
2025-07-11 11:30:52 +02:00
parent 221bd0229e
commit 876e5160df
2 changed files with 221 additions and 74 deletions
+220 -74
View File
@@ -7,56 +7,142 @@ local internet = component.internet
local computer = require("computer")
local fs = require("filesystem")
local gpu = component.gpu
local width,height = gpu.getResolution()
local event = require("event")
local keyboard = require("keyboard")
local installLocation
local installAddress
local drives = {}
local driveAddresses = {}
for drive in fs.list("/mnt/") do
table.insert(drives, drive)
local address = component.get(drive:sub(1, 3), "filesystem")
if not component.invoke(address, "isReadOnly") then
table.insert(drives, drive)
table.insert(driveAddresses,address)
end
end
if #drives == 1 and not component.invoke(component.get(drives[1]:sub(1, 3), "filesystem"), "isReadOnly") then
installLocation = "/mnt/" .. drives[1]
elseif #drives == 1 then
io.stderr.write("All drives are read-only.\nHalyde cannot be installed.")
else
local installDrivesText = "Possible drives to install to:"
for i = 1, #drives do
local address = component.get(drives[i]:sub(1, 3), "filesystem")
local fsComponent = component.proxy(address)
if not fsComponent.isReadOnly() then
local label = fsComponent.getLabel()
if label then
installDrivesText = installDrivesText .. "\n " .. tostring(i) .. ". - " .. label .. "(" .. address:sub(1, 5) .. "...)"
else
installDrivesText = installDrivesText .. "\n " .. tostring(i) .. ". - " .. address:sub(1, 5) .. "..."
end
else
table.remove(drives, i)
i = i - 1
end
local function reset()
gpu.setBackground(0x000000)
gpu.setForeground(0xFFFFFF)
gpu.fill(1,1,width,height," ")
require("tty").setCursor(1,1)
end
local function chooseDrive()
local driveLabels = {}
for i,drive in ipairs(drives) do
local address = driveAddresses[i]
table.insert(driveLabels,component.invoke(address,"getLabel"))
end
io.write(installDrivesText .. "\nPlease select a drive by entering its number or \"q\" to quit. ")
local answer
gpu.set(1,1,"Please select a drive to install Halyde to:")
local cur = 1
local function renderItem(idx,cur)
if cur then
gpu.setBackground(0xFFFFFF)
gpu.setForeground(0x000000)
else
gpu.setBackground(0x000000)
gpu.setForeground(0xFFFFFF)
end
gpu.fill(4,2+idx,width-3,1," ")
local label = driveLabels[idx] or "No label"
gpu.set(4,2+idx,label)
gpu.set(4+#label+1,2+idx,driveAddresses[idx]:sub(1,5).."...")
end
local function moveCur(dir)
local ncur = math.max(math.min(cur+dir,#drives),1)
if cur==ncur then return end
renderItem(ncur,true)
renderItem(cur,false)
cur=ncur
end
gpu.fill(2,3,1,#drives,"*")
for i=1,#drives do
renderItem(i,cur==i)
end
while true do
answer = io.read()
if tonumber(answer) and tonumber(answer) >= 1 and tonumber(answer) <= #drives then
local args = {event.pull("key_down")}
if not args or not args[1] or not args[4] then goto continue end
local key = keyboard.keys[args[4]]
if key=="up" then
moveCur(-1)
elseif key=="down" then
moveCur(1)
elseif key=="right" or key=="enter" then
gpu.setBackground(0x000000)
gpu.setForeground(0xFFFFFF)
gpu.fill(1,1,width,height-1," ")
break
elseif answer == "q" then
return
else
print("Answer invalid, try again.")
elseif key=="left" or key=="back" then
return false
end
::continue::
end
installLocation = "/mnt/" .. drives[cur]
installAddress = driveAddresses[cur]
return true
end
if #drives == 0 then
io.stderr.write("All drives are read-only.\nHalyde cannot be installed.")
elseif #drives == 1 then
installLocation = "/mnt/" .. drives[1]
end
gpu.fill(1,1,width,height," ")
gpu.setBackground(0xFFFFFF)
gpu.setForeground(0x000000)
gpu.fill(1,height,width,1," ")
gpu.set(1,height,"Halyde Web Installer (OpenOS)")
gpu.setBackground(0x000000)
gpu.setForeground(0xFFFFFF)
if #drives>1 then
if not chooseDrive() then
reset()
return
end
installLocation = "/mnt/" .. drives[tonumber(answer)]
end
if not installLocation then
print("All drives are read-only.\nHalyde cannot be installed.")
return
end
io.write("Are you sure you would like to install Halyde to " .. installLocation .. "? This will erase all data on this disk. [Y/n] ")
if io.read():lower() == "n" then
reset()
io.stderr.write("All drives are read-only.\nHalyde cannot be installed.")
return
end
if width<80 then
gpu.set(1,1,"Are you sure you would like to install Halyde?")
else
gpu.set(1,1,"Are you sure you would like to install Halyde to "..installLocation.."?")
end
gpu.set(1,2,"This will erase all data on this disk.")
gpu.set(1,height-1,"Press Y to accept, or N to cancel.")
gpu.set(3,4,"Capacity: ")
gpu.set(3,5,"Used: ")
gpu.set(3,6,"ID: ")
gpu.set(3,7,"Label: ")
gpu.setForeground(0x00FF00)
if width>=80 then
gpu.set(50,1,installLocation)
end
gpu.set(13,4,math.floor(component.invoke(installAddress,"spaceTotal")/1024).." KiB")
gpu.set(9,5,math.floor(component.invoke(installAddress,"spaceUsed")/1024).." KiB")
gpu.set(7,6,installAddress)
gpu.set(10,7,component.invoke(installAddress,"getLabel") or "No label")
if keyboard.keys[({event.pull("key_down")})[4]]=="n" then
return reset()
end
-- installation
local computer = require("computer")
local function getFile(url)
@@ -79,42 +165,86 @@ local function getFile(url)
return data
end
local function getFile(path)
if path:sub(1,1) == "/" then
if not fs.exists(path) then
return false, "file does not exist"
end
local handle, data, tmpdata = fs.open(path, "r"), "", nil
repeat
tmpdata = handle:read(math.huge)
data = data .. (tmpdata or "")
until not tmpdata
handle:close()
return data
else
local request, data, tmpdata = nil, "", nil
local status, errorMessage = pcall(function()
request = internet.request(path)
request:finishConnect()
end)
if not status then
return false, errorMessage
end
local responseCode = request:response()
if responseCode and responseCode ~= 200 then
return false, responseCode
end
repeat
tmpdata = request.read(math.huge)
data = data .. (tmpdata or "")
until not tmpdata
return data
end
-- installation graphics
local webInstallConfig
local installationOrder = {"halyde", "edit", "argentum", "webinstall-extras"}
gpu.setBackground(0x000000)
gpu.setForeground(0xFFFFFF)
gpu.fill(1,1,width,height," ")
local function lpad(str, len, char)
str=tostring(str)
if char == nil then char = ' ' end
return string.rep(char, len - #str) .. str
end
local webInstallConfig = getFile("https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/main/argentum.cfg")
local function progress(package,progress)
local total = 0
if webInstallConfig and type(webInstallConfig)=="table" then
for i,pck in ipairs(installationOrder) do
local packConfig = webInstallConfig[pck]
-- print(pck,packConfig)
total=total+#(packConfig.directories or {})+#(packConfig.files or {})
end
else
total=1
end
local info = ""
local realProgress = 1
if type(package)=="string" then
realProgress = progress
info = string.format("%s %s%%",package,lpad(math.floor(progress*100),2))
else
realProgress = 0
for i=1,package do
local packConfig = webInstallConfig[installationOrder[i]]
if i==package then
realProgress=realProgress+progress
else
local value = #(packConfig.directories or {})+#packConfig.files
realProgress=realProgress+value
end
end
realProgress=realProgress/total
local packConfig = webInstallConfig[installationOrder[package]]
progress=progress/(#(packConfig.directories or {})+#packConfig.files)
-- realProgress = (progress+package-1)/#installationOrder
local packInfo = installationOrder[package].." "..lpad(math.floor(progress*100),2).."%"
info = string.format("%s%% [%s]",lpad(math.floor(realProgress*100),2),packInfo)
end
info=info..string.rep(" ",width-#info)
local progX = math.floor(realProgress*width)
gpu.setBackground(0x00FF00)
gpu.setForeground(0x000000)
gpu.set(1,height,info:sub(1,progX))
gpu.setBackground(0x000000)
gpu.setForeground(0xFFFFFF)
gpu.set(progX+1,height,info:sub(progX+1))
end
local logY = 1
local function log(txt)
if logY>=height then
gpu.copy(1,2,width,height-2,0,-1)
gpu.fill(1,height-1,width,1," ")
logY=logY-1
end
gpu.set(1,logY,txt)
logY=logY+1
end
----------------------------
log("Fetching Argentum configuration for Halyde")
progress("Preparing",0)
webInstallConfig = getFile("https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/main/argentum.cfg")
log("Loading Argentum configuration")
progress("Preparing",0.5)
webInstallConfig = load(webInstallConfig)
webInstallConfig = webInstallConfig()
local installationOrder = {"halyde", "edit", "argentum", "webinstall-extras"}
log("Looking for outdated files in the drive")
progress("Preparing",1)
local oldFiles = {}
for oldFile in fs.list(installLocation) do
local usedFlag = false
@@ -132,29 +262,45 @@ for oldFile in fs.list(installLocation) do
end
end
end
if oldFile=="halyde/" then usedFlag = true end
if not usedFlag then
table.insert(oldFiles, oldFile)
end
end
log("Found "..#oldFiles)
progress(1,0)
for i = 1, 4 do
local webInstallConfig = webInstallConfig[installationOrder[i]]
local dirCount = 0
if webInstallConfig.directories then
for _, directory in pairs(webInstallConfig.directories) do
print("Creating " .. directory .. "...")
dirCount=#webInstallConfig.directories
for dirIdx, directory in ipairs(webInstallConfig.directories) do
log("Creating " .. directory .. "...")
progress(i,dirIdx-1)
fs.makeDirectory(installLocation .. directory)
end
end
for _, file in pairs(webInstallConfig.files) do
print("Downloading " .. file .. "...")
for fileIdx, file in ipairs(webInstallConfig.files) do
log("Downloading " .. file .. "...")
progress(i,fileIdx-1+dirCount)
local handle = fs.open(installLocation .. file, "w")
handle:write(getFile("https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/main/" .. file))
handle:close()
end
end
for _, oldFile in pairs(oldFiles) do
fs.remove(oldFile)
for i, oldFile in ipairs(oldFiles) do
log("Removing "..oldFile)
progress("Finishing up",(i-1)/#oldFiles*1)
fs.remove(installLocation .. oldFile)
end
log("Setting boot address")
progress("Finishing up",1)
computer.setBootAddress(component.get(installLocation:sub(6, -2)))
log("Setting label to Halyde")
component.invoke(component.get(installLocation:sub(6, -2)), "setLabel", "Halyde")
gpu.fill(1,1,width,height," ")
computer.shutdown(true)