Compare commits

...

6 Commits

7 changed files with 156 additions and 53 deletions

View File

@@ -6,36 +6,19 @@ import tarfile
from rich.console import Console from rich.console import Console
from rich.progress import SpinnerColumn from rich.progress import SpinnerColumn
from util import check_ground_libs_path, check_sudo
console = Console() console = Console()
def install(args): def install_package(package_name, version, args):
# check if we are sudo
if os.getuid() != 0:
console.print("[b red]digpkg: the install command requires sudo to run[/]")
sys.exit(1)
# ensure the GROUND_LIBS var is set
if not os.getenv("GROUND_LIBS"):
console.print("digpkg: the [i]GROUND_LIBS[/] environment variable is not set, defaulting to /usr/lib/ground/")
os.environ["GROUND_LIBS"] = "/usr/lib/ground/"
# figure out which version to install
package_name = args.name
version = "latest"
if "@" in args.name:
split = package_name.split("@")
package_name = split[0]
version = split[1]
retries_left = args.max_retries retries_left = args.max_retries
with console.status("Downloading tarball...", spinner="bouncingBall", spinner_style="blue") as status: with console.status("Downloading tarball...", spinner="bouncingBall", spinner_style="blue") as status:
while retries_left > 0: while retries_left > 0:
# grab the tar ball # grab the tar ball
response = requests.get(f"https://chookspace.com/api/packages/SpookyDervish/generic/{package_name}/{version}/mineral.tar") response = requests.get(f"https://chookspace.com/api/packages/ground/generic/{package_name}/{version}/mineral.tar")
# check response code for errors # check response code for errors
if response.status_code == 404: # package doesn't exist if response.status_code == 404: # package doesn't exist
@@ -79,3 +62,28 @@ def install(args):
f.close() f.close()
console.print(f"[d][:white_check_mark:] Extracted to {extract_dir}.") console.print(f"[d][:white_check_mark:] Extracted to {extract_dir}.")
console.status("Finishing up...")
# create a symlink from the main.so file to the ground libs folder so ground can find it
symlink_path = os.path.join(extract_dir, f"{package_name}.so") # the path where the symlink is
if not os.path.isfile(symlink_path):
os.symlink(os.path.join(extract_dir, package_name, "main.so"), symlink_path)
console.print("[:white_check_mark:] Done!")
def install(args):
check_sudo()
check_ground_libs_path()
for package in args.names:
# figure out which version to install
package_name = package
version = "latest"
if "@" in package:
split = package.split("@")
package_name = split[0]
version = split[1]
install_package(package_name, version, args)

38
src/list.py Normal file
View File

@@ -0,0 +1,38 @@
import os
import configparser
from rich import print
from rich.table import Table
from util import check_ground_libs_path
def list_cmd(args):
check_ground_libs_path()
ground_libs_folder = os.getenv("GROUND_LIBS")
folders = os.listdir(ground_libs_folder)
table = Table("Name", "Version", "Description", title="Installed")
config_parser = configparser.ConfigParser()
for folder in folders:
full_path = os.path.join(ground_libs_folder, folder)
# skip anything that isnt a folder
if not os.path.isdir(full_path):
continue
# read the mineral.ini file to figure out the version and description
ini_path = os.path.join(full_path, "mineral.ini")
if not os.path.isfile(ini_path):
continue
config_parser.read(ini_path)
table.add_row(
f"[b]{folder}",
f"[blue]{config_parser.get('package', 'version')}",
config_parser.get("package", "description"),
)
print(table)

View File

@@ -4,6 +4,8 @@ import os, sys
from install import install from install import install
from publish import publish from publish import publish
from remove import remove from remove import remove
from list import list_cmd
from uninstall import uninstall
def parse_arguments(): def parse_arguments():
@@ -13,7 +15,7 @@ def parse_arguments():
# install command # install command
install_command = sub_parsers.add_parser(name="install", description="install a mineral") install_command = sub_parsers.add_parser(name="install", description="install a mineral")
install_command.add_argument("name", help="name of the mineral to install") install_command.add_argument("names", help="name of the minerals to install", nargs="+")
install_command.add_argument("--max-retries", help="max number of download retries before giving up", default=3, type=int) install_command.add_argument("--max-retries", help="max number of download retries before giving up", default=3, type=int)
# uninstall command # uninstall command
@@ -22,7 +24,6 @@ def parse_arguments():
# list command # list command
list_command = sub_parsers.add_parser(name="list", description="list all minerals installed in the current environment") 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
publish_command = sub_parsers.add_parser(name="publish", description="publish a package to the repository") publish_command = sub_parsers.add_parser(name="publish", description="publish a package to the repository")
@@ -34,28 +35,12 @@ def parse_arguments():
remove_command.add_argument("name", help="name and version of the package") remove_command.add_argument("name", help="name and version of the package")
# env command # env command
"""env_command = sub_parsers.add_parser(name="env", description="manage Ground environments")
env_sub_parsers = env_command.add_subparsers(dest="env_command")
env_new_command = env_sub_parsers.add_parser(name="new", description="create a new environment")
env_new_command.add_argument("name", help="name of the new environment")
env_new_command.add_argument("--dont-use", help="by default, the newly created environment will be set as the current environment. if this argument is parsed, then it will be created but not activated.", action="store_true")
env_destroy_command = env_sub_parsers.add_parser(name="destroy", description="delete an environment")
env_destroy_command.add_argument("name", help="name of the environment to DESTROYYY")
env_destroy_command.add_argument("--confirm-yes", action="store_true", help="don't ask for confirmation (DANGEROUS)")
env_list_command = env_sub_parsers.add_parser(name="list", description="list all environments")"""
args = arg_parser.parse_args() args = arg_parser.parse_args()
if not args.command: if not args.command:
arg_parser.print_help() arg_parser.print_help()
sys.exit(0) sys.exit(0)
if args.command == "env" and not args.env_command:
env_command.print_help()
sys.exit(0)
if args.command == "install": if args.command == "install":
install(args) install(args)
@@ -63,6 +48,10 @@ def parse_arguments():
publish(args) publish(args)
elif args.command == "remove": elif args.command == "remove":
remove(args) remove(args)
elif args.command == "list":
list_cmd(args)
elif args.command == "uninstall":
uninstall(args)
def main(): def main():
parse_arguments() parse_arguments()

View File

@@ -39,8 +39,21 @@ def publish(args):
console.print() console.print()
with console.status("Compressing...", spinner="bouncingBall", spinner_style="blue") as status: with console.status("Authenticating...", spinner="bouncingBall", spinner_style="blue") as status:
# check if we have permission to link the package to the repo
repo_perms_request = requests.get(
url=f"https://chookspace.com/api/v1/users/{username}/orgs/ground/permissions",
auth=HTTPBasicAuth(username, password)
)
if repo_perms_request.status_code == 401:
console.print(f"[b red]digpkg: failed to publish mineral: checking authorization failed: invalid password[/b red]")
sys.exit(1)
elif not repo_perms_request.ok:
console.print(f"[b red]digpkg: failed to publish mineral: checking authorization failed: {repo_perms_request.content.decode()}[/b red]")
sys.exit(1)
# compress to a tar file # compress to a tar file
console.status("Compressing")
f = tempfile.TemporaryFile(mode="wb+") f = tempfile.TemporaryFile(mode="wb+")
with tarfile.open(fileobj=f, mode="w:gz") as tar_file: with tarfile.open(fileobj=f, mode="w:gz") as tar_file:
tar_file.add(args.folder_path, arcname=os.path.basename(args.folder_path)) tar_file.add(args.folder_path, arcname=os.path.basename(args.folder_path))
@@ -52,22 +65,25 @@ def publish(args):
# send the request # send the request
status.update("Uploading...") status.update("Uploading...")
response = requests.put( response = requests.put(
url=f"https://chookspace.com/api/packages/{username}/generic/{mineral_name}/{version}/mineral.tar", url=f"https://chookspace.com/api/packages/ground/generic/{mineral_name}/{version}/mineral.tar",
data=f, data=f,
auth=HTTPBasicAuth(username, password) auth=HTTPBasicAuth(username, password)
) )
f.close() f.close()
if response.status_code == 401: match response.status_code:
case 401:
console.print("[b red]digpkg: failed to publish mineral: authentication failed[/]") console.print("[b red]digpkg: failed to publish mineral: authentication failed[/]")
sys.exit(1) sys.exit(1)
elif response.status_code == 400: case 400:
console.print("[b red]digpkg: failed to publish mineral: the package name or version number are invalid[/]") console.print("[b red]digpkg: failed to publish mineral: the package name or version number are invalid[/]")
sys.exit(1) sys.exit(1)
elif response.status_code == 409: case 409:
console.print("[b red]digpkg: failed to publish mineral: that version number is already in use[/]") console.print("[b red]digpkg: failed to publish mineral: that version number is already in use[/]")
sys.exit(1) sys.exit(1)
response.raise_for_status() response.raise_for_status()
console.print("[d][:white_check_mark:] Uploaded![/]") console.print("[d][:white_check_mark:] Uploaded![/]")
console.print("[:white_check_mark:] Done!")

View File

@@ -30,7 +30,7 @@ def remove(args):
# send the request # send the request
response = requests.delete( response = requests.delete(
url=f"https://chookspace.com/api/packages/{username}/generic/{mineral_name}/{version}", url=f"https://chookspace.com/api/packages/ground/generic/{mineral_name}/{version}",
auth=HTTPBasicAuth(username, password) auth=HTTPBasicAuth(username, password)
) )
@@ -42,4 +42,4 @@ def remove(args):
sys.exit(1) sys.exit(1)
response.raise_for_status() response.raise_for_status()
console.print("[d][:white_check_mark:] Success![/]") console.print("[d][:white_check_mark:] Done![/]")

36
src/uninstall.py Normal file
View File

@@ -0,0 +1,36 @@
import os, sys
import shutil
from util import check_ground_libs_path, check_sudo
from rich.console import Console
console = Console()
def uninstall(args):
check_sudo()
check_ground_libs_path()
with console.status(status=f"Looking for [i]{args.name}[/]...", spinner="bouncingBall", spinner_style="blue") as status:
mineral_path = os.path.join(os.getenv("GROUND_LIBS"), args.name)
symlink_path = os.path.join(os.getenv("GROUND_LIBS"), f"{args.name}.so")
# check to make sure the mineral is installed
if not os.path.isdir(mineral_path):
console.print(f"[b red]digpkg: failed to uninstall [i]{args.name}[/]: mineral is not installed[/b red]")
sys.exit(1)
# remove the symlink
status.update("Removing symlink...")
if os.path.islink(symlink_path):
os.unlink(symlink_path)
console.print(f"[d][:white_check_mark:] Removed symlink!")
# delete the folder
shutil.rmtree(mineral_path)
console.print(f"[d][:white_check_mark:] Removed mineral folder!")
console.print(f"[:white_check_mark:] Done!")

16
src/util.py Normal file
View File

@@ -0,0 +1,16 @@
import os, sys
from rich import print
def check_ground_libs_path():
# ensure the GROUND_LIBS var is set
if not os.getenv("GROUND_LIBS"):
print("digpkg: the [i]GROUND_LIBS[/] environment variable is not set, defaulting to /usr/lib/ground/")
os.environ["GROUND_LIBS"] = "/usr/lib/ground/"
def check_sudo():
# check if we are sudo
if os.getuid() != 0:
print("[b red]digpkg: that command requires sudo to run[/]")
sys.exit(1)