Updated the Halyde web installer to work on read-only floppies, now with a graphical environment
This commit is contained in:
+220
-74
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user