2018-11-24 00:49:01 +08:00
|
|
|
-- Grab : Official OpenComputerScripts Installer
|
2018-11-17 17:26:07 +08:00
|
|
|
-- Created By Kiritow
|
2018-11-21 12:14:03 +08:00
|
|
|
local computer=require('computer')
|
2018-11-17 17:26:07 +08:00
|
|
|
local component=require('component')
|
|
|
|
local shell=require('shell')
|
2018-11-21 11:55:45 +08:00
|
|
|
local filesystem=require('filesystem')
|
|
|
|
local serialization=require('serialization')
|
2018-11-24 16:04:29 +08:00
|
|
|
local event=require('event')
|
2018-11-17 17:26:07 +08:00
|
|
|
local args,options=shell.parse(...)
|
2018-11-24 00:49:01 +08:00
|
|
|
|
2018-11-24 16:04:29 +08:00
|
|
|
local grab_version="Grab v2.2.1-alpha"
|
2018-11-24 00:49:01 +08:00
|
|
|
|
|
|
|
local valid_options={
|
2018-11-24 01:29:56 +08:00
|
|
|
["cn"]=true, ["help"]=true, ["version"]=true, ["proxy"]=true, ["skip_install"]=true
|
2018-11-24 00:49:01 +08:00
|
|
|
}
|
|
|
|
local valid_command={
|
2018-11-24 01:29:56 +08:00
|
|
|
["install"]=true,["update"]=true,["list"]=true,["show"]=true,["download"]=true
|
2018-11-24 00:49:01 +08:00
|
|
|
}
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
local nOptions=0
|
2018-11-24 00:49:01 +08:00
|
|
|
for k,v in pairs(options) do
|
|
|
|
if(not valid_options[k]) then
|
|
|
|
if(string.len(k)>1) then print("Unknown option: --" .. k)
|
|
|
|
else print("Unknown option: -" .. k) end
|
|
|
|
return
|
|
|
|
end
|
|
|
|
nOptions=nOptions+1
|
|
|
|
end
|
|
|
|
|
|
|
|
local function show_usage()
|
|
|
|
print([===[Grab - Official OpenComputerScripts Installer
|
|
|
|
Usage:
|
|
|
|
grab [<options>] <command> ...
|
|
|
|
Options:
|
|
|
|
--cn Use mirror site in China. By default grab will download from Github.
|
|
|
|
--help Display this help page."
|
|
|
|
--version Display version and exit."
|
|
|
|
--proxy=<Proxy File> Given a proxy file which will be loaded and returns a proxy function like: "
|
|
|
|
function(RepoName: string, Branch: string ,FileAddress: string): string"
|
2018-11-24 01:29:56 +08:00
|
|
|
--skip_install Library installers will not be executed.
|
2018-11-24 00:49:01 +08:00
|
|
|
Command:
|
|
|
|
install <Project> ...: Install projects. Dependency will be downloaded automatically.
|
|
|
|
update: Update program info.
|
|
|
|
list: List available projects.
|
2018-11-24 01:29:56 +08:00
|
|
|
show <Project> : Show more info about project.
|
2018-11-24 00:49:01 +08:00
|
|
|
download <Filename> ...: Directly download files. (Just like the old `update`!)
|
|
|
|
]===])
|
|
|
|
end
|
|
|
|
|
|
|
|
local function check_internet()
|
|
|
|
if(component.internet==nil) then
|
|
|
|
print("Error: An internet card is required to run this program.")
|
|
|
|
return false
|
|
|
|
else
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
if( (#args<1 and nOptions<1) or options["help"]) then
|
2018-11-24 00:49:01 +08:00
|
|
|
show_usage()
|
|
|
|
return
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
2018-11-24 00:49:01 +08:00
|
|
|
|
2018-11-17 19:21:26 +08:00
|
|
|
if(options["version"]) then
|
2018-11-24 00:49:01 +08:00
|
|
|
print(grab_version)
|
2018-11-17 19:21:26 +08:00
|
|
|
return
|
|
|
|
end
|
2018-11-24 00:49:01 +08:00
|
|
|
|
2018-11-17 17:26:07 +08:00
|
|
|
local function download(url)
|
|
|
|
if(component.internet==nil) then
|
|
|
|
error("This program requires an Internet card.")
|
|
|
|
end
|
|
|
|
local handle=component.internet.request(url)
|
|
|
|
while true do
|
|
|
|
local ret,err=handle.finishConnect()
|
|
|
|
if(ret==nil) then
|
|
|
|
return false,err
|
|
|
|
elseif(ret==true) then
|
|
|
|
break
|
2018-11-24 01:29:56 +08:00
|
|
|
else
|
|
|
|
local ev=event.pull(0.5,"interrupted")
|
|
|
|
if(ev~=nil) then
|
|
|
|
handle.close()
|
|
|
|
return false,"Interrupted from terminal."
|
|
|
|
end
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
local code=handle.response()
|
|
|
|
local result=''
|
|
|
|
while true do
|
|
|
|
local temp=handle.read()
|
|
|
|
if(temp==nil) then break end
|
|
|
|
result=result .. temp
|
|
|
|
end
|
|
|
|
handle.close()
|
|
|
|
return true,result,code
|
|
|
|
end
|
|
|
|
local UrlGenerator
|
2018-11-17 19:21:26 +08:00
|
|
|
if(not options["proxy"]) then
|
|
|
|
if(not options["cn"]) then
|
|
|
|
UrlGenerator=function(RepoName,Branch,FileAddress)
|
|
|
|
return "https://raw.githubusercontent.com/" .. RepoName .. "/" .. Branch .. "/" .. FileAddress
|
|
|
|
end
|
|
|
|
else
|
|
|
|
UrlGenerator=function(RepoName,Branch,FileAddress)
|
|
|
|
return "http://kiritow.com:3000/" .. RepoName .. "/raw/" .. Branch .. "/" .. FileAddress
|
|
|
|
end
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
|
|
|
else
|
2018-11-17 19:21:26 +08:00
|
|
|
local ok,err=pcall(function()
|
|
|
|
local f=io.open(options["proxy"],"r")
|
|
|
|
if(f==nil) then error("Proxy file not found") end
|
|
|
|
local src=f:read("a")
|
|
|
|
f:close()
|
|
|
|
local fn=load(src)
|
|
|
|
UrlGenerator=fn()
|
|
|
|
end)
|
|
|
|
if(not ok) then
|
|
|
|
print("Proxy file error: " .. err)
|
|
|
|
return
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
|
|
|
|
local db_dirs={"/etc/grab",".grab","/tmp/.grab"}
|
|
|
|
local db_positions={"/etc/grab/programs.info",".grab/programs.info","/tmp/.grab/programs.info"}
|
|
|
|
|
|
|
|
local function CheckAndLoad(raw_content)
|
|
|
|
local fn,err=load(raw_content)
|
|
|
|
if(fn) then
|
|
|
|
local ok,result=pcall(fn)
|
|
|
|
if(ok) then return result
|
|
|
|
else return nil,result end
|
|
|
|
end
|
|
|
|
return nil,err
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
|
2018-11-24 01:29:56 +08:00
|
|
|
local function ReadDB(read_from_this)
|
|
|
|
if(read_from_this) then
|
|
|
|
local f=io.open(read_from_this,"r")
|
2018-11-21 11:55:45 +08:00
|
|
|
if(f) then
|
|
|
|
local result=serialization.unserialize(f:read("*a"))
|
2018-11-17 17:26:07 +08:00
|
|
|
f:close()
|
2018-11-21 11:55:45 +08:00
|
|
|
return result,filename
|
2018-11-24 01:29:56 +08:00
|
|
|
else
|
|
|
|
return nil
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
end
|
2018-11-24 01:29:56 +08:00
|
|
|
|
|
|
|
for idx,filename in ipairs(db_positions) do
|
|
|
|
local a,b=ReadDB(filename)
|
|
|
|
if(a) then return a,b end
|
|
|
|
end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2018-11-24 01:29:56 +08:00
|
|
|
local function WriteDB(filename,tb)
|
2018-11-21 11:55:45 +08:00
|
|
|
local f=io.open(filename,"w")
|
|
|
|
if(f) then
|
2018-11-24 01:29:56 +08:00
|
|
|
f:write(serialization.serialize(tb))
|
2018-11-21 11:55:45 +08:00
|
|
|
f:close()
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2018-11-24 01:29:56 +08:00
|
|
|
local function UpdateDB(main_tb,new_tb) -- Change values with same key in main_tb to values in new_tb. Add new items to main_tb
|
|
|
|
for k,v in pairs(new_tb) do
|
|
|
|
main_tb[k]=v
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function CreateDB(tb)
|
2018-11-21 11:55:45 +08:00
|
|
|
for idx,dirname in ipairs(db_dirs) do
|
|
|
|
filesystem.makeDirectory(dirname) -- buggy
|
|
|
|
end
|
|
|
|
for idx,filename in ipairs(db_positions) do
|
2018-11-24 01:29:56 +08:00
|
|
|
local main_db=ReadDB(filename)
|
|
|
|
if(main_db) then
|
|
|
|
UpdateDB(main_db,tb)
|
|
|
|
if(WriteDB(filename,main_db)) then
|
|
|
|
return filename
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if(WriteDB(filename,tb)) then
|
|
|
|
return filename
|
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
if(args[1]=="update") then
|
2018-11-24 00:49:01 +08:00
|
|
|
if(not check_internet()) then return end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
print("Updating programs info....")
|
|
|
|
io.write("Downloading... ")
|
|
|
|
local ok,result,code=download(UrlGenerator("Kiritow/OpenComputerScripts","master","programs.info"))
|
2018-11-17 17:26:07 +08:00
|
|
|
if(not ok) then
|
2018-11-21 11:55:45 +08:00
|
|
|
print("[Failed] " .. result)
|
|
|
|
elseif(code~=200) then
|
|
|
|
print("[Failed] response code " .. code .. " is not 200.")
|
|
|
|
else
|
|
|
|
print("[OK]")
|
|
|
|
io.write("Validating... ")
|
|
|
|
local tb_data,validate_err=CheckAndLoad("return " .. result)
|
|
|
|
result=nil -- release memory
|
|
|
|
if(tb_data) then
|
|
|
|
print("[OK]")
|
|
|
|
io.write("Saving files... ")
|
|
|
|
local dbfilename=CreateDB(tb_data)
|
|
|
|
if(dbfilename) then
|
|
|
|
print("[OK]")
|
|
|
|
print("Programs info updated and saved to " .. dbfilename)
|
|
|
|
else
|
|
|
|
print("[Failed] Unable to save programs info")
|
|
|
|
end
|
|
|
|
else
|
|
|
|
print("[Failed]" .. validate_err)
|
|
|
|
end
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local db,dbfilename=ReadDB()
|
|
|
|
|
2018-11-24 01:29:56 +08:00
|
|
|
local function check_db()
|
|
|
|
if(db) then return true
|
|
|
|
else
|
|
|
|
print("No programs info found on this computer.")
|
|
|
|
print("Please run `grab update` first.")
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
if(args[1]=="install") then
|
|
|
|
if(#args<2) then
|
|
|
|
print("Nothing to install.")
|
|
|
|
return
|
|
|
|
else
|
2018-11-24 00:49:01 +08:00
|
|
|
if(not check_internet()) then return end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
print("Checking programs info...")
|
|
|
|
end
|
|
|
|
|
2018-11-24 01:29:56 +08:00
|
|
|
if(not check_db()) then return end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
local to_install={}
|
|
|
|
for i=2,#args,1 do
|
|
|
|
to_install[args[i]]=true
|
|
|
|
end
|
|
|
|
|
|
|
|
local newly_added=0
|
|
|
|
while true do
|
|
|
|
local to_add={}
|
|
|
|
for this_lib in pairs(to_install) do
|
|
|
|
if(not db[this_lib]) then
|
2018-11-21 12:08:25 +08:00
|
|
|
print("Library '" .. this_lib .. "' not found.")
|
2018-11-21 11:55:45 +08:00
|
|
|
return
|
|
|
|
else
|
|
|
|
if(db[this_lib].requires) then
|
|
|
|
for idx,this_req in ipairs(db[this_lib].requires) do
|
|
|
|
if(not to_install[this_req] and not to_add[this_req]) then
|
|
|
|
newly_added=newly_added+1
|
|
|
|
to_add[this_req]=true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
for this_lib in pairs(to_add) do
|
|
|
|
to_install[this_lib]=true
|
|
|
|
end
|
|
|
|
if(newly_added==0) then break
|
|
|
|
else
|
|
|
|
newly_added=0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
print("About to install the following libraries...")
|
|
|
|
local count_libs=0
|
|
|
|
local count_files=0
|
2018-11-21 12:24:10 +08:00
|
|
|
io.write("\t")
|
2018-11-21 11:55:45 +08:00
|
|
|
for this_lib in pairs(to_install) do
|
|
|
|
io.write(this_lib .. " ")
|
|
|
|
count_libs=count_libs+1
|
|
|
|
for k in ipairs(db[this_lib].files) do
|
|
|
|
count_files=count_files+1
|
|
|
|
end
|
|
|
|
end
|
2018-11-21 12:24:10 +08:00
|
|
|
print("\n" .. count_libs .. " libraries will be installed. " .. count_files .. " files will be downloaded.")
|
2018-11-21 11:55:45 +08:00
|
|
|
|
2018-11-21 12:14:03 +08:00
|
|
|
local time_before=computer.uptime()
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
print("Downloading...")
|
2018-11-21 12:24:10 +08:00
|
|
|
local id_installing=0
|
2018-11-21 11:55:45 +08:00
|
|
|
for this_lib in pairs(to_install) do
|
2018-11-24 16:04:29 +08:00
|
|
|
for k,v in pairs(db[this_lib].files) do
|
2018-11-21 12:24:10 +08:00
|
|
|
id_installing=id_installing+1
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
local toDownload
|
|
|
|
if(type(k)=="number" and type(v)=="string") then
|
|
|
|
toDownload=v
|
|
|
|
elseif(type(k)=="string") then
|
|
|
|
toDownload=k
|
|
|
|
else
|
|
|
|
print("Invalid programs info: key type: " .. type(k) .. ". value type: " .. type(v))
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
io.write("[" .. id_installing .. "/" .. count_files .. "] Downloading " .. toDownload .. " for " .. this_lib .. "... ")
|
2018-11-21 11:58:26 +08:00
|
|
|
local ok,result,code=download(UrlGenerator("Kiritow/OpenComputerScripts","master",toDownload))
|
2018-11-21 11:55:45 +08:00
|
|
|
if(not ok) then
|
|
|
|
print("[Download Failed] " .. result)
|
2018-11-21 12:08:25 +08:00
|
|
|
return
|
2018-11-21 11:55:45 +08:00
|
|
|
elseif(code~=200) then
|
|
|
|
print("[Download Failed] response code " .. code .. " is not 200.")
|
2018-11-21 12:08:25 +08:00
|
|
|
return
|
2018-11-21 11:55:45 +08:00
|
|
|
else
|
|
|
|
if(type(v)=="string") then
|
|
|
|
local f=io.open(v,"w")
|
|
|
|
if(f==nil) then
|
|
|
|
print("[Error] Unable to write to file " .. v)
|
|
|
|
return
|
|
|
|
else
|
|
|
|
f:write(result)
|
|
|
|
f:close()
|
|
|
|
print("[OK]")
|
|
|
|
end
|
|
|
|
elseif(type(v)=="table") then
|
|
|
|
local success=false
|
|
|
|
for idx,value in ipairs(v) do
|
|
|
|
local f=io.open(value,"w")
|
|
|
|
if(f) then
|
|
|
|
success=true
|
|
|
|
f:write(result)
|
|
|
|
f:close()
|
|
|
|
print("[OK]")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if(not success) then
|
|
|
|
print("[Error] Unable to write file: " .. toDownload)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-11-24 01:29:56 +08:00
|
|
|
print("Fetched " .. count_files .. " files in " .. string.format("%.1f",computer.uptime()-time_before) .. " seconds.")
|
2018-11-21 12:24:10 +08:00
|
|
|
print("Installing...")
|
2018-11-24 01:29:56 +08:00
|
|
|
for this_lib in pairs(to_install) do
|
|
|
|
if(db[this_lib].installer) then
|
|
|
|
print("Running installer for " .. this_lib .. "...")
|
|
|
|
os.execute(db[this_lib].installer)
|
|
|
|
end
|
|
|
|
end
|
2018-11-21 12:24:10 +08:00
|
|
|
print("Installed " .. count_libs .. " libraies with " .. count_files .. " files.")
|
2018-11-21 11:55:45 +08:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
if(args[1]=="list") then
|
2018-11-24 01:29:56 +08:00
|
|
|
if(not check_db()) then return end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
print("Listing projects...")
|
2018-11-21 11:58:26 +08:00
|
|
|
for this_lib in pairs(db) do
|
|
|
|
print(this_lib)
|
2018-11-21 11:55:45 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2018-11-24 01:29:56 +08:00
|
|
|
if(args[1]=="show") then
|
|
|
|
if(not check_db()) then return end
|
|
|
|
if(#args<2) then
|
|
|
|
print("Nothing to show.")
|
|
|
|
end
|
|
|
|
|
|
|
|
if(db[args[2]]) then
|
|
|
|
local this_info=db[args[2]]
|
|
|
|
print("Name: " .. args[2])
|
|
|
|
print("Title: " .. db[args[2]].title)
|
|
|
|
print("Info:\n\t" .. db[args[2]].info)
|
|
|
|
|
|
|
|
local nFiles=0
|
|
|
|
for k,v in pairs(db[args[2]].files) do nFiles=nFiles+1 end
|
|
|
|
print("Files: " .. nFiles)
|
|
|
|
else
|
|
|
|
print("Library " .. args[2] .. " not found.")
|
|
|
|
end
|
|
|
|
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2018-11-24 00:49:01 +08:00
|
|
|
if(args[1]=="download") then
|
|
|
|
if(#args<2) then
|
|
|
|
print("Nothing to download.")
|
|
|
|
return
|
|
|
|
else
|
|
|
|
if(not check_internet()) then return end
|
|
|
|
print("Collecting files...")
|
|
|
|
end
|
|
|
|
|
|
|
|
local files={}
|
|
|
|
for i=2,#args,1 do
|
|
|
|
table.insert(files,args[i])
|
|
|
|
end
|
|
|
|
|
|
|
|
for i=1,#files,1 do
|
|
|
|
io.write("[" .. i .. "/" .. #files .. "] Downloading " .. files[i] .. "...")
|
|
|
|
local ok,result,code=download(UrlGenerator("Kiritow/OpenComputerScripts","master",files[i]))
|
|
|
|
if(not ok) then
|
|
|
|
print("[Download Failed] " .. result)
|
|
|
|
return
|
|
|
|
elseif(code~=200) then
|
|
|
|
print("[Download Failed] response code " .. code .. " is not 200.")
|
|
|
|
return
|
|
|
|
else
|
|
|
|
local f,ferr=io.open(files[i],"w")
|
|
|
|
if(not f) then
|
|
|
|
print("[Write Failed] Unable to write. Error:" .. ferr)
|
|
|
|
else
|
|
|
|
f:write(result)
|
|
|
|
f:close()
|
|
|
|
print("[OK]")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
-- reach here?
|
2018-11-24 00:49:01 +08:00
|
|
|
if(#args<1) then
|
|
|
|
show_usage()
|
|
|
|
else
|
|
|
|
print("Unknown command: " .. args[1])
|
|
|
|
end
|