Last active
May 1, 2019 07:55
-
-
Save hugeblank/f3259ddf7e4e20b465e34a9f77da2bef to your computer and use it in GitHub Desktop.
Temporary gist so I have a chance to better the mess that is this code. Flame broil it!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ Depman by hugeblank | |
Simple and free to use dependency managment framework to be packaged with your software. | |
The end user is not only allowed to modify the code below, but is encouraged to. | |
Please do not modify this comment block, but do credit yourself and any other | |
contributors in the box below. | |
]] | |
--[[ Depman Instance Information | |
Maintained by: | |
Modified for: | |
]] | |
--## Argument Evaluation ##-- | |
local args = {...} | |
if #args < 5 then | |
print("Usage: depman <task> <list URL> <config path> <dep dir path> <application version>") | |
return | |
end | |
local request, url, path, lib_path, version, save_semver = table.unpack(args) | |
--## Dependency Loading ##-- | |
local semver | |
do -- Block to keep request value private | |
local req = http.get("https://raw.github.com/hugeblank/semparse/master/semver.lua") | |
if not req then error("Could not download semver, try again later") end | |
if save_semver == "true" then | |
local file = fs.open("semver.lua", "w") | |
file.write(req.readAll()) | |
file.close() | |
end | |
if not fs.exists("semver.lua") then | |
semver = load(req.readAll())() | |
req.close() | |
else | |
semver = require("semver") | |
end | |
end | |
--## Local Function Definitions ##-- | |
local pullTable | |
do -- Block to keep cache value private | |
local cache | |
function pullTable() -- Downloads the package listing from online | |
if cache then -- If the the listing has already been downloaded | |
return cache -- Spit it to 'em | |
end | |
if not http.checkURL(url) then -- Check for the existence of the URL | |
error("Could not download dependency data") | |
end | |
local contents, file = http.get(url) -- Get the contents | |
local out = textutils.unserialise(contents.readAll()) -- Read it all | |
if not out then | |
error("Could not parse dependency data") | |
end | |
cache = out -- Put the output into the cache for later calls | |
contents.close() -- Close it all | |
return out -- Return it all. nuff said. | |
end | |
end | |
local function getTable() -- Provides the internal dependency listing | |
if not fs.exists(path) then -- If the listing file doesn't exist, return a blank table | |
return {} | |
end | |
local file = fs.open(path, "r") -- Read from current package listing | |
local out = textutils.unserialise(file.readAll()) -- Read out the contents | |
file.close() -- Close the file | |
if not out then -- If it couldn't be unserialized, error | |
error("Could not parse dependency data") | |
else -- OTHERWISE | |
return out -- return the table of currently installed deps | |
end | |
end | |
local function applyData(name, meta) -- Set data within the local dependency listing | |
local data = getTable() -- Get currently installed deps | |
data[name] = meta -- Add the new index | |
local file = fs.open(path, "w") -- Open the listing file | |
if not file then -- If it can't be opened for writing | |
error("Could not save dependeny data") | |
end | |
file.write(textutils.serialise(data)) -- Write the serialized listing | |
file.close() -- Close | |
return true -- Mission accomplished | |
end | |
local function checkVersions(versions) -- Check versions within a range, comparison, or explicit list | |
for _, vstr in pairs(versions) do | |
local range = vstr:find("&&") -- Matched a range definition | |
local comp, c_e = vstr:find("[><][=]*") -- I do love me some pattern matching | |
local function convert(str) -- Use the semver API to convert. Provide a detailled error if conversion fails | |
if type(str) ~= "string" then | |
error("Could not convert "..tostring(str)) | |
end | |
local ver, rule = semver.parse(str:gsub("%s", "")) | |
if not ver then | |
error("Could not parse "..str:gsub("%s", "")..", breaks semver spec rule "..rule) | |
end | |
return ver | |
end | |
local version = convert(version) -- Duplicates the version of the main program, lowering the scope so we can parse it | |
local function compare(in_str) -- compare version provided in string to input versions, using the operator provided | |
local _, split = in_str:find("[><][=]*") | |
local lim, op, res = convert(in_str:sub(split+1)), in_str:sub(1, split), nil -- Split operator and version string | |
if op == ">" then | |
res = version > lim | |
elseif op == "<" then | |
res = version < lim | |
elseif op == ">=" then | |
res = version >= lim | |
elseif op == "<=" then | |
res = version <= lim | |
end | |
return res | |
end | |
if range then -- If there's a range beginning definition | |
local a, b = compare(vstr:sub(1, range-1)), compare(vstr:sub(range+3, -1)) | |
if a and b then | |
return true | |
end | |
elseif comp then -- Otherwise if there's a comparison operator | |
if compare(lim) then | |
return true | |
end | |
elseif convert(vstr) == version then -- Otherwise this is a simple list element | |
return true | |
end | |
end | |
return false | |
end | |
local function filterDeps() -- Filter dependencies that have matching versions and need updating | |
local data, cur_data = pullTable(), getTable() -- Get the online query table, and current listing table | |
local out = {} | |
for i = 1, #data do -- For each dependency in the table | |
local name = data[i].path -- Set a special variable for the path | |
local valid = checkVersions(data[i].versions) | |
if valid then -- If the version supported matches the inputted one | |
if cur_data[name] then -- and this dep exists internally | |
-- Let's download/open the remote/local sources | |
local code_response, code_file = http.get(data[i].source), fs.open(fs.combine(lib_path, name), "r") | |
if code_response and code_file then -- If we opened both of them | |
local code_remote, code_local = code_response.readAll(), code_file.readAll() -- Read them | |
code_response.close() code_file.close() | |
if code_remote ~= code_local then -- If they aren't equal to each other | |
out[name] = data[i] -- Add it to the list | |
end | |
elseif not code_response then -- If we didn't get an http response | |
error("Could not read remote source "..name) | |
elseif not code_file then -- If we didn't open an existing file | |
error("Could not read local source "..name) | |
end | |
else -- OTHERWISE | |
out[name] = data[i] -- Add it anyways | |
end | |
end | |
end | |
return out -- Fin. | |
end | |
--## Task Definitions ##-- | |
local tasks = {} -- Write tasks below this point | |
tasks.update = function() -- The generic update task, made for you | |
local depmeta = filterDeps() -- Get all dependencies to be updated | |
for name, meta in pairs(depmeta) do -- For each dependency | |
if not http.checkURL(meta.source) then -- Check that the source is valid | |
printError("Could not locate dependency "..name) -- Mention it couldn't be found then move on | |
end | |
local contents, file = http.get(meta.source), fs.open(fs.combine(lib_path, name), "w") | |
-- Download the library, and open the file to dump it in | |
if not file then -- If it couldn't be opened | |
error("Could not write dependency "..name) -- Crash and burn | |
end | |
file.write(contents.readAll()) -- Write the library to the file | |
meta.path, meta.versions = nil, nil -- Remove the unnecessary contents in the dependency metadata | |
applyData(name, meta) -- Apply that data to the dependency listing file | |
end | |
return true -- Job done. | |
end | |
tasks.clean = function() -- Clean up dependencies that are no longer used | |
local function find(tbl, str) -- Hunt down a match of a string within a table | |
for i = 1, #tbl do -- For each index | |
if tbl[i] == str then -- If the index matches the string | |
return true -- Return triumphantly | |
end | |
end | |
return false -- Return defeated | |
end | |
local depmeta, listing = pullTable(), getTable() -- Get dependency listing online, and internally | |
local delet = {} | |
for i = 1, #depmeta do -- For each dependency | |
if not find(depmeta[i].versions, version) then -- If the version can't be found within that dep | |
delet[#delet+1] = fs.combine(lib_path, depmeta[i].path) -- Send it to the ranch | |
end | |
end | |
for i = 1, #delet do -- For each thing within the ranch | |
if delet[i] then | |
if fs.exists(delet[i]) then -- If it can be removed | |
fs.delete(delet[i]) -- Get rid of it | |
applyData(delet[i]:gsub(lib_path, ""), nil) -- Remove it from the fr*cking internal listing | |
end | |
end | |
end | |
end | |
local action = tasks[request] -- Set action to the index within the task list corresponding to a task | |
if action then -- If it's actually a task | |
action() -- Fulfill it | |
else -- OTHERWISE | |
print("Invalid task "..request) -- Let's tell them that they goofed | |
print("List of valid tasks: ") -- And then list all the tasks that are availible | |
for name in pairs(tasks) do | |
print("- "..name) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment