mirror of
https://github.com/kepler155c/opus
synced 2025-12-16 13:18:06 +00:00
format and installer branches
This commit is contained in:
456
sys/apps/Installer.lua
Normal file
456
sys/apps/Installer.lua
Normal file
@@ -0,0 +1,456 @@
|
||||
local colors = _G.colors
|
||||
local fs = _G.fs
|
||||
local http = _G.http
|
||||
local install = _ENV.install
|
||||
local os = _G.os
|
||||
|
||||
local requireInjector
|
||||
if not install.testing then
|
||||
local url ='https://raw.githubusercontent.com/kepler155c/opus/master/sys/apis/injector.lua'
|
||||
requireInjector = load(http.get(url).readAll())()
|
||||
else
|
||||
requireInjector = _G.requireInjector
|
||||
end
|
||||
|
||||
requireInjector(_ENV)
|
||||
|
||||
if not install.testing then
|
||||
if package then
|
||||
for _ = 1, 4 do
|
||||
table.remove(package.loaders, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local Git = require('git')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
local currentFile = ''
|
||||
local currentProgress = 0
|
||||
local cancelEvent
|
||||
|
||||
local args = { ... }
|
||||
local steps = install.steps[args[1] or 'install']
|
||||
|
||||
if not steps then
|
||||
error('Invalid install type')
|
||||
end
|
||||
|
||||
local mode = steps[#steps]
|
||||
|
||||
if UI.term.width < 32 then
|
||||
cancelEvent = 'quit'
|
||||
end
|
||||
|
||||
local page = UI.Page {
|
||||
backgroundColor = colors.cyan,
|
||||
titleBar = UI.TitleBar {
|
||||
event = cancelEvent,
|
||||
},
|
||||
wizard = UI.Wizard {
|
||||
y = 2, ey = -2,
|
||||
},
|
||||
notification = UI.Notification(),
|
||||
accelerators = {
|
||||
q = 'quit',
|
||||
},
|
||||
}
|
||||
|
||||
local pages = {
|
||||
splash = UI.Viewport { },
|
||||
review = UI.Viewport { },
|
||||
license = UI.Viewport {
|
||||
backgroundColor = colors.black,
|
||||
},
|
||||
branch = UI.Window {
|
||||
grid = UI.ScrollingGrid {
|
||||
ey = -3,
|
||||
columns = {
|
||||
{ heading = 'Branch', key = 'branch' },
|
||||
{ heading = 'Description', key = 'description' },
|
||||
},
|
||||
values = install.branches,
|
||||
autospace = true,
|
||||
},
|
||||
},
|
||||
files = UI.Window {
|
||||
grid = UI.ScrollingGrid {
|
||||
ey = -3,
|
||||
columns = {
|
||||
{ heading = 'Files', key = 'file' },
|
||||
},
|
||||
sortColumn = 'file',
|
||||
},
|
||||
},
|
||||
install = UI.Window {
|
||||
progressBar = UI.ProgressBar {
|
||||
y = -1,
|
||||
},
|
||||
},
|
||||
uninstall = UI.Window {
|
||||
progressBar = UI.ProgressBar {
|
||||
y = -1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local function getFileList()
|
||||
if install.gitRepo then
|
||||
local gitFiles = Git.list(string.format('%s/%s', install.gitRepo, install.gitBranch or 'master'))
|
||||
install.files = { }
|
||||
install.diskspace = 0
|
||||
for path, entry in pairs(gitFiles) do
|
||||
install.files[path] = entry.url
|
||||
install.diskspace = install.diskspace + entry.size
|
||||
end
|
||||
end
|
||||
|
||||
if not install.files or Util.empty(install.files) then
|
||||
error('File list is missing or empty')
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Splash ]]--
|
||||
function pages.splash:enable()
|
||||
page.titleBar.title = 'Installer v1.0'
|
||||
UI.Viewport.enable(self)
|
||||
end
|
||||
|
||||
function pages.splash:draw()
|
||||
self:clear()
|
||||
self:setCursorPos(1, 1)
|
||||
self:print(
|
||||
string.format('%s v%s\n', install.title, install.version), nil, colors.yellow)
|
||||
self:print(
|
||||
string.format('By: %s\n\n%s\n', install.author, install.description))
|
||||
|
||||
self.ymax = self.cursorY
|
||||
end
|
||||
|
||||
--[[ License ]]--
|
||||
function pages.license:enable()
|
||||
page.titleBar.title = 'License Review'
|
||||
page.wizard.nextButton.text = 'Accept'
|
||||
UI.Viewport.enable(self)
|
||||
end
|
||||
|
||||
function pages.license:draw()
|
||||
self:clear()
|
||||
self:setCursorPos(1, 1)
|
||||
self:print(
|
||||
string.format('Copyright (c) %s %s\n\n', install.copyrightYear,
|
||||
install.copyrightHolders),
|
||||
nil, colors.yellow)
|
||||
self:print(install.license)
|
||||
|
||||
self.ymax = self.cursorY + 1
|
||||
end
|
||||
|
||||
--[[ Review ]]--
|
||||
function pages.review:enable()
|
||||
if mode == 'uninstall' then
|
||||
page.nextButton.text = 'Remove'
|
||||
page.titleBar.title = 'Remove Installed Files'
|
||||
else
|
||||
page.wizard.nextButton.text = 'Begin'
|
||||
page.titleBar.title = 'Download and Install'
|
||||
end
|
||||
UI.Viewport.enable(self)
|
||||
end
|
||||
|
||||
function pages.review:draw()
|
||||
self:clear()
|
||||
self:setCursorPos(1, 1)
|
||||
|
||||
local text = 'Ready to begin installation.\n\nProceeding will download and install the files to the hard drive.'
|
||||
if mode == 'uninstall' then
|
||||
text = 'Ready to begin.\n\nProceeding will remove the files previously installed.'
|
||||
end
|
||||
self:print(text)
|
||||
|
||||
self.ymax = self.cursorY + 1
|
||||
end
|
||||
|
||||
--[[ Files ]]--
|
||||
function pages.files:enable()
|
||||
page.titleBar.title = 'Review Files'
|
||||
self.grid.values = { }
|
||||
for k,v in pairs(install.files) do
|
||||
table.insert(self.grid.values, { file = k, code = v })
|
||||
end
|
||||
self.grid:update()
|
||||
UI.Window.enable(self)
|
||||
end
|
||||
|
||||
function pages.files:draw()
|
||||
self:clear()
|
||||
|
||||
local function formatSize(size)
|
||||
if size >= 1000000 then
|
||||
return string.format('%dM', math.floor(size/1000000, 2))
|
||||
elseif size >= 1000 then
|
||||
return string.format('%dK', math.floor(size/1000, 2))
|
||||
end
|
||||
return size
|
||||
end
|
||||
|
||||
if install.diskspace then
|
||||
|
||||
local bg = self.backgroundColor
|
||||
|
||||
local diskFree = fs.getFreeSpace('/')
|
||||
if install.diskspace > diskFree then
|
||||
bg = colors.red
|
||||
end
|
||||
|
||||
local text = string.format('Space Required: %s, Free: %s',
|
||||
formatSize(install.diskspace), formatSize(diskFree))
|
||||
|
||||
if #text > self.width then
|
||||
text = string.format('Space: %s Free: %s',
|
||||
formatSize(install.diskspace), formatSize(diskFree))
|
||||
end
|
||||
|
||||
self:write(1, self.height, Util.widthify(text, self.width), bg)
|
||||
end
|
||||
self.grid:draw()
|
||||
end
|
||||
|
||||
--[[
|
||||
function pages.files:view(url)
|
||||
local s, m = pcall(function()
|
||||
page.notification:info('Downloading')
|
||||
page:sync()
|
||||
Util.download(url, '/.source')
|
||||
end)
|
||||
page.notification:disable()
|
||||
if s then
|
||||
shell.run('edit /.source')
|
||||
fs.delete('/.source')
|
||||
page:draw()
|
||||
page.notification:cancel()
|
||||
else
|
||||
page.notification:error(m:gsub('.*: (.*)', '%1'))
|
||||
end
|
||||
end
|
||||
|
||||
function pages.files:eventHandler(event)
|
||||
if event.type == 'grid_select' then
|
||||
self:view(event.selected.code)
|
||||
return true
|
||||
end
|
||||
end
|
||||
--]]
|
||||
|
||||
local function drawCommon(self)
|
||||
if currentFile then
|
||||
self:write(1, 3, 'File:')
|
||||
self:write(1, 4, Util.widthify(currentFile, self.width))
|
||||
else
|
||||
self:write(1, 3, 'Finished')
|
||||
end
|
||||
if self.failed then
|
||||
self:write(1, 5, Util.widthify(self.failed, self.width), colors.red)
|
||||
end
|
||||
self:write(1, self.height - 1, 'Progress')
|
||||
|
||||
self.progressBar.value = currentProgress
|
||||
self.progressBar:draw()
|
||||
self:sync()
|
||||
end
|
||||
|
||||
--[[ Branch ]]--
|
||||
function pages.branch:enable()
|
||||
page.titleBar.title = 'Select Branch'
|
||||
UI.Window.enable(self)
|
||||
end
|
||||
|
||||
function pages.branch:eventHandler(event)
|
||||
-- user is navigating to next view (not previous)
|
||||
if event.type == 'enable_view' and event.next then
|
||||
install.gitBranch = self.grid:getSelected().branch
|
||||
getFileList()
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Install ]]--
|
||||
function pages.install:enable()
|
||||
page.wizard.cancelButton:disable()
|
||||
page.wizard.previousButton:disable()
|
||||
page.wizard.nextButton:disable()
|
||||
|
||||
page.titleBar.title = 'Installing...'
|
||||
page.titleBar.event = nil
|
||||
|
||||
UI.Window.enable(self)
|
||||
|
||||
page:draw()
|
||||
page:sync()
|
||||
|
||||
local i = 0
|
||||
local numFiles = Util.size(install.files)
|
||||
for filename,url in pairs(install.files) do
|
||||
currentFile = filename
|
||||
currentProgress = i / numFiles * 100
|
||||
self:draw(self)
|
||||
self:sync()
|
||||
local s, m = pcall(function()
|
||||
Util.download(url, fs.combine(install.directory or '', filename))
|
||||
end)
|
||||
if not s then
|
||||
self.failed = m:gsub('.*: (.*)', '%1')
|
||||
break
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if not self.failed then
|
||||
currentProgress = 100
|
||||
currentFile = nil
|
||||
|
||||
if install.postInstall then
|
||||
local s, m = pcall(function() install.postInstall(page, UI) end)
|
||||
if not s then
|
||||
self.failed = m:gsub('.*: (.*)', '%1')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
page.wizard.nextButton.text = 'Exit'
|
||||
page.wizard.nextButton.event = 'quit'
|
||||
if not self.failed and install.rebootAfter then
|
||||
page.wizard.nextButton.text = 'Reboot'
|
||||
page.wizard.nextButton.event = 'reboot'
|
||||
end
|
||||
|
||||
page.wizard.nextButton:enable()
|
||||
page:draw()
|
||||
page:sync()
|
||||
|
||||
if not self.failed and Util.key(args, 'automatic') then
|
||||
if install.rebootAfter then
|
||||
os.reboot()
|
||||
else
|
||||
UI:exitPullEvents()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pages.install:draw()
|
||||
self:clear()
|
||||
local text = 'The files are being installed'
|
||||
if #text > self.width then
|
||||
text = 'Installing files'
|
||||
end
|
||||
self:write(1, 1, text, nil, colors.yellow)
|
||||
|
||||
drawCommon(self)
|
||||
end
|
||||
|
||||
--[[ Uninstall ]]--
|
||||
function pages.uninstall:enable()
|
||||
page.wizard.cancelButton:disable()
|
||||
page.wizard.previousButton:disable()
|
||||
page.wizard.nextButton:disable()
|
||||
|
||||
page.titleBar.title = 'Uninstalling...'
|
||||
page.titleBar.event = nil
|
||||
|
||||
page:draw()
|
||||
page:sync()
|
||||
|
||||
UI.Window.enable(self)
|
||||
|
||||
local function pruneDir(dir)
|
||||
if #dir > 0 then
|
||||
if fs.exists(dir) then
|
||||
local files = fs.list(dir)
|
||||
if #files == 0 then
|
||||
fs.delete(dir)
|
||||
pruneDir(fs.getDir(dir))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local i = 0
|
||||
local numFiles = Util.size(install.files)
|
||||
for k in pairs(install.files) do
|
||||
currentFile = k
|
||||
currentProgress = i / numFiles * 100
|
||||
self:draw()
|
||||
self:sync()
|
||||
fs.delete(k)
|
||||
pruneDir(fs.getDir(k))
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
currentProgress = 100
|
||||
currentFile = nil
|
||||
|
||||
page.wizard.nextButton.text = 'Exit'
|
||||
page.wizard.nextButton.event = 'quit'
|
||||
page.wizard.nextButton:enable()
|
||||
|
||||
page:draw()
|
||||
page:sync()
|
||||
end
|
||||
|
||||
function pages.uninstall:draw()
|
||||
self:clear()
|
||||
self:write(1, 1, 'Uninstalling files', nil, colors.yellow)
|
||||
drawCommon(self)
|
||||
end
|
||||
|
||||
function page:eventHandler(event)
|
||||
if event.type == 'cancel' then
|
||||
UI:exitPullEvents()
|
||||
|
||||
elseif event.type == 'reboot' then
|
||||
os.reboot()
|
||||
|
||||
elseif event.type == 'quit' then
|
||||
UI:exitPullEvents()
|
||||
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function page:enable()
|
||||
UI.Page.enable(self)
|
||||
self:setFocus(page.wizard.nextButton)
|
||||
if UI.term.width < 32 then
|
||||
page.wizard.cancelButton:disable()
|
||||
page.wizard.previousButton.x = 2
|
||||
end
|
||||
end
|
||||
|
||||
getFileList()
|
||||
|
||||
local wizardPages = { }
|
||||
for k,v in ipairs(steps) do
|
||||
if not pages[v] then
|
||||
error('Invalid step: ' .. v)
|
||||
end
|
||||
wizardPages[k] = pages[v]
|
||||
wizardPages[k].index = k
|
||||
wizardPages[k].x = 2
|
||||
wizardPages[k].y = 2
|
||||
wizardPages[k].ey = -3
|
||||
wizardPages[k].ex = -2
|
||||
end
|
||||
page.wizard:add(wizardPages)
|
||||
|
||||
if Util.key(steps, 'install') and install.preInstall then
|
||||
install.preInstall(page, UI)
|
||||
end
|
||||
|
||||
UI:setPage(page)
|
||||
local s, m = pcall(function() UI:pullEvents() end)
|
||||
if not s then
|
||||
UI.term:reset()
|
||||
_G.printError(m)
|
||||
end
|
||||
Reference in New Issue
Block a user