Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
ca18a070c7
Parameters refactor 2025-07-13 13:08:01 +02:00
7 changed files with 120 additions and 111 deletions

View file

@ -1,4 +1,2 @@
from ...models import Command
from .mappings import mapping
commands: dict[str, Command] = { c.cmd: c for c in mapping }
from .mappings import commands

View file

@ -2,10 +2,10 @@ from urllib.parse import urlparse
from getpass import getpass
import re
from ...models import Credentials
from ...models.command import LoginNamespace, StacknameNamespace, HostNamespace, HelpNamespace
from ...service import storage
from ...service.connection import DockgeConnection
from ..utils import stack_formatter, status_formatter, generic_formatter, credential_parser_factory
from ..utils import stack_formatter, status_formatter, generic_formatter
class FunctionBindings():
"""
@ -22,31 +22,30 @@ class FunctionBindings():
return con
@staticmethod
def host(extra_args):
def host(args: HostNamespace):
"""
host command binding
"""
if len(extra_args) > 0:
mat = re.search(r"((\w+\.)?\w+\.\w+(\/.+)?)", extra_args[0], re.IGNORECASE)
if args.uri is not None:
mat = re.search(r"((\w+\.)?\w+\.\w+(\/.+)?)", args.uri, re.IGNORECASE)
if mat is None:
raise ValueError("Given host did not match regex")
res = urlparse(f"https://{mat[0]}")
if all([res.scheme, res.netloc]):
storage.put("host", mat[0])
else:
raise ValueError(f"Malformed URL {extra_args[0]}")
raise ValueError(f"Malformed URL {args.uri}")
print(storage.get("host"))
@staticmethod
def login(extra_args):
def login(args: LoginNamespace):
"""
login command binding
"""
print(f"WARNING! These credentials will be saved unencrypted in {storage._file.absolute()}")
if len(extra_args) > 0:
credentials = credential_parser_factory().parse_args(extra_args, namespace=Credentials)
storage.put("username", credentials.username, encoded=True)
storage.put("password", credentials.password, encoded=True)
if args.username is not None and args.password is not None:
storage.put("username", args.username, encoded=True)
storage.put("password", args.password, encoded=True)
return
storage.put("username", input("Username: "), encoded=True)
storage.put("password", getpass("Password: "), encoded=True)
@ -76,62 +75,56 @@ class FunctionBindings():
con.disconnect()
@staticmethod
def status(extra_args):
def status(args: StacknameNamespace):
"""
status command binding
"""
con = FunctionBindings.__setup()
status_formatter(con.list_stack(extra_args[0]))
status_formatter(con.list_stack(args.stackname))
con.disconnect()
@staticmethod
def restart(extra_args):
def restart(args: StacknameNamespace):
"""
restart command binding
"""
con = FunctionBindings.__setup()
generic_formatter(con.restart(extra_args[0]))
generic_formatter(con.restart(args.stackname))
con.disconnect()
@staticmethod
def update(extra_args):
def update(args: StacknameNamespace):
"""
update command binding
"""
con = FunctionBindings.__setup()
generic_formatter(con.update(extra_args[0]))
generic_formatter(con.update(args.stackname))
con.disconnect()
@staticmethod
def stop(extra_args):
def stop(args: StacknameNamespace):
"""
stop command binding
"""
con = FunctionBindings.__setup()
generic_formatter(con.stop(extra_args[0]))
generic_formatter(con.stop(args.stackname))
con.disconnect()
@staticmethod
def start(extra_args):
def start(args: StacknameNamespace):
"""
start command binding
"""
con = FunctionBindings.__setup()
generic_formatter(con.start(extra_args[0]))
generic_formatter(con.start(args.stackname))
con.disconnect()
@staticmethod
def down(extra_args):
def down(args: StacknameNamespace):
"""
down command binding
"""
con = FunctionBindings.__setup()
generic_formatter(con.down(extra_args[0]))
generic_formatter(con.down(args.stackname))
con.disconnect()
@staticmethod
def help():
"""
exit command binding - This should never be invoked
"""
print("WTF")

View file

@ -1,90 +1,104 @@
from typing import List
from ...models import Command
from ...models.command import HelpNamespace, HelpParser, HostNamespace, HostnameParser, LoginNamespace, LoginParser, StacknameNamespace, StacknameParser
from .functions import FunctionBindings
mapping: List[Command] = [
Command(
cmd="host",
description="Sets and gets the URI of the dockge instance. Remove any unnecessary subdomains/protocols from the URI",
args=1,
optional=True,
func=FunctionBindings.host
func=FunctionBindings.host,
namespace=HostNamespace(),
parser=HostnameParser
),
Command(
cmd="login",
description="Logs into a given dockge account, either with an interactive dialogue or by passing --user and --password",
args=2,
optional=True,
func=FunctionBindings.login
func=FunctionBindings.login,
namespace=LoginNamespace(),
parser=LoginParser
),
Command(
cmd="logout",
description="Removes the credentials from the local storage.",
args=0,
optional=False,
func=FunctionBindings.logout
func=FunctionBindings.logout,
namespace=None,
parser=None
),
Command(
cmd="list",
description="Lists all available stacks with their status",
args=0,
optional=False,
func=FunctionBindings.list
func=FunctionBindings.list,
namespace=None,
parser=None
),
Command(
cmd="status",
description="Returns the status of one stack",
args=1,
optional=False,
func=FunctionBindings.status
func=FunctionBindings.status,
namespace=StacknameNamespace(),
parser=StacknameParser
),
Command(
cmd="restart",
description="Restarts a given stack",
args=1,
optional=False,
func=FunctionBindings.restart
func=FunctionBindings.restart,
namespace=StacknameNamespace(),
parser=StacknameParser
),
Command(
cmd="start",
description="Starts a given stack",
args=1,
optional=False,
func=FunctionBindings.start
func=FunctionBindings.start,
namespace=StacknameNamespace(),
parser=StacknameParser
),
Command(
cmd="stop",
description="Stops a given stack",
args=1,
optional=False,
func=FunctionBindings.stop
func=FunctionBindings.stop,
namespace=StacknameNamespace(),
parser=StacknameParser
),
Command(
cmd="down",
description="Stop & Downs a given stack",
args=1,
optional=False,
func=FunctionBindings.down
func=FunctionBindings.down,
namespace=StacknameNamespace(),
parser=StacknameParser
),
Command(
cmd="update",
description="Updates a stack",
args=1,
optional=False,
func=FunctionBindings.update
func=FunctionBindings.update,
namespace=StacknameNamespace(),
parser=StacknameParser
),
Command(
cmd="exit",
description="Exits the CLI - this will reset all settings, including credentials and host",
args=0,
optional=False,
func=FunctionBindings.exit
func=FunctionBindings.exit,
namespace=None,
parser=None
),
Command(
cmd="help",
description="Displays helping hints for commands",
args=1,
optional=True,
func=FunctionBindings.help
func=None, # type: ignore
namespace=HelpNamespace(),
parser=HelpParser
)
]
commands: dict[str, Command] = { c.cmd: c for c in mapping }
def helpfunc(args: HelpNamespace):
if args.command is None:
print("WTF")
return
if args.command not in commands:
raise ValueError("Unknown command")
print(f"{commands[args.command].description}")
commands["help"].func = helpfunc

View file

@ -1,18 +1,5 @@
from .commands import commands
def display_help(extra_args):
"""
Display help dialogues for each command
"""
if not extra_args:
print(f"{commands['help'].description}")
return
if len(extra_args) > 1:
raise ValueError("Invalid arguments for help command")
if extra_args[0] not in commands:
raise ValueError("Unknown command")
print(f"{commands[extra_args[0]].description}")
def run(command, args):
"""
Runs a given command with the provided args
@ -22,14 +9,7 @@ def run(command, args):
raise ValueError("Invalid Command")
c = commands[command]
if args and c.args > len(args):
raise ValueError("Too many arguments")
if args and c.args < len(args) and not c.optional:
raise ValueError("Missing arguments")
if command == "help":
display_help(args)
return
c.func(args)
parsed = None
if c.parser is not None:
parsed, _ = c.parser.parse_known_args(args, namespace=c.namespace)
c.func(parsed)

View file

@ -1,19 +1,6 @@
import argparse
from tabulate import tabulate
from ..models import StackStatus
def credential_parser_factory():
"""
Creates a new parser for login credentials
"""
credentialparser = argparse.ArgumentParser(
prog="login",
description="Subparser for login credentials provided by CI"
)
credentialparser.add_argument("--username", action="store", type=str, required=True)
credentialparser.add_argument("--password", action="store", type=str, required=True)
return credentialparser
def stack_formatter(stacks):
"""
Prints a given stack list formatted as a table

View file

@ -1,12 +1,49 @@
from typing import Callable
from pydantic import BaseModel
from argparse import Namespace, ArgumentParser
from typing import Callable, Optional
class Command(BaseModel):
class Command:
"""
Basic command structure for the CLI to automatically generate valid commands
"""
cmd: str
func: Callable
args: int
optional: bool
description: str
namespace: Optional[Namespace]
parser: Optional[ArgumentParser]
def __init__(self, cmd: str, func: Callable, description: str, namespace: Optional[Namespace], parser: Optional[ArgumentParser] ):
self.cmd = cmd
self.func = func
self.description = description
self.namespace = namespace
self.parser = parser
class HelpNamespace(Namespace):
"""Namespace for help command"""
command: str
HelpParser = ArgumentParser(prog="help", description="Subparser for help command")
HelpParser.add_argument("command", nargs="?", default=None, action="store", type=str)
class StacknameNamespace(Namespace):
"""Namespace for update command"""
stackname: str
StacknameParser = ArgumentParser(prog="stack", description="Subparser for stack command")
StacknameParser.add_argument("stackname", action="store", type=str)
class HostNamespace(Namespace):
"""Namespace for host command"""
uri: Optional[str]
HostnameParser = ArgumentParser(prog="host", description="Subparser for host command")
HostnameParser.add_argument("uri", nargs="?", default=None, action="store", type=str)
class LoginNamespace(Namespace):
"""Namespace for login command"""
username: Optional[str]
password: Optional[str]
LoginParser = ArgumentParser(prog="login", description="Subparser for login command")
LoginParser.add_argument("--username", action="store", default=None ,type=str, dest="username")
LoginParser.add_argument("--password", action="store", default=None ,type=str, dest="password")

View file

@ -1,6 +1,6 @@
[project]
name = "dockge_cli"
version = "0.1.2"
version = "0.2.0-c.1"
dependencies = [
"pyyaml~=6.0.1",
"pydantic~=2.8.0",