refactored updater

This commit is contained in:
2024-02-24 01:34:57 +01:00
parent 8604292f1d
commit 7bab5e6be0

View File

@@ -1,128 +1,115 @@
local args = {...} local args = {...}
local internet=require("internet") local internet=require("internet")
local text=require("text")
local filesystem=require("filesystem") local filesystem=require("filesystem")
local unicode=require("unicode") local unicode=require("unicode")
local term=require("term")
local event=require("event")
local keyboard=require("keyboard")
local repository = "seesberger/PowerManager" YES = {"y","yes","Y","Yes","YES"}
local repoTargetPath = "/usr/PowerManager/" NO = {"n","no","N","No","NO"}
local libTargetPath = "/lib/"
local helpText = "This updater pulls the git files for installation and application updates.\n".. SupportedRemotes = {
"Usage:\n" .. Github = {
"updater <option> - no args: manual update and install\n".. BaseUrl = "https://github.com/",
" '' -h - this help text\n" .. Implemented = true
" '' -a - automatic update no install\n" .. },
" '' -i - automatic update and install" Gitea = {
BaseUrl = "https://git.realrobin.io",
Implemented = false
}
}
function manualUpdate() Repository = {}
print("Manual repo pull...")
downloadRepo(repository, repoTargetPath, false)
end
function manualInstall() ---default repo to update
print("installing shortcut...") DefaultRepository = {
os.execute("mv "..repoTargetPath.."shortcut.lua /usr/bin/powerman.lua") Owner = "seesberger",
end Name = "PowerManager",
ShortName = "powerman",
RepoIdentifier = "seesberger/PowerManager",
Remote = SupportedRemotes.Github,
CurrentBranch = "master",
CurrentLocalPath = ""
}
function automaticUpdate() DefaultTemporaryDownloadPath = "/home/.tmp/git/"
print("Auto repo pull...") DefaultInstallationPath = "/usr/"
downloadRepo(repository, repoTargetPath, true)
end
function automaticInstall() local function askYesOrNoQuestion(question, expectedTrue, expectedFalse, defaultYesOnEnter)
print("downloading GUI-API") local function checkContains(array,value)
downloadDependencies() for idx, val in array do
manualInstall() if value == val then return true
end end
return false
function downloadDependencies(automatic)
os.execute("wget -f https://github.com/kevinkk525/OC-GUI-API/raw/master/shapes_default.lua /lib/shapes_default.lua")
os.execute("wget -f https://github.com/kevinkk525/OC-GUI-API/raw/master/GUI.lua /lib/GUI.lua")
os.execute("wget -f https://github.com/kevinkk525/OC-GUI-API/raw/master/term_mod.lua /lib/term_mod.lua")
os.execute("wget -f https://github.com/kevinkk525/OC-GUI-API/raw/master/tech_demo.lua /home/GUI_tech_demo.lua")
end
function downloadRepo(repo, target, automatic)
if not repo:match("^[%w-.]*/[%w-.]*$") then
print('"'..repo..'" does not look like a valid repo identifier.\nShould be <owner>/<reponame>')
return
end end
target=target and ("/"..target:match("^/?(.-)/?$").."/") or "/tmp/"..repo if defaultYesOnEnter==true then print(question.." [Y/n]") else print(question.." [y/N]") end
while true do
local userInput = io.read("l")
if checkContains({""}, userInput) then return defaultYesOnEnter end
if checkContains(expectedTrue, userInput) then return true end
if checkContains(expectedFalse, userInput) then return false end
print("Please answer with yes or no. You can also press ENTER to choose the default option.")
end
end
end
local function askTextQuestion(question, defaultAnswerOnEnter, allowOnly)
local allowedInputsString = ""
for idx, entry in allowOnly do allowedInputsString = allowedInputsString..entry end
if allowOnly then print(question.." ["..allowedInputsString.."]")
else print(question) end
local userInput = nil
local found = false
repeat
userInput = io.read("l")
if allowOnly then
for idx, entry in allowOnly do if entry == userInput then found = true end end
else break end
until found
if userInput == "" then return defaultAnswerOnEnter else return userInput end
end
--- todo: pcall and catch errors
local function downloadRepo(repository, autoOverride)
local function validateRepositoryIdentifier(repository)
if not repository.RepoIdentifier:match("^[%w-.]*/[%w-.]*$") then
print('"'..repository.RepoIdentifier..'" does not look like a valid repo identifier.\nShould be <owner>/<reponame>')
return
end
end
validateRepositoryIdentifier(repository)
local function makeDirIfNotExists(target)
if filesystem.exists(target) then if filesystem.exists(target) then
if not filesystem.isDirectory(target) then if not filesystem.isDirectory(target) then error("target directory already exists and is not a directory.") end
print("target directory already exists and is not a directory.") if filesystem.get(target).isReadOnly() then error("target directory is read-only.") end
return
end
if filesystem.get(target).isReadOnly() then
print("target directory is read-only.")
return
end
else else
if not filesystem.makeDirectory(target) then if not filesystem.makeDirectory(target) then error("directory creation failed") end
print("target directory is read-only")
return
end end
end end
--- FIXME: If download only mode is enabled, set this to /home. still todo
local targetDownloadPath = DefaultTemporaryDownloadPath..repository.RepoIdentifier
repository.CurrentLocalPath = targetDownloadPath
local success, err = pcall(makeDirIfNotExists, targetDownloadPath)
if not success then error("the download failed because of filesystem errors.") end
local function fetchFilesAndSubdirs(repository, remoteType, dir)
-- this isn't acually used, but it is tested and works on decoding the base64 encoded data that github dir = dir or "" -- default value, start at root dir
--sends for some queries, leaving it in here for possible future/related use, might be able to pull if remoteType == SupportedRemotes.Github then
--and display difs and things like that? print("fetching contents for "..repository.RepoIdentifier..dir)
local symb="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" local githubApiUrl="https://api.github.com/repos/"..repository.RepoIdentifier.."/contents"..dir
local success,chunks=pcall(internet.request,githubApiUrl)
function decode64(text)
local val,bits=0,0
local output=""
for ch in text:gmatch(".") do
if symb:find(ch) then
--print("ch "..ch.."-> "..(symb:find(ch)-1))
val=bit32.lshift(val,6)+symb:find(ch)-1
else
print(ch.."?")
return
end
bits=bits+6
--print("bits : "..bits)
--print(string.format("val : 0x%04x",val))
if bits>=8 then
local och=unicode.char(bit32.rshift(val,bits-8))
--print("os<<"..och)
--print("& with "..(2^(bits-8)-1))
val=bit32.band(val,2^(bits-8)-1)
bits=bits-8
--print(string.format("val : 0x%04x",val))
output=output..och
end
end
return output
end
local function gitContents(repo, dir)
print("fetching contents for "..repo..dir)
local url="https://api.github.com/repos/"..repo.."/contents"..dir
local result,response=pcall(internet.request,url)
local raw="" local raw=""
local files={} local files={}
local directories={} local directories={}
if result then if success then for chunk in chunks do raw=raw..chunk end
for chunk in response do else error("you've been cut off. Serves you right.") end
raw=raw..chunk
end
else
error("you've been cut off. Serves you right.")
end
response=nil --- do not question the magic of the outer gods
--- turns raw response into t, which has usable fields.
raw=raw:gsub("%[","{"):gsub("%]","}"):gsub("(\".-\"):(.-[,{}])",function(a,b) return "["..a.."]="..b end) raw=raw:gsub("%[","{"):gsub("%]","}"):gsub("(\".-\"):(.-[,{}])",function(a,b) return "["..a.."]="..b end)
local t=load("return "..raw)() local t=load("return "..raw)()
@@ -130,7 +117,7 @@ function downloadRepo(repo, target, automatic)
if t[i].type=="dir" then if t[i].type=="dir" then
table.insert(directories,dir.."/"..t[i].name) table.insert(directories,dir.."/"..t[i].name)
local subfiles,subdirs=gitContents(repo,dir.."/"..t[i].name) local subfiles,subdirs=fetchFilesAndSubdirs(repository.RepoIdentifier,dir.."/"..t[i].name)
for i=1,#subfiles do for i=1,#subfiles do
table.insert(files,subfiles[i]) table.insert(files,subfiles[i])
end end
@@ -143,108 +130,124 @@ function downloadRepo(repo, target, automatic)
end end
return files, directories return files, directories
else error("not Implemented") end
end end
local files,dirs=gitContents(repo,"") --- fetch and make dirs in the target download path recursively
local files,dirs=fetchFilesAndSubdirs(repository.RepoIdentifier, "", SupportedRemotes.Github)
for i=1,#dirs do for i=1,#dirs do
print("making dir "..target..dirs[i]) local success, err = pcall(makeDirIfNotExists, targetDownloadPath..dirs[i])
if filesystem.exists(target..dirs[i]) then if not success then error(("the download failed because of filesystem errors. %x"):format(err)) end
if not filesystem.isDirectory(target..dirs[i]) then
print("error: directory "..target..dirs[i].." blocked by file with the same name")
return
end
else
filesystem.makeDirectory(target..dirs[i])
end
end end
local replaceMode="ask" local replaceMode="ask"
if automatic == true then replaceMode = "always" end if autoOverride == true then replaceMode = "always" end
local function downloadFiles(files, targetDownloadPath, replaceMode)
for i=1,#files do for i=1,#files do
local replace=nil local replace=nil
if filesystem.exists(target..files[i]) then if filesystem.exists(targetDownloadPath..files[i]) then
if filesystem.isDirectory(target..files[i]) then --- FIXME dir löschen statt error
print("Error: file "..target..files[i].." blocked by directory with same name!") if filesystem.isDirectory(targetDownloadPath..files[i]) then error("file "..targetDownloadPath..files[i].." blocked by directory with same name!") end
return if not replaceMode == "ask" then
replace = (replaceMode=="always")
replace = not (replaceMode=="never")
end end
if replaceMode=="always" then else print("\nFile "..targetDownloadPath..files[i].." already exists.\nReplace with new version?") end
replace=true
elseif replaceMode=="never" then
replace=false
else
print("\nFile "..target..files[i].." already exists.\nReplace with new version?")
local response="" local response=""
while replace==nil do if replace == nil then
term.write("yes,no,always,skip all[ynAS]: ") --- FIXME: ggf falsch und buggy
local char local userInput = askTextQuestion("\nFile "..targetDownloadPath..files[i].." already exists.\nReplace with new version?","n",{"y","n","A","S"})
repeat
_,_,char=event.pull("key_down") if userInput=="A" then
until not keyboard.isControl(char)
char=unicode.char(char)
print(char)
if char=="A" then
replaceMode="always" replaceMode="always"
replace=true userInput="y"
char="y" elseif userInput=="S" then
elseif char=="S" then
replaceMode="never" replaceMode="never"
replace=false userInput="n"
char="n" elseif userInput == "y" then replace=true
elseif char:lower()=="y" then elseif userInput == "n" then replace=false
replace=true
elseif char:lower()=="n" then
replace=false
else
print("invalid response.")
end end
end end
end if replace then filesystem.remove(targetDownloadPath..files[i]) end
if replace then if replace or replace == nil then
filesystem.remove(target..files[i]) --- TODO @Freddy: Coole Animation hinzufügen
end print("downloading file "..files[i])
end local url=repository.Remote.BaseUrl..repository.Name.."/"..repository.CurrentBranch..files[i]
if replace~=false then local success,response=pcall(internet.request,url)
print("downloading "..files[i]) if success then
local url="https://raw.github.com/"..repo.."/master"..files[i]
local result,response=pcall(internet.request,url)
if result then
local raw="" local raw=""
for chunk in response do for chunk in response do
raw=raw..chunk raw=raw..chunk
end end
print("writing to "..target..files[i]) print("writing to "..targetDownloadPath..files[i])
local file=io.open(target..files[i],"w") local file=io.open(targetDownloadPath..files[i],"w")
if file then -- might be nil under wierd circumstances
file:write(raw) file:write(raw)
file:close() file:close()
end
else error("a file did not download correctly. aborting") end
end
end
end
success, err = pcall(downloadFiles, files, targetDownloadPath, replaceMode)
if success then print("All files downloaded successfully.") else error(err) end
end
local function installShortcut(currentRepoPath, shortcutName)
print("installing shortcut...")
os.execute("mv "..currentRepoPath.."shortcut.lua /usr/bin/"..shortcutName..".lua")
end
--- todo: automatic read of dependency list (txt file containing lines with <link> <dst> or somethink like it)
--- removeme
local function legacyInstallDependencies(enabled)
if enabled==true then
os.execute("wget -f https://github.com/kevinkk525/OC-GUI-API/raw/master/shapes_default.lua /lib/shapes_default.lua")
os.execute("wget -f https://github.com/kevinkk525/OC-GUI-API/raw/master/GUI.lua /lib/GUI.lua")
os.execute("wget -f https://github.com/kevinkk525/OC-GUI-API/raw/master/term_mod.lua /lib/term_mod.lua")
os.execute("wget -f https://github.com/kevinkk525/OC-GUI-API/raw/master/tech_demo.lua /home/GUI_tech_demo.lua")
end
end
--- fix legacy shit
local function runFullInstallTask(repository)
--- first, download the actual repo.
--- then, find dependencies - if then exist, download and install them
--- then, install the actual program
legacyInstallDependencies(true) --only for testing while new version not implemented yet
print("downloading "..repository.RepoIdentifier)
downloadRepo(repository, false) --enable autooverwrite in other situations still todo
installShortcut(repository.CurrentLocalPath)
end
local function printHelpText()
local helpText = "This updater pulls the git files for installation and application updates.\n"..
"Usage:\n" ..
"updater <option> - no args: manual update and install\n"..
" '' -h or --help - this help text"
print(helpText)
end
local function run(cliArgs)
if #cliArgs<1 then
print("No Arguments given. For help, please check -h or --help")
return
end
if cliArgs[1] == ("-h" or "--help") then
printHelpText()
return
elseif cliArgs[1] == ("--setup-default") then
--- ask user about repo
Repository = DefaultRepository
if askYesOrNoQuestion("Use default config? (github::seesberger/PowerManager)?",YES,NO,true) then runFullInstallTask(Repository) end
return
else else
print("failed, skipping") print('"'..cliArgs[1]..'" - Bad argument. Try --help')
end return
end print("Program exited.")
end end
end end
if #args<1 then run(args)
print("No Arguments given. Manual process...")
manualUpdate()
automaticInstall()
return
end
if args[1] == "-h" then
print(helpText)
return
elseif args[1] == "-a" then
automaticUpdate()
return
elseif args[1] == "-i" then
automaticUpdate()
automaticInstall()
return
else
print('"'..args[1]..'" - Bad argument.')
print(helpText)
return
end