From ba466e7549cbfce519f924f0d66520da481e7650 Mon Sep 17 00:00:00 2001 From: SpookyDervish Date: Sun, 18 Jan 2026 21:26:23 +1100 Subject: [PATCH] added publish and remove commands --- request/mineral.ini | 5 ++++ src/install.py | 16 ++++++---- src/main.py | 15 ++++++++++ src/mineral.py | 28 ------------------ src/publish.py | 72 +++++++++++++++++++++++++++++++++++++++++++++ src/remove.py | 45 ++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+), 33 deletions(-) create mode 100644 request/mineral.ini delete mode 100644 src/mineral.py create mode 100644 src/publish.py create mode 100644 src/remove.py diff --git a/request/mineral.ini b/request/mineral.ini new file mode 100644 index 0000000..6cc4230 --- /dev/null +++ b/request/mineral.ini @@ -0,0 +1,5 @@ +[package] +description=A library to make HTTP requests. +version=1.0.0 + +[dependencies] \ No newline at end of file diff --git a/src/install.py b/src/install.py index be90993..000692b 100644 --- a/src/install.py +++ b/src/install.py @@ -51,14 +51,20 @@ def install(args): continue + response.raise_for_status() + break # create temporary file for tarball - f = tempfile.TemporaryFile("wb+") - f.write(response.content) - f.flush() - f.seek(0) - console.print("[d][:white_check_mark:] Tarball downloaded![/]") + try: + f = tempfile.TemporaryFile("wb+") + f.write(response.content) + f.flush() + f.seek(0) + console.print("[d][:white_check_mark:] Tarball downloaded![/]") + except KeyboardInterrupt: + console.print("[b yellow]digpkg: operation cancelled by user[/]") + return # extract the tarball to the GROUND_LIBS folder status.update("Extracting...") diff --git a/src/main.py b/src/main.py index 35fb961..94d95e8 100644 --- a/src/main.py +++ b/src/main.py @@ -2,6 +2,8 @@ import argparse import os, sys from install import install +from publish import publish +from remove import remove def parse_arguments(): @@ -22,6 +24,15 @@ def parse_arguments(): list_command = sub_parsers.add_parser(name="list", description="list all minerals installed in the current environment") list_command.add_argument("--env_name", help="list all minerals from a specific environment.", default=None, required=False) + # publish command + publish_command = sub_parsers.add_parser(name="publish", description="publish a package to the repository") + publish_command.add_argument("name", help="name and version of the package") + publish_command.add_argument("folder_path", help="path to the folder that will be uploaded") + + # remove command + remove_command = sub_parsers.add_parser(name="remove", description="remove a published package from the repository") + remove_command.add_argument("name", help="name and version of the package") + # env command """env_command = sub_parsers.add_parser(name="env", description="manage Ground environments") env_sub_parsers = env_command.add_subparsers(dest="env_command") @@ -48,6 +59,10 @@ def parse_arguments(): if args.command == "install": install(args) + elif args.command == "publish": + publish(args) + elif args.command == "remove": + remove(args) def main(): parse_arguments() diff --git a/src/mineral.py b/src/mineral.py deleted file mode 100644 index 8ac7cb6..0000000 --- a/src/mineral.py +++ /dev/null @@ -1,28 +0,0 @@ -import configparser -import os, sys -from rich import print - - -class Mineral: - def __init__(self, mineral_name: str, ground_folder_path: str, env_name: str): - self.folder_path = os.path.join(ground_folder_path, env_name, mineral_name) - self.config_path = os.path.join(self.folder_path, "mineral.ini") - self.name = mineral_name - - if not os.path.isdir(self.folder_path): # mineral does not exist on system - print(f"[b red]digpkg: error: folder \"{self.folder_path}\" does not exist![/]") - sys.exit(1) - - if not os.path.isfile(self.config_path): # mineral has no file inside it named "mineral.ini" - print(f"[b red]digpkg: error: the mineral \"{self.name}\" has no config.ini file![/]") - sys.exit(1) - - self.config_parser = configparser.ConfigParser() - self.config_parser.read(self.config_path) - - self.description = self.config_parser.get("package", "description") - self.version = self.config_parser.get("package", "version") - - self.dependencies = dict(self.config_parser["dependencies"]) - - print(self.description) \ No newline at end of file diff --git a/src/publish.py b/src/publish.py new file mode 100644 index 0000000..b5a9d53 --- /dev/null +++ b/src/publish.py @@ -0,0 +1,72 @@ +import tarfile +import tempfile +import os, sys + +import requests +from requests.auth import HTTPBasicAuth + +from rich.console import Console +from rich.prompt import Prompt + + +console = Console() + + +def publish(args): + if not "@" in args.name: + console.print(f"[b red]digpkg: failed to publish mineral: please include the version number in the package name. e.g: request@1.0.0") + sys.exit(1) + + split_name = args.name.split("@") + mineral_name = split_name[0] + version = split_name[1] + + # sanity checks + if not os.path.isdir(args.folder_path): + console.print(f"[b red]digpkg: failed to publish mineral: \"{args.folder_path}\" is not a directory") + sys.exit(1) + if not os.path.isfile(os.path.join(args.folder_path, "mineral.ini")): + console.print(f"[b red]digpkg: failed to publish mineral: mineral has no \"mineral.ini\" file") + sys.exit(1) + + # ask for user and pass + console.print("[b]Please authenticate.\n[/]") + try: + username = Prompt.ask("Username", console=console) + password = Prompt.ask("Password (or PAT)", console=console, password=True) + except KeyboardInterrupt: + return + + console.print() + + with console.status("Compressing...", spinner="bouncingBall", spinner_style="blue") as status: + # compress to a tar file + with tempfile.TemporaryFile(mode="wb+") as f: + tar_file = tarfile.open(fileobj=f, mode="w:gz") + tar_file.add(args.folder_path, arcname=os.path.basename(args.folder_path)) + + console.print("[d][:white_check_mark:] Compressed![/]") + + # send the request + status.update("Uploading...") + response = requests.put( + url=f"https://chookspace.com/api/packages/{username}/generic/{mineral_name}/{version}/mineral.tar", + data=f, + auth=HTTPBasicAuth(username, password) + ) + + tar_file.close() + + if response.status_code == 401: + console.print("[b red]digpkg: failed to publish mineral: authentication failed[/]") + sys.exit(1) + elif response.status_code == 400: + console.print("[b red]digpkg: failed to publish mineral: the package name or version number are invalid[/]") + sys.exit(1) + elif response.status_code == 409: + console.print("[b red]digpkg: failed to publish mineral: that version number is already in use[/]") + sys.exit(1) + + response.raise_for_status() + console.print("[d][:white_check_mark:] Uploaded![/]") + \ No newline at end of file diff --git a/src/remove.py b/src/remove.py new file mode 100644 index 0000000..9b50663 --- /dev/null +++ b/src/remove.py @@ -0,0 +1,45 @@ +import requests +import sys +from requests.auth import HTTPBasicAuth + +from rich.console import Console +from rich.prompt import Prompt + + +console = Console() + + +def remove(args): + if not "@" in args.name: + console.print(f"[b red]digpkg: failed to publish mineral: please include the version number in the package name. e.g: request@1.0.0") + sys.exit(1) + + split_name = args.name.split("@") + mineral_name = split_name[0] + version = split_name[1] + + # ask for user and pass + console.print("[b]Please authenticate.\n[/]") + try: + username = Prompt.ask("Username", console=console) + password = Prompt.ask("Password (or PAT)", console=console, password=True) + except KeyboardInterrupt: + return + + console.print() + + # send the request + response = requests.delete( + url=f"https://chookspace.com/api/packages/{username}/generic/{mineral_name}/{version}", + auth=HTTPBasicAuth(username, password) + ) + + if response.status_code == 404: + console.print("[b red]digpkg: failed to remove mineral: mineral name or version was not found[/]") + sys.exit(1) + elif response.status_code == 401: + console.print("[b red]digpkg: failed to remove mineral: authentication failed[/]") + sys.exit(1) + + response.raise_for_status() + console.print("[d][:white_check_mark:] Success![/]") \ No newline at end of file