2018-01-24 22:39:38 +00:00
|
|
|
local colors = _G.colors
|
|
|
|
local fs = _G.fs
|
|
|
|
local http = _G.http
|
|
|
|
local install = _ENV.install
|
|
|
|
local os = _G.os
|
|
|
|
|
2018-01-24 23:21:07 +00:00
|
|
|
local injector
|
2018-01-24 22:39:38 +00:00
|
|
|
if not install.testing then
|
2018-03-21 17:42:29 +00:00
|
|
|
_G.OPUS_BRANCH = 'master-1.8'
|
|
|
|
local url ='https://raw.githubusercontent.com/kepler155c/opus/master-1.8/sys/apis/injector.lua'
|
2018-01-24 23:45:47 +00:00
|
|
|
injector = load(http.get(url).readAll(), 'injector.lua', nil, _ENV)()
|
2018-01-24 22:39:38 +00:00
|
|
|
else
|
2018-01-24 23:21:07 +00:00
|
|
|
injector = _G.requireInjector
|
2018-01-24 22:39:38 +00:00
|
|
|
end
|
|
|
|
|
2018-01-24 23:21:07 +00:00
|
|
|
injector(_ENV)
|
2018-01-24 22:39:38 +00:00
|
|
|
|
|
|
|
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
|