diff --git a/ag2/registry.json b/ag2/registry.json index b81d3a3..ba4e6f9 100644 --- a/ag2/registry.json +++ b/ag2/registry.json @@ -1,5 +1,7 @@ { "halyde": "https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/Pre-Alpha-3.0.0/", "argentum": "https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/Pre-Alpha-3.0.0/", - "edit": "https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/Pre-Alpha-3.0.0/" + "edit": "https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/Pre-Alpha-3.0.0/", + "package1": "https://raw.githubusercontent.com/WahPlus/ArgentumPackages/refs/heads/main/", + "package2": "https://raw.githubusercontent.com/WahPlus/ArgentumPackages/refs/heads/main/" } diff --git a/halyde/apps/ag2.lua b/halyde/apps/ag2.lua new file mode 100644 index 0000000..b3f62c6 --- /dev/null +++ b/halyde/apps/ag2.lua @@ -0,0 +1,320 @@ +local cliparse = require("cliparse") +local fs = require("filesystem") +local component = require("component") +local json = require("json") + +local function getFile(path) + checkArg(1, path, "string") + if path:sub(1, 7) == "http://" or path:sub(1, 8) == "https://" then + if not component.list("internet")() then + return false, "Internet card required but not found." + end + local handle, data, tmpdata = component.internet.request(path), "", nil + local success, errorMessage = pcall(function() + handle:finishConnect() + end) + if not success then + return false, errorMessage + end + local code, message = handle:response() + if code and code ~= 200 then + return false, ("%d %s"):format(code, message) + end + repeat + tmpdata = handle.read(math.huge or math.maxinteger) + data = data .. (tmpdata or "") + until not tmpdata + return true, data + elseif path:sub(1, 1) == "/" then + if not fs.exists(path) then + return false, "No such file or directory: " .. path + end + if fs.isDirectory(path) then + return false, "Expected file, found directory: " .. path + end + local handle, data, tmpdata = fs.open(path, "r", false), "", nil + if not handle then + return false, data + end + repeat + tmpdata = handle:read(math.huge or math.maxinteger) + data = data .. (tmpdata or "") + until not tmpdata + return true, data + else + return false, "Unsupported path: " .. path + end +end + +cliparse.config({ + ["x"] = 0, + ["exclude-deps"] = 0, + ["u"] = 0, + ["update-registry"] = 0, + ["f"] = 0, + ["force"] = 0, + ["c"] = 0, + ["clean"] = 0, + ["s"] = 1, + ["source"] = 1 +}) + +local parsed, errorMessage = cliparse.parse(...) + +if not parsed then + print(("\27[91m%s\n\27[0mExiting."):format(errorMessage)) + return +end + +local command = parsed.args[1] + +if not command then + print("\27[91mNo command specified.\n\27[0mExiting.") + return +end + +if + not ( + command == "install" + or command == "remove" + or command == "update" + or command == "list" + or command == "repo-list" + or command == "repo-add" + or command == "repo-remove" + or command == "info" + ) +then + print(("\27[91mInvalid command: %s\n\27[0mExiting."):format(command)) + return +end + +local packages = parsed.args +table.remove(packages, 1) +-- Remove the command from the actual package list +local result, data +if parsed.flags.u or parsed.flags["update-registry"] then + terminal.write("Updating registry...") + result, data = getFile("https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/Pre-Alpha-3.0.0/ag2/registry.json") + if not result then + print(("\27[91mFailed to get registry: %s\n\27[0mExiting."):format(data)) + return + end + local handle, errorMessage = fs.open("/ag2/registry.json", "w") + if not handle then + print(("\27[91mFailed to open write handle to registry: %s\n\27[0mExiting."):format(errorMessage)) + return + end + local success, errorMessage = handle:write(data) + if not success then + print(("\27[91mFailed to write to registry: %s\n\27[0mExiting."):format(errorMessage)) + return + end + handle:close() +else + result, data = getFile("/ag2/registry.json") + if not result then + print(("\27[91mFailed to get registry: %s\n\27[0mExiting."):format(data)) + return + end +end + +local success, registry = pcall(function() + return json.decode(data) +end) +if not success then + print(("\27[91mFailed to parse registry: %s\n\27[0mExiting."):format(registry)) + return +end + +-- Check if everything is valid +local failure = false +local dependencyCounter = 0 +if command == "install" then + if #packages == 0 then + print("\27[91mNo packages specified.\n\27[0mExiting.") + return + end + for i, package in ipairs(packages) do + if not registry[package] then + print("\27[91mCould not find package in registry: " .. package) + failure = true + goto SKIP + end + local success, data = getFile(fs.concat(registry[package], "/ag2.json")) + -- TODO: Make the above compatible with --source + if not success then + print(("\27[91mFailed to get package config (ag2.json) of package '%s': " .. data):format(package)) + failure = true + goto SKIP + end + local success, packageConfig = pcall(function() + return json.decode(data) + end) + if not success then + print(("\27[91mFailed to parse package config (ag2.json) of package '%s': " .. packageConfig):format(package)) + failure = true + goto SKIP + end + if not packageConfig[package] then + print(("\27[91mRepository package config (ag2.json) does not contain package '%s'."):format(package)) + failure = true + goto SKIP + end + packageConfig = packageConfig[package] + if packageConfig.dependencies then + for _, dependency in ipairs(packageConfig.dependencies) do + table.insert(packages, i + 1, dependency) + dependencyCounter = dependencyCounter + 1 + end + end + -- TODO: Add check for if package exists + ::SKIP:: + end +end +-- TODO: Add checks for the other commands + +if failure then + print("Exiting.") + return +end + +if command == "install" then + if dependencyCounter == 1 then + print("\27[93m1 dependency pulled in.") + elseif dependencyCounter >= 2 then + print(("\27[93m%d dependencies pulled in."):format(dependencyCounter)) + end + print("Packages that will be installed:") + print(table.concat(packages)) + local answer = terminal.read({prefix = "\nContinue? [Y/n] "}) + if answer:lower() == "n" then + print("Exiting.") + return + end + + for _, package in ipairs(packages) do + print(("Installing %s..."):format(package)) + local _, data = getFile(fs.concat(registry[package], "/ag2.json")) + -- TODO: Make the above compatible with --source + local packageConfig = json.decode(data)[package] + if packageConfig.directories then + for _, directory in ipairs(packageConfig.directories) do + print((" Creating directory %s..."):format(directory)) + fs.makeDirectory(directory) + end + end + if packageConfig.files then + for _, file in ipairs(packageConfig.files) do + ::RETRY:: + print((" Downloading file %s..."):format(file)) + local success, data = getFile(fs.concat(registry[package], file)) + + if not success then + print(("\27[91mFailed to get file '%s' of package '%s': " .. data):format(file, package)) + local answer = terminal.read({prefix = "Abort, Retry, Skip? [a/R/s]"}) + if answer:lower() == "a" then + print("Exiting.") + return + elseif answer:lower() == "s" then + print((" \27[93mSkipped file %s."):format(file)) + goto SKIP + else + goto RETRY + end + end + + if fs.exists(file) then + print(("\27[93mFile '%s' already exists."):format(file)) + local answer = terminal.read({prefix = "Abort, Overwrite, Skip? [a/O/s]"}) + if answer:lower() == "a" then + print("Exiting.") + return + elseif answer:lower() == "s" then + print((" \27[93mSkipped file %s."):format(file)) + goto SKIP + end + end + + local handle, errorMessage = fs.open(file, "w") + if not handle then + print(("\27[91mFailed to open write handle to file '%s': " .. errorMessage):format(file)) + local answer = terminal.read({prefix = "Abort, Retry, Skip? [a/R/s]"}) + if answer:lower() == "a" then + print("Exiting.") + return + elseif answer:lower() == "s" then + print((" \27[93mSkipped file %s."):format(file)) + goto SKIP + else + goto RETRY + end + end + + local success, errorMessage = handle:write(data) + if not success then + handle:close() + print(("\27[91mFailed to write to file '%s': " .. errorMessage):format(file)) + local answer = terminal.read({prefix = "Abort, Retry, Skip? [a/R/s]"}) + if answer:lower() == "a" then + print("Exiting.") + return + elseif answer:lower() == "s" then + print((" \27[93mSkipped file %s."):format(file)) + goto SKIP + else + goto RETRY + end + end + + handle:close() + + ::SKIP:: + end + end + + print(" Writing tracking file...") + if not fs.exists("/ag2/store/") then + fs.makeDirectory("/ag2/store/") + -- Technically this would break if /ag2/store/ was a file, but... why would it be a file? + end + + -- TODO: Make functions for reading from and writing to a file with error handling since this is really repetitive + ::RETRY:: + local handle, errorMessage = fs.open(("/ag2/store/%s.json"):format(package), "w") + if not handle then + print(("\27[91mFailed to open write handle to file '/ag2/store/%s.json': " .. errorMessage):format(package)) + local answer = terminal.read({prefix = "Abort, Retry, Skip? [a/R/s]"}) + if answer:lower() == "a" then + print("Exiting.") + return + elseif answer:lower() == "s" then + print((" \27[93mSkipped file /ag2/store/%s.json."):format(package)) + goto SKIP + else + goto RETRY + end + end + + local success, errorMessage = handle:write(data) + if not success then + handle:close() + print(("\27[91mFailed to write to file '/ag2/store/%s.json': " .. errorMessage):format(package)) + local answer = terminal.read({prefix = "Abort, Retry, Skip? [a/R/s]"}) + if answer:lower() == "a" then + print("Exiting.") + return + elseif answer:lower() == "s" then + print((" \27[93mSkipped file /ag2/store/%s.json."):format(package)) + goto SKIP + else + goto RETRY + end + end + + handle:close() + + ::SKIP:: + end +end +print("Operation completed successfully.") diff --git a/halyde/apps/helpdb/ag2.txt b/halyde/apps/helpdb/ag2.txt new file mode 100644 index 0000000..f519ee2 --- /dev/null +++ b/halyde/apps/helpdb/ag2.txt @@ -0,0 +1,31 @@ +Usage: ag2 [COMMAND] [PACKAGES] +Uses the Argentum 2 package manager. + + COMMAND Specifies the operation for Argentum 2 to do. + install Installs packages. + remove Removes packages. + update Updates packages. + list Lists all available packages. + repo-list Lists all installed repositories. + repo-add Adds a custom repository. + repo-remove Removes a repository. + info Shows a packages version, description and other relevant information. + PACKAGES* Packages to work on. + + These flags are also available and can be inserted anywhere: + -x, --exclude-deps Ignore dependencies. + WARNING: Using this can and will leave you with broken packages. + Use it at your own risk and only when truly necessary. + -u, --update-repos Update the list of repositories. + -f, --force Force the operation, even if there are conflicts or unresolvable dependencies. + WARNING: Using this can and will leave you with broken packages. + Use it at your own risk and only when truly necessary. + -c, --clean Clean up now-unnecessary packages (previous dependencies). + -s, --source [URL] Use a custom source for the operation. + +Examples: + ag2 install halyde Installs the halyde package. + ag2 list Lists all packages. + ag2 info halyde Shows information about the halyde package. + ag2 remove -x edit Removes edit, but does not remove any packages that depend on it. + ag2 remove -c hal-draw Removes hal-draw and any dependencies that are no longer needed. diff --git a/p1f1 b/p1f1 new file mode 100644 index 0000000..4a31856 --- /dev/null +++ b/p1f1 @@ -0,0 +1 @@ +Package 1 File 1 v1.0.0 diff --git a/p1f2 b/p1f2 new file mode 100644 index 0000000..dd60693 --- /dev/null +++ b/p1f2 @@ -0,0 +1 @@ +Package 1 File 2 v1.0.0