2023-02-19 17:54:02 +00:00
--[[
2023-08-22 02:18:25 +00:00
CC - MEK - SCADA Installer Utility
2024-02-19 02:40:25 +00:00
Copyright ( c ) 2023 - 2024 Mikayla Fischler
2023-02-19 17:54:02 +00:00
Permission is hereby granted , free of charge , to any person obtaining a copy of this software and
2023-04-06 16:48:54 +00:00
associated documentation files ( the " Software " ) , to deal in the Software without restriction ,
2023-02-19 17:54:02 +00:00
including without limitation the rights to use , copy , modify , merge , publish , distribute , sublicense ,
and / or sell copies of the Software , and to permit persons to whom the Software is furnished to do so .
2023-04-06 16:48:54 +00:00
THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR IMPLIED , INCLUDING BUT NOT
2023-02-19 17:54:02 +00:00
LIMITED TO THE WARRANTIES OF MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY ,
WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
] ] --
2024-08-12 02:11:57 +00:00
local CCMSI_VERSION = " v1.17 "
2023-02-19 17:54:02 +00:00
local install_dir = " /.install-cache "
2023-05-28 03:50:00 +00:00
local manifest_path = " https://mikaylafischler.github.io/cc-mek-scada/manifests/ "
2023-02-20 01:17:03 +00:00
local repo_path = " http://raw.githubusercontent.com/MikaylaFischler/cc-mek-scada/ "
2023-02-19 17:54:02 +00:00
2024-08-12 02:11:57 +00:00
---@diagnostic disable-next-line: undefined-global
local _is_pkt_env = pocket -- luacheck: ignore pocket
local function println ( msg ) print ( tostring ( msg ) ) end
-- stripped down & modified copy of log.dmesg
local function print ( msg )
msg = tostring ( msg )
local cur_x , cur_y = term.getCursorPos ( )
local out_w , out_h = term.getSize ( )
-- jump to next line if needed
if cur_x == out_w then
cur_x = 1
if cur_y == out_h then
term.scroll ( 1 )
term.setCursorPos ( 1 , cur_y )
else
term.setCursorPos ( 1 , cur_y + 1 )
end
end
-- wrap
local lines , remaining , s_start , s_end , ln = { } , true , 1 , out_w + 1 - cur_x , 1
while remaining do
local line = string.sub ( msg , s_start , s_end )
if line == " " then
remaining = false
else
lines [ ln ] = line
s_start = s_end + 1
s_end = s_end + out_w
ln = ln + 1
end
end
-- print
for i = 1 , # lines do
cur_x , cur_y = term.getCursorPos ( )
if i > 1 and cur_x > 1 then
if cur_y == out_h then
term.scroll ( 1 )
term.setCursorPos ( 1 , cur_y )
else term.setCursorPos ( 1 , cur_y + 1 ) end
end
term.write ( lines [ i ] )
end
end
2023-02-19 17:54:02 +00:00
local opts = { ... }
2023-06-27 23:05:51 +00:00
local mode , app , target
2024-02-19 19:18:23 +00:00
local install_manifest = manifest_path .. " main/install_manifest.json "
2023-06-25 21:53:02 +00:00
local function red ( ) term.setTextColor ( colors.red ) end
local function orange ( ) term.setTextColor ( colors.orange ) end
local function yellow ( ) term.setTextColor ( colors.yellow ) end
local function green ( ) term.setTextColor ( colors.green ) end
2023-11-04 16:49:54 +00:00
local function cyan ( ) term.setTextColor ( colors.cyan ) end
2023-06-25 21:53:02 +00:00
local function blue ( ) term.setTextColor ( colors.blue ) end
local function white ( ) term.setTextColor ( colors.white ) end
local function lgray ( ) term.setTextColor ( colors.lightGray ) end
-- get command line option in list
local function get_opt ( opt , options )
for _ , v in pairs ( options ) do if opt == v then return v end end
return nil
2023-02-20 00:14:47 +00:00
end
2023-07-17 01:21:33 +00:00
-- wait for any key to be pressed
---@diagnostic disable-next-line: undefined-field
local function any_key ( ) os.pullEvent ( " key_up " ) end
2023-06-18 04:40:01 +00:00
-- ask the user yes or no
local function ask_y_n ( question , default )
print ( question )
2023-06-25 21:53:02 +00:00
if default == true then print ( " (Y/n)? " ) else print ( " (y/N)? " ) end
2023-07-17 01:21:33 +00:00
local response = read ( ) ; any_key ( )
2023-06-25 21:53:02 +00:00
if response == " " then return default
elseif response == " Y " or response == " y " then return true
elseif response == " N " or response == " n " then return false
else return nil end
2023-06-18 04:40:01 +00:00
end
2023-06-25 21:53:02 +00:00
-- print out a white + blue text message
2024-02-19 19:18:23 +00:00
local function pkg_message ( message , package ) white ( ) ; print ( message .. " " ) ; blue ( ) ; println ( package ) ; white ( ) end
2023-06-18 04:40:01 +00:00
-- indicate actions to be taken based on package differences for installs/updates
2023-07-31 00:46:04 +00:00
local function show_pkg_change ( name , v )
if v.v_local ~= nil then
if v.v_local ~= v.v_remote then
2024-02-19 19:18:23 +00:00
print ( " [ " .. name .. " ] updating " ) ; blue ( ) ; print ( v.v_local ) ; white ( ) ; print ( " \xbb " ) ; blue ( ) ; println ( v.v_remote ) ; white ( )
2023-06-18 04:40:01 +00:00
elseif mode == " install " then
2024-02-19 19:18:23 +00:00
pkg_message ( " [ " .. name .. " ] reinstalling " , v.v_local )
2023-06-18 04:40:01 +00:00
end
2024-02-19 19:18:23 +00:00
else pkg_message ( " [ " .. name .. " ] new install of " , v.v_remote ) end
2023-07-31 00:46:04 +00:00
return v.v_local ~= v.v_remote
2023-06-18 04:40:01 +00:00
end
2023-06-25 21:53:02 +00:00
-- read the local manifest file
local function read_local_manifest ( )
local local_ok = false
local local_manifest = { }
local imfile = fs.open ( " install_manifest.json " , " r " )
if imfile ~= nil then
local_ok , local_manifest = pcall ( function ( ) return textutils.unserializeJSON ( imfile.readAll ( ) ) end )
imfile.close ( )
end
return local_ok , local_manifest
end
2023-06-27 23:05:51 +00:00
-- get the manifest from GitHub
2023-06-25 21:53:02 +00:00
local function get_remote_manifest ( )
local response , error = http.get ( install_manifest )
if response == nil then
2023-08-22 02:18:25 +00:00
orange ( ) ; println ( " Failed to get installation manifest from GitHub, cannot update or install. " )
2024-02-19 19:18:23 +00:00
red ( ) ; println ( " HTTP error: " .. error ) ; white ( )
2023-06-25 21:53:02 +00:00
return false , { }
end
local ok , manifest = pcall ( function ( ) return textutils.unserializeJSON ( response.readAll ( ) ) end )
2023-07-16 23:42:20 +00:00
if not ok then red ( ) ; println ( " error parsing remote installation manifest " ) ; white ( ) end
2023-06-25 21:53:02 +00:00
return ok , manifest
end
-- record the local installation manifest
local function write_install_manifest ( manifest , dependencies )
local versions = { }
for key , value in pairs ( manifest.versions ) do
local is_dependency = false
for _ , dependency in pairs ( dependencies ) do
if ( key == " bootloader " and dependency == " system " ) or key == dependency then
2023-07-16 23:42:20 +00:00
is_dependency = true ; break
2023-06-25 21:53:02 +00:00
end
end
if key == app or key == " comms " or is_dependency then versions [ key ] = value end
end
manifest.versions = versions
local imfile = fs.open ( " install_manifest.json " , " w " )
imfile.write ( textutils.serializeJSON ( manifest ) )
imfile.close ( )
end
2024-07-03 15:01:43 +00:00
-- try at most 3 times to download a file from the repository and write into w_path base directory
local function http_get_file ( file , w_path )
2024-07-03 02:07:12 +00:00
local dl , err
for i = 1 , 3 do
dl , err = http.get ( repo_path .. file )
2024-07-03 15:01:43 +00:00
if dl then
if i > 1 then green ( ) ; println ( " success! " ) ; lgray ( ) end
local f = fs.open ( w_path .. file , " w " )
f.write ( dl.readAll ( ) )
f.close ( )
break
2024-07-03 02:07:12 +00:00
else
2024-07-04 01:14:39 +00:00
red ( ) ; println ( " HTTP Error: " .. err )
2024-07-03 15:01:43 +00:00
if i < 3 then lgray ( ) ; print ( " > retrying... " ) end
2024-07-03 02:07:12 +00:00
---@diagnostic disable-next-line: undefined-field
2024-07-03 15:01:43 +00:00
os.sleep ( i / 3.0 )
2024-07-03 02:07:12 +00:00
end
end
2024-07-03 15:01:43 +00:00
return dl ~= nil
2024-07-03 02:07:12 +00:00
end
2023-07-16 23:42:20 +00:00
-- recursively build a tree out of the file manifest
2024-06-30 02:39:58 +00:00
local function gen_tree ( manifest , log )
2023-07-16 23:42:20 +00:00
local function _tree_add ( tree , split )
if # split > 1 then
local name = table.remove ( split , 1 )
if tree [ name ] == nil then tree [ name ] = { } end
table.insert ( tree [ name ] , _tree_add ( tree [ name ] , split ) )
else return split [ 1 ] end
return nil
end
2024-06-30 02:39:58 +00:00
local list , tree = { log } , { }
2023-07-16 23:42:20 +00:00
-- make a list of each and every file
for _ , files in pairs ( manifest.files ) do for i = 1 , # files do table.insert ( list , files [ i ] ) end end
for i = 1 , # list do
local split = { }
2024-06-29 18:30:32 +00:00
---@diagnostic disable-next-line: discard-returns
2023-07-16 23:42:20 +00:00
string.gsub ( list [ i ] , " ([^/]+) " , function ( c ) split [ # split + 1 ] = c end )
if # split == 1 then table.insert ( tree , list [ i ] )
else table.insert ( tree , _tree_add ( tree , split ) ) end
end
return tree
end
local function _in_array ( val , array )
for _ , v in pairs ( array ) do if v == val then return true end end
return false
end
local function _clean_dir ( dir , tree )
2023-07-17 00:58:34 +00:00
if tree == nil then tree = { } end
2023-07-16 23:42:20 +00:00
local ls = fs.list ( dir )
for _ , val in pairs ( ls ) do
2024-02-19 19:18:23 +00:00
local path = dir .. " / " .. val
2023-07-16 23:42:20 +00:00
if fs.isDir ( path ) then
_clean_dir ( path , tree [ val ] )
2024-02-19 19:18:23 +00:00
if # fs.list ( path ) == 0 then fs.delete ( path ) ; println ( " deleted " .. path ) end
2024-06-30 16:33:52 +00:00
elseif ( not _in_array ( val , tree ) ) and ( val ~= " config.lua " ) then ---@todo remove config.lua on full release
2023-07-17 01:07:37 +00:00
fs.delete ( path )
2024-02-19 19:18:23 +00:00
println ( " deleted " .. path )
2023-07-16 23:42:20 +00:00
end
end
end
-- go through app/common directories to delete unused files
local function clean ( manifest )
2024-06-30 02:39:58 +00:00
local log = nil
if fs.exists ( app .. " .settings " ) and settings.load ( app .. " .settings " ) then
log = settings.get ( " LogPath " )
2024-07-04 01:14:39 +00:00
if log : sub ( 1 , 1 ) == " / " then log = log : sub ( 2 ) end
2024-06-30 02:39:58 +00:00
end
local tree = gen_tree ( manifest , log )
2023-07-17 01:07:37 +00:00
2023-07-16 23:42:20 +00:00
table.insert ( tree , " install_manifest.json " )
table.insert ( tree , " ccmsi.lua " )
2023-07-17 01:07:37 +00:00
2023-07-16 23:42:20 +00:00
local ls = fs.list ( " / " )
for _ , val in pairs ( ls ) do
2023-12-18 01:10:11 +00:00
if fs.isDriveRoot ( val ) then
2024-02-19 19:18:23 +00:00
yellow ( ) ; println ( " skipped mount ' " .. val .. " ' " )
2023-12-18 01:10:11 +00:00
elseif fs.isDir ( val ) then
2024-02-19 19:18:23 +00:00
if tree [ val ] ~= nil then lgray ( ) ; _clean_dir ( " / " .. val , tree [ val ] )
else white ( ) ; if ask_y_n ( " delete the unused directory ' " .. val .. " ' " ) then lgray ( ) ; _clean_dir ( " / " .. val ) end end
if # fs.list ( val ) == 0 then fs.delete ( val ) ; lgray ( ) ; println ( " deleted empty directory ' " .. val .. " ' " ) end
2023-10-04 03:39:14 +00:00
elseif not _in_array ( val , tree ) and ( string.find ( val , " .settings " ) == nil ) then
2024-02-19 19:18:23 +00:00
white ( ) ; if ask_y_n ( " delete the unused file ' " .. val .. " ' " ) then fs.delete ( val ) ; lgray ( ) ; println ( " deleted " .. val ) end
2023-07-16 23:42:20 +00:00
end
end
2023-07-17 01:07:37 +00:00
white ( )
2023-07-16 23:42:20 +00:00
end
2023-02-19 17:54:02 +00:00
-- get and validate command line options
2024-08-12 02:11:57 +00:00
if _is_pkt_env then println ( " - SCADA Installer " .. CCMSI_VERSION .. " - " )
else println ( " -- CC Mekanism SCADA Installer " .. CCMSI_VERSION .. " -- " ) end
2023-02-19 17:54:02 +00:00
2023-02-20 00:56:12 +00:00
if # opts == 0 or opts [ 1 ] == " help " then
2023-06-18 04:48:06 +00:00
println ( " usage: ccmsi <mode> <app> <branch> " )
2024-08-12 02:11:57 +00:00
if _is_pkt_env then
yellow ( ) ; println ( " <mode> " ) ; lgray ( )
println ( " check - check latest " )
println ( " install - fresh install " )
println ( " update - update app " )
println ( " uninstall - remove app " )
yellow ( ) ; println ( " <app> " ) ; lgray ( )
println ( " reactor-plc " )
println ( " rtu " )
println ( " supervisor " )
println ( " coordinator " )
println ( " pocket " )
println ( " installer (update only) " )
yellow ( ) ; println ( " <branch> " ) ; lgray ( ) ;
println ( " main (default) | devel " ) ; white ( )
else
2023-02-19 17:54:02 +00:00
println ( " <mode> " )
2023-06-25 21:53:02 +00:00
lgray ( )
2023-11-04 16:49:54 +00:00
println ( " check - check latest versions available " )
2023-06-25 21:53:02 +00:00
yellow ( )
2023-06-18 04:48:06 +00:00
println ( " ccmsi check <branch> for target " )
2023-06-25 21:53:02 +00:00
lgray ( )
2024-02-19 19:18:23 +00:00
println ( " install - fresh install " )
println ( " update - update files " )
2023-08-29 03:19:30 +00:00
println ( " uninstall - delete files INCLUDING config/logs " )
2023-07-16 23:42:20 +00:00
white ( ) ; println ( " <app> " ) ; lgray ( )
2023-02-19 17:54:02 +00:00
println ( " reactor-plc - reactor PLC firmware " )
println ( " rtu - RTU firmware " )
println ( " supervisor - supervisor server application " )
println ( " coordinator - coordinator application " )
println ( " pocket - pocket application " )
2023-08-22 02:47:00 +00:00
println ( " installer - ccmsi installer (update only) " )
white ( ) ; println ( " <branch> " )
2024-02-20 01:40:05 +00:00
lgray ( ) ; println ( " main (default) | devel " ) ; white ( )
2024-08-12 02:11:57 +00:00
end
2023-02-19 17:54:02 +00:00
return
else
2023-08-29 03:19:30 +00:00
mode = get_opt ( opts [ 1 ] , { " check " , " install " , " update " , " uninstall " } )
2023-02-19 17:54:02 +00:00
if mode == nil then
2023-07-16 23:42:20 +00:00
red ( ) ; println ( " Unrecognized mode. " ) ; white ( )
2023-02-19 17:54:02 +00:00
return
end
2023-08-22 02:47:00 +00:00
app = get_opt ( opts [ 2 ] , { " reactor-plc " , " rtu " , " supervisor " , " coordinator " , " pocket " , " installer " } )
2023-02-20 00:41:32 +00:00
if app == nil and mode ~= " check " then
2023-07-16 23:42:20 +00:00
red ( ) ; println ( " Unrecognized application. " ) ; white ( )
2023-02-19 17:54:02 +00:00
return
2023-08-22 02:47:00 +00:00
elseif app == " installer " and mode ~= " update " then
red ( ) ; println ( " Installer app only supports 'update' option. " ) ; white ( )
return
2023-02-19 17:54:02 +00:00
end
2023-06-18 17:12:34 +00:00
-- determine target
if mode == " check " then target = opts [ 2 ] else target = opts [ 3 ] end
2024-02-20 01:40:05 +00:00
if ( target ~= " main " ) and ( target ~= " devel " ) then
2023-07-16 23:42:20 +00:00
if ( target and target ~= " " ) then yellow ( ) ; println ( " Unknown target, defaulting to 'main' " ) ; white ( ) end
2023-06-18 17:12:34 +00:00
target = " main "
end
2023-06-25 21:53:02 +00:00
-- set paths
2024-02-19 19:18:23 +00:00
install_manifest = manifest_path .. target .. " /install_manifest.json "
repo_path = repo_path .. target .. " / "
2023-02-19 17:54:02 +00:00
end
-- run selected mode
2023-02-20 00:30:03 +00:00
if mode == " check " then
2023-06-25 21:53:02 +00:00
local ok , manifest = get_remote_manifest ( )
if not ok then return end
2023-02-19 17:54:02 +00:00
2023-06-25 21:53:02 +00:00
local local_ok , local_manifest = read_local_manifest ( )
2023-02-20 00:30:03 +00:00
if not local_ok then
2023-07-16 23:42:20 +00:00
yellow ( ) ; println ( " failed to load local installation information " ) ; white ( )
2023-03-04 19:40:49 +00:00
local_manifest = { versions = { installer = CCMSI_VERSION } }
2023-03-04 19:21:42 +00:00
else
local_manifest.versions . installer = CCMSI_VERSION
2023-02-20 00:30:03 +00:00
end
2023-02-20 01:43:39 +00:00
-- list all versions
2023-02-20 00:30:03 +00:00
for key , value in pairs ( manifest.versions ) do
2023-02-20 00:56:12 +00:00
term.setTextColor ( colors.purple )
2024-08-12 02:11:57 +00:00
local tag = string.format ( " %-14s " , " [ " .. key .. " ] " )
if not _is_pkt_env then print ( tag ) end
2023-03-04 19:40:49 +00:00
if key == " installer " or ( local_ok and ( local_manifest.versions [ key ] ~= nil ) ) then
2024-08-12 02:11:57 +00:00
if _is_pkt_env then println ( tag ) end
2023-07-16 23:42:20 +00:00
blue ( ) ; print ( local_manifest.versions [ key ] )
2023-02-20 01:43:39 +00:00
if value ~= local_manifest.versions [ key ] then
2023-07-16 23:42:20 +00:00
white ( ) ; print ( " ( " )
2023-11-04 16:49:54 +00:00
cyan ( ) ; print ( value ) ; white ( ) ; println ( " available) " )
2023-07-16 23:42:20 +00:00
else green ( ) ; println ( " (up to date) " ) end
2024-08-12 02:11:57 +00:00
elseif not _is_pkt_env then
2023-07-16 23:42:20 +00:00
lgray ( ) ; print ( " not installed " ) ; white ( ) ; print ( " (latest " )
2023-11-04 16:49:54 +00:00
cyan ( ) ; print ( value ) ; white ( ) ; println ( " ) " )
2023-02-20 00:30:03 +00:00
end
end
2023-08-22 02:47:00 +00:00
2024-08-12 02:11:57 +00:00
if manifest.versions . installer ~= local_manifest.versions . installer and not _is_pkt_env then
2023-11-12 16:57:05 +00:00
yellow ( ) ; println ( " \n A different version of the installer is available, it is recommended to update (use 'ccmsi update installer'). " ) ; white ( )
2023-08-22 02:47:00 +00:00
end
2023-02-20 00:30:03 +00:00
elseif mode == " install " or mode == " update " then
2023-08-22 02:47:00 +00:00
local update_installer = app == " installer "
2023-06-25 21:53:02 +00:00
local ok , manifest = get_remote_manifest ( )
if not ok then return end
2023-02-20 00:30:03 +00:00
2023-06-18 04:40:01 +00:00
local ver = {
app = { v_local = nil , v_remote = nil , changed = false } ,
boot = { v_local = nil , v_remote = nil , changed = false } ,
comms = { v_local = nil , v_remote = nil , changed = false } ,
2023-07-31 00:46:04 +00:00
common = { v_local = nil , v_remote = nil , changed = false } ,
2023-06-25 21:53:02 +00:00
graphics = { v_local = nil , v_remote = nil , changed = false } ,
lockbox = { v_local = nil , v_remote = nil , changed = false }
2023-06-18 04:40:01 +00:00
}
2023-02-20 01:43:39 +00:00
-- try to find local versions
2023-08-22 02:18:25 +00:00
local local_ok , lmnf = read_local_manifest ( )
2023-02-20 00:18:06 +00:00
if not local_ok then
if mode == " update " then
2023-08-22 02:18:25 +00:00
red ( ) ; println ( " Failed to load local installation information, cannot update. " ) ; white ( )
2023-02-20 02:52:43 +00:00
return
2023-02-20 00:18:06 +00:00
end
2023-08-22 02:47:00 +00:00
elseif not update_installer then
2023-08-22 02:18:25 +00:00
ver.boot . v_local = lmnf.versions . bootloader
ver.app . v_local = lmnf.versions [ app ]
ver.comms . v_local = lmnf.versions . comms
ver.common . v_local = lmnf.versions . common
ver.graphics . v_local = lmnf.versions . graphics
ver.lockbox . v_local = lmnf.versions . lockbox
if lmnf.versions [ app ] == nil then
2023-08-29 03:19:30 +00:00
red ( ) ; println ( " Another application is already installed, please uninstall it before installing a new application. " ) ; white ( )
2023-02-20 00:18:06 +00:00
return
end
2023-08-22 02:18:25 +00:00
end
2023-02-19 17:54:02 +00:00
2023-08-22 02:18:25 +00:00
if manifest.versions . installer ~= CCMSI_VERSION then
2023-11-12 16:57:05 +00:00
if not update_installer then yellow ( ) ; println ( " A different version of the installer is available, it is recommended to update to it. " ) ; white ( ) end
2023-08-22 02:47:00 +00:00
if update_installer or ask_y_n ( " Would you like to update now " ) then
2023-08-22 02:18:25 +00:00
lgray ( ) ; println ( " GET ccmsi.lua " )
2024-02-19 19:18:23 +00:00
local dl , err = http.get ( repo_path .. " ccmsi.lua " )
2023-08-22 02:18:25 +00:00
if dl == nil then
2024-07-04 01:14:39 +00:00
red ( ) ; println ( " HTTP Error: " .. err )
2023-08-22 02:18:25 +00:00
println ( " Installer download failed. " ) ; white ( )
else
2023-08-22 02:47:00 +00:00
local handle = fs.open ( debug.getinfo ( 1 , " S " ) . source : sub ( 2 ) , " w " ) -- this file, regardless of name or location
2023-08-22 02:18:25 +00:00
handle.write ( dl.readAll ( ) )
handle.close ( )
green ( ) ; println ( " Installer updated successfully. " ) ; white ( )
end
return
2023-03-04 19:19:17 +00:00
end
2023-08-22 02:47:00 +00:00
elseif update_installer then
green ( ) ; println ( " Installer already up-to-date. " ) ; white ( )
return
2023-03-04 19:13:47 +00:00
end
2023-06-18 04:40:01 +00:00
ver.boot . v_remote = manifest.versions . bootloader
ver.app . v_remote = manifest.versions [ app ]
ver.comms . v_remote = manifest.versions . comms
2023-07-31 00:46:04 +00:00
ver.common . v_remote = manifest.versions . common
2023-06-18 04:40:01 +00:00
ver.graphics . v_remote = manifest.versions . graphics
2023-06-25 21:53:02 +00:00
ver.lockbox . v_remote = manifest.versions . lockbox
2023-02-19 17:54:02 +00:00
2023-06-25 21:53:02 +00:00
green ( )
2024-02-20 01:24:30 +00:00
if mode == " install " then print ( " Installing " ) else print ( " Updating " ) end
2024-02-19 19:18:23 +00:00
println ( app .. " files... " ) ; white ( )
2023-02-19 17:54:02 +00:00
2023-07-31 00:46:04 +00:00
ver.boot . changed = show_pkg_change ( " bootldr " , ver.boot )
ver.common . changed = show_pkg_change ( " common " , ver.common )
ver.comms . changed = show_pkg_change ( " comms " , ver.comms )
2023-06-18 17:12:34 +00:00
if ver.comms . changed and ver.comms . v_local ~= nil then
2023-07-16 23:42:20 +00:00
print ( " [comms] " ) ; yellow ( ) ; println ( " other devices on the network will require an update " ) ; white ( )
2023-02-19 17:54:02 +00:00
end
2023-07-31 00:46:04 +00:00
ver.app . changed = show_pkg_change ( app , ver.app )
ver.graphics . changed = show_pkg_change ( " graphics " , ver.graphics )
ver.lockbox . changed = show_pkg_change ( " lockbox " , ver.lockbox )
2023-06-25 21:53:02 +00:00
2023-02-19 17:54:02 +00:00
--------------------------
-- START INSTALL/UPDATE --
--------------------------
2023-02-28 04:51:26 +00:00
local space_required = manifest.sizes . manifest
2023-02-19 17:54:02 +00:00
local space_available = fs.getFreeSpace ( " / " )
local single_file_mode = false
local file_list = manifest.files
local size_list = manifest.sizes
local dependencies = manifest.depends [ app ]
2023-02-19 22:15:26 +00:00
table.insert ( dependencies , app )
2024-07-04 01:14:39 +00:00
-- helper function to check if a dependency is unchanged
local function unchanged ( dependency )
if dependency == " system " then return not ver.boot . changed
elseif dependency == " graphics " then return not ver.graphics . changed
elseif dependency == " lockbox " then return not ver.lockbox . changed
elseif dependency == " common " then return not ( ver.common . changed or ver.comms . changed )
elseif dependency == app then return not ver.app . changed
else return true end
end
local any_change = false
2023-02-19 17:54:02 +00:00
for _ , dependency in pairs ( dependencies ) do
local size = size_list [ dependency ]
space_required = space_required + size
2024-07-04 01:14:39 +00:00
any_change = any_change or not unchanged ( dependency )
end
if mode == " update " and not any_change then
yellow ( ) ; println ( " Nothing to do, everything is already up-to-date! " ) ; white ( )
return
2023-02-19 17:54:02 +00:00
end
2024-07-04 01:14:39 +00:00
-- ask for confirmation
if not ask_y_n ( " Continue " , false ) then return end
2023-02-20 01:43:39 +00:00
-- check space constraints
2023-02-19 17:54:02 +00:00
if space_available < space_required then
single_file_mode = true
2023-08-29 03:19:30 +00:00
yellow ( ) ; println ( " NOTICE: Insufficient space available for a full cached download! " ) ; white ( )
lgray ( ) ; println ( " Files can instead be downloaded one by one. If you are replacing a current install this may corrupt your install ONLY if it fails (such as a sudden network issue). If that occurs, you can still try again. " )
if mode == " update " then println ( " If installation still fails, delete this device's log file and/or any unrelated files you have on this computer then try again. " ) end
white ( ) ;
2023-08-22 02:18:25 +00:00
if not ask_y_n ( " Do you wish to continue " , false ) then
2023-06-18 04:40:01 +00:00
println ( " Operation cancelled. " )
2023-02-19 17:54:02 +00:00
return
end
end
local success = true
if not single_file_mode then
2023-07-31 00:46:04 +00:00
if fs.exists ( install_dir ) then fs.delete ( install_dir ) ; fs.makeDir ( install_dir ) end
2023-02-19 17:54:02 +00:00
2023-02-20 01:43:39 +00:00
-- download all dependencies
2023-02-19 17:54:02 +00:00
for _ , dependency in pairs ( dependencies ) do
2023-06-18 05:19:00 +00:00
if mode == " update " and unchanged ( dependency ) then
2023-06-18 04:40:01 +00:00
pkg_message ( " skipping download of unchanged package " , dependency )
2023-02-19 23:49:04 +00:00
else
2023-06-18 04:40:01 +00:00
pkg_message ( " downloading package " , dependency )
2023-06-25 21:53:02 +00:00
lgray ( )
2023-06-18 04:40:01 +00:00
2023-02-19 23:49:04 +00:00
local files = file_list [ dependency ]
for _ , file in pairs ( files ) do
2024-02-19 19:18:23 +00:00
println ( " GET " .. file )
2024-07-03 15:01:43 +00:00
if not http_get_file ( file , install_dir .. " / " ) then
2024-07-03 02:07:12 +00:00
red ( ) ; println ( " failed to download " .. file )
success = false
break
2023-02-19 23:49:04 +00:00
end
2023-02-19 17:54:02 +00:00
end
end
2024-07-04 01:14:39 +00:00
if not success then break end
2023-02-19 17:54:02 +00:00
end
2023-02-20 01:43:39 +00:00
-- copy in downloaded files (installation)
2023-02-19 17:54:02 +00:00
if success then
for _ , dependency in pairs ( dependencies ) do
2023-06-18 05:19:00 +00:00
if mode == " update " and unchanged ( dependency ) then
2023-06-18 04:40:01 +00:00
pkg_message ( " skipping install of unchanged package " , dependency )
2023-02-19 23:49:04 +00:00
else
2023-06-18 04:40:01 +00:00
pkg_message ( " installing package " , dependency )
2023-06-25 21:53:02 +00:00
lgray ( )
2023-06-18 04:40:01 +00:00
2023-02-19 23:49:04 +00:00
local files = file_list [ dependency ]
for _ , file in pairs ( files ) do
2024-02-19 19:18:23 +00:00
local temp_file = install_dir .. " / " .. file
if fs.exists ( file ) then fs.delete ( file ) end
fs.move ( temp_file , file )
2023-02-19 17:54:02 +00:00
end
end
end
end
fs.delete ( install_dir )
if success then
2023-02-20 00:14:47 +00:00
write_install_manifest ( manifest , dependencies )
2023-06-25 21:53:02 +00:00
green ( )
2023-02-19 17:54:02 +00:00
if mode == " install " then
2023-06-18 04:40:01 +00:00
println ( " Installation completed successfully. " )
2023-06-25 21:53:02 +00:00
else println ( " Update completed successfully. " ) end
2023-07-17 00:53:39 +00:00
white ( ) ; println ( " Ready to clean up unused files, press any key to continue... " )
any_key ( ) ; clean ( manifest )
white ( ) ; println ( " Done. " )
2023-02-19 17:54:02 +00:00
else
if mode == " install " then
2023-07-16 23:42:20 +00:00
red ( ) ; println ( " Installation failed. " )
else orange ( ) ; println ( " Update failed, existing files unmodified. " ) end
2023-02-19 17:54:02 +00:00
end
else
2023-02-20 01:43:39 +00:00
-- go through all files and replace one by one
2023-02-19 17:54:02 +00:00
for _ , dependency in pairs ( dependencies ) do
2023-06-18 05:19:00 +00:00
if mode == " update " and unchanged ( dependency ) then
2023-06-18 04:40:01 +00:00
pkg_message ( " skipping install of unchanged package " , dependency )
2023-02-19 23:49:04 +00:00
else
2023-06-18 04:40:01 +00:00
pkg_message ( " installing package " , dependency )
2023-06-25 21:53:02 +00:00
lgray ( )
2023-06-18 04:40:01 +00:00
2023-02-19 23:49:04 +00:00
local files = file_list [ dependency ]
for _ , file in pairs ( files ) do
2024-02-19 19:18:23 +00:00
println ( " GET " .. file )
2024-07-03 15:01:43 +00:00
if not http_get_file ( file , " / " ) then
2024-07-03 02:07:12 +00:00
red ( ) ; println ( " failed to download " .. file )
success = false
break
2023-02-19 23:49:04 +00:00
end
2023-02-19 17:54:02 +00:00
end
end
2024-07-04 01:14:39 +00:00
if not success then break end
2023-02-19 17:54:02 +00:00
end
if success then
2023-02-20 00:14:47 +00:00
write_install_manifest ( manifest , dependencies )
2023-06-25 21:53:02 +00:00
green ( )
2023-02-19 17:54:02 +00:00
if mode == " install " then
2023-06-18 04:40:01 +00:00
println ( " Installation completed successfully. " )
2023-06-25 21:53:02 +00:00
else println ( " Update completed successfully. " ) end
2023-07-17 00:53:39 +00:00
white ( ) ; println ( " Ready to clean up unused files, press any key to continue... " )
any_key ( ) ; clean ( manifest )
white ( ) ; println ( " Done. " )
2023-02-19 17:54:02 +00:00
else
2023-06-25 21:53:02 +00:00
red ( )
2023-02-19 17:54:02 +00:00
if mode == " install " then
2023-06-18 04:40:01 +00:00
println ( " Installation failed, files may have been skipped. " )
2023-06-25 21:53:02 +00:00
else println ( " Update failed, files may have been skipped. " ) end
2023-02-19 17:54:02 +00:00
end
end
2023-08-29 03:19:30 +00:00
elseif mode == " uninstall " then
2023-06-25 21:53:02 +00:00
local ok , manifest = read_local_manifest ( )
2023-02-19 17:54:02 +00:00
if not ok then
2023-07-16 23:42:20 +00:00
red ( ) ; println ( " Error parsing local installation manifest. " ) ; white ( )
2023-02-19 17:54:02 +00:00
return
2023-02-19 22:15:26 +00:00
end
2023-08-29 03:19:30 +00:00
if manifest.versions [ app ] == nil then
2024-02-19 19:18:23 +00:00
red ( ) ; println ( " Error: ' " .. app .. " ' is not installed. " )
2023-08-29 03:19:30 +00:00
return
2023-02-19 17:54:02 +00:00
end
2024-02-19 19:18:23 +00:00
orange ( ) ; println ( " Uninstalling all " .. app .. " files... " )
2023-08-29 03:19:30 +00:00
2023-06-18 04:40:01 +00:00
-- ask for confirmation
2023-07-17 01:21:33 +00:00
if not ask_y_n ( " Continue " , false ) then return end
2023-02-19 17:54:02 +00:00
2023-07-16 23:42:20 +00:00
-- delete unused files first
clean ( manifest )
2023-02-19 17:54:02 +00:00
local file_list = manifest.files
local dependencies = manifest.depends [ app ]
2023-02-19 22:15:26 +00:00
table.insert ( dependencies , app )
2023-08-29 03:19:30 +00:00
-- delete all installed files
2024-06-30 03:38:51 +00:00
lgray ( )
2023-02-19 17:54:02 +00:00
for _ , dependency in pairs ( dependencies ) do
local files = file_list [ dependency ]
for _ , file in pairs ( files ) do
2024-02-19 19:18:23 +00:00
if fs.exists ( file ) then fs.delete ( file ) ; println ( " deleted " .. file ) end
2023-02-19 17:54:02 +00:00
end
2023-02-20 00:41:32 +00:00
2023-08-29 03:19:30 +00:00
local folder = files [ 1 ]
while true do
local dir = fs.getDir ( folder )
if dir == " " or dir == " .. " then break else folder = dir end
end
2023-02-20 00:41:32 +00:00
2023-08-29 03:19:30 +00:00
if fs.isDir ( folder ) then
fs.delete ( folder )
2024-02-19 19:18:23 +00:00
println ( " deleted directory " .. folder )
2023-02-20 00:41:32 +00:00
end
2023-02-19 17:54:02 +00:00
end
2024-06-30 02:39:58 +00:00
-- delete log file
local log_deleted = false
local settings_file = app .. " .settings "
if fs.exists ( settings_file ) and settings.load ( settings_file ) then
local log = settings.get ( " LogPath " )
if log ~= nil then
log_deleted = true
if fs.exists ( log ) then
fs.delete ( log )
println ( " deleted log file " .. log )
end
end
end
if not log_deleted then
2024-06-30 03:38:51 +00:00
red ( ) ; println ( " Failed to delete log file (it may not exist). " ) ; lgray ( )
2024-02-19 19:18:23 +00:00
end
2023-10-04 03:11:52 +00:00
if fs.exists ( settings_file ) then
2024-02-19 19:18:23 +00:00
fs.delete ( settings_file ) ; println ( " deleted " .. settings_file )
2023-10-04 03:11:52 +00:00
end
2023-08-29 03:19:30 +00:00
fs.delete ( " install_manifest.json " )
println ( " deleted install_manifest.json " )
2023-02-20 00:14:47 +00:00
2023-07-16 23:42:20 +00:00
green ( ) ; println ( " Done! " )
2023-02-19 17:54:02 +00:00
end
2023-02-19 22:15:26 +00:00
2023-06-25 21:53:02 +00:00
white ( )