Skip to content

Commit

Permalink
fix: get state_reader via function
Browse files Browse the repository at this point in the history
this is not much better, but the function name documents what is happening
  • Loading branch information
Jean-Louis Fuchs committed Mar 14, 2024
1 parent 93290d7 commit 5ecb270
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 54 deletions.
131 changes: 126 additions & 5 deletions pyaptly/cli.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,53 @@
"""python-click based command line interface for pyaptly."""

import logging
import sys
from pathlib import Path
from subprocess import CalledProcessError

import click

# I decided it is a good pattern to do lazy imports in the cli module. I had to
# do this in a few other CLIs for startup performance.
lg = logging.getLogger(__name__)


# TODO this makes the legacy command more usable. remove and set the entry point
# back to `pyaptly = 'pyaptly.cli:cli'
def entry_point():
"""Fix args then call click."""
# TODO this makes the legacy command more usable. remove legacy commands when
# we are out of beta
argv = list(sys.argv)
len_argv = len(argv)
if len_argv > 0 and argv[0].endswith("pyaptly"):
if len_argv > 2 and argv[1] == "legacy" and argv[2] != "--":
argv = argv[:2] + ["--"] + argv[2:]
cli.main(argv[1:])

try:
cli.main(argv[1:])
except CalledProcessError:
pass # already logged
except Exception as e:
from . import util

path = util.write_traceback()
tb = f"Wrote traceback to: {path}"
msg = " ".join([str(x) for x in e.args])
lg.error(f"{msg}\n {tb}")


# I want to release the new cli interface with 2.0, so we do not repeat breaking changes.
# But changing all functions that use argparse, means also changing all the tests, which
# (ab)use the argparse interface. So we currently fake that interface, so we can roll-out
# the new interface early.
# TODO: remove this, once argparse is gone
class FakeArgs:
"""Helper for compatiblity."""

def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)


# I decided it is a good pattern to do lazy imports in the cli module. I had to
# do this in a few other CLIs for startup performance.


@click.group()
Expand Down Expand Up @@ -47,6 +76,98 @@ def legacy(passthrough):
main.main(argv=passthrough)


@cli.command()
@click.option("--info/--no-info", "-i/-ni", default=False, type=bool)
@click.option("--debug/--no-debug", "-d/-nd", default=False, type=bool)
@click.option(
"--pretend/--no-pretend",
"-p/-np",
default=False,
type=bool,
help="Do not change anything",
)
@click.argument("config", type=click.Path(file_okay=True, dir_okay=False, exists=True))
@click.argument("task", type=click.Choice(["create"]))
@click.option("--repo-name", "-n", default="all", type=str, help='deafult: "all"')
def repo(**kwargs):
"""Create aptly repos."""
from . import main, repo

fake_args = FakeArgs(**kwargs)
main.setup_logger(fake_args)
cfg = main.prepare(fake_args)
repo.repo(cfg, args=fake_args)


@cli.command()
@click.option("--info/--no-info", "-i/-ni", default=False, type=bool)
@click.option("--debug/--no-debug", "-d/-nd", default=False, type=bool)
@click.option(
"--pretend/--no-pretend",
"-p/-np",
default=False,
type=bool,
help="Do not change anything",
)
@click.argument("config", type=click.Path(file_okay=True, dir_okay=False, exists=True))
@click.argument("task", type=click.Choice(["create", "update"]))
@click.option("--mirror-name", "-n", default="all", type=str, help='deafult: "all"')
def mirror(**kwargs):
"""Manage aptly mirrors."""
from . import main, mirror

fake_args = FakeArgs(**kwargs)
main.setup_logger(fake_args)
cfg = main.prepare(fake_args)
mirror.mirror(cfg, args=fake_args)


@cli.command()
@click.option("--info/--no-info", "-i/-ni", default=False, type=bool)
@click.option("--debug/--no-debug", "-d/-nd", default=False, type=bool)
@click.option(
"--pretend/--no-pretend",
"-p/-np",
default=False,
type=bool,
help="Do not change anything",
)
@click.argument("config", type=click.Path(file_okay=True, dir_okay=False, exists=True))
@click.argument("task", type=click.Choice(["create", "update"]))
@click.option("--snapshot-name", "-n", default="all", type=str, help='deafult: "all"')
def snapshot(**kwargs):
"""Manage aptly snapshots."""
from . import main, snapshot

fake_args = FakeArgs(**kwargs)
main.setup_logger(fake_args)
cfg = main.prepare(fake_args)
snapshot.snapshot(cfg, args=fake_args)


@cli.command()
@click.option("--info/--no-info", "-i/-ni", default=False, type=bool)
@click.option("--debug/--no-debug", "-d/-nd", default=False, type=bool)
@click.option(
"--pretend/--no-pretend",
"-p/-np",
default=False,
type=bool,
help="Do not change anything",
)
@click.argument("config", type=click.Path(file_okay=True, dir_okay=False, exists=True))
@click.argument("task", type=click.Choice(["create", "update"]))
@click.option("--publish-name", "-n", default="all", type=str, help='deafult: "all"')
def publish(**kwargs):
"""Manage aptly publishs."""
from . import main, publish

fake_args = FakeArgs(**kwargs)
main.setup_logger(fake_args)
cfg = main.prepare(fake_args)
publish.publish(cfg, args=fake_args)


@cli.command(help="convert yaml- to toml-comfig")
@click.argument(
"yaml_path",
Expand Down
58 changes: 32 additions & 26 deletions pyaptly/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,42 @@
lg = logging.getLogger(__name__)


def setup_logger(args):
"""Setup the logger."""
global _logging_setup
root = logging.getLogger()
formatter = custom_logger.CustomFormatter()
if not _logging_setup: # noqa
handler = logging.StreamHandler(sys.stderr)
handler.setFormatter(formatter)
root.addHandler(handler)
root.setLevel(logging.WARNING)
handler.setLevel(logging.WARNING)
if args.info:
root.setLevel(logging.INFO)
handler.setLevel(logging.INFO)
if args.debug:
root.setLevel(logging.DEBUG)
handler.setLevel(logging.DEBUG)
_logging_setup = True # noqa


def prepare(args):
"""Set pretend mode, read config and load state."""
command.Command.pretend_mode = args.pretend

with open(args.config, "rb") as f:
cfg = tomli.load(f)
state_reader.state_reader().read()
return cfg


def main(argv=None):
"""Define parsers and executes commands.
:param argv: Arguments usually taken from sys.argv
:type argv: list
"""
global _logging_setup
if not argv: # pragma: no cover
argv = sys.argv[1:]
parser = argparse.ArgumentParser(description="Manage aptly")
Expand Down Expand Up @@ -78,31 +107,8 @@ def main(argv=None):
repo_parser.add_argument("repo_name", type=str, nargs="?", default="all")

args = parser.parse_args(argv)
root = logging.getLogger()
formatter = custom_logger.CustomFormatter()
if not _logging_setup: # noqa
handler = logging.StreamHandler(sys.stderr)
handler.setFormatter(formatter)
root.addHandler(handler)
root.setLevel(logging.WARNING)
handler.setLevel(logging.WARNING)
if args.info:
root.setLevel(logging.INFO)
handler.setLevel(logging.INFO)
if args.debug:
root.setLevel(logging.DEBUG)
handler.setLevel(logging.DEBUG)
if args.pretend:
command.Command.pretend_mode = True
else:
command.Command.pretend_mode = False

_logging_setup = True # noqa
lg.debug("Args: %s", vars(args))

with open(args.config, "rb") as f:
cfg = tomli.load(f)
state_reader.state.read()
setup_logger(args)
cfg = prepare(args)

# run function for selected subparser
args.func(cfg, args)
Expand Down
8 changes: 4 additions & 4 deletions pyaptly/mirror.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def add_gpg_keys(mirror_config):
keys_urls[key] = None

for key in keys_urls.keys():
if key in state_reader.state.gpg_keys:
if key in state_reader.state_reader().gpg_keys:
continue
try:
key_command = [
Expand All @@ -59,7 +59,7 @@ def add_gpg_keys(mirror_config):
util.run_command(["bash", "-c", key_shell], check=True)
else:
raise
state_reader.state.read_gpg()
state_reader.state_reader().read_gpg()


def mirror(cfg, args):
Expand Down Expand Up @@ -102,7 +102,7 @@ def cmd_mirror_create(cfg, mirror_name, mirror_config):
:param mirror_config: Configuration of the snapshot from the toml file.
:type mirror_config: dict
"""
if mirror_name in state_reader.state.mirrors: # pragma: no cover
if mirror_name in state_reader.state_reader().mirrors: # pragma: no cover
return

add_gpg_keys(mirror_config)
Expand Down Expand Up @@ -142,7 +142,7 @@ def cmd_mirror_update(cfg, mirror_name, mirror_config):
:param mirror_config: Configuration of the snapshot from the toml file.
:type mirror_config: dict
"""
if mirror_name not in state_reader.state.mirrors: # pragma: no cover
if mirror_name not in state_reader.state_reader().mirrors: # pragma: no cover
raise Exception("Mirror not created yet")
add_gpg_keys(mirror_config)
aptly_cmd = ["aptly", "mirror", "update"]
Expand Down
19 changes: 12 additions & 7 deletions pyaptly/publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def publish(cfg, args):
]

for cmd in command.Command.order_commands(
commands, state_reader.state.has_dependency
commands, state_reader.state_reader().has_dependency
):
cmd.execute()

Expand All @@ -49,7 +49,7 @@ def publish(cfg, args):
for publish_conf_entry in cfg["publish"][args.publish_name]
]
for cmd in command.Command.order_commands(
commands, state_reader.state.has_dependency
commands, state_reader.state_reader().has_dependency
):
cmd.execute()
else:
Expand Down Expand Up @@ -83,7 +83,7 @@ def publish_cmd_update(cfg, publish_name, publish_config, ignore_existing=False)
return command.Command(publish_cmd + options + args)

publish_fullname = "%s %s" % (publish_name, publish_config["distribution"])
current_snapshots = state_reader.state.publish_map[publish_fullname]
current_snapshots = state_reader.state_reader().publish_map[publish_fullname]
if "snapshots" in publish_config:
snapshots_config = publish_config["snapshots"]
new_snapshots = [
Expand All @@ -97,7 +97,7 @@ def publish_cmd_update(cfg, publish_name, publish_config, ignore_existing=False)
if publish["distribution"] == distribution:
snapshots_config.extend(publish["snapshots"])
break
new_snapshots = list(state_reader.state.publish_map[conf_value])
new_snapshots = list(state_reader.state_reader().publish_map[conf_value])
else: # pragma: no cover
raise ValueError(
"No snapshot references configured in publish %s" % publish_name
Expand All @@ -121,7 +121,9 @@ def publish_cmd_update(cfg, publish_name, publish_config, ignore_existing=False)
archive = archive.replace(
"%T", date_tools.format_timestamp(datetime.datetime.now())
)
if archive in state_reader.state.snapshots: # pragma: no cover
if (
archive in state_reader.state_reader().snapshots
): # pragma: no cover
continue
prefix_to_search = re.sub("%T$", "", snap["name"])

Expand Down Expand Up @@ -155,7 +157,10 @@ def publish_cmd_create(cfg, publish_name, publish_config, ignore_existing=False)
:type publish_config: dict
"""
publish_fullname = "%s %s" % (publish_name, publish_config["distribution"])
if publish_fullname in state_reader.state.publishes and not ignore_existing:
if (
publish_fullname in state_reader.state_reader().publishes
and not ignore_existing
):
# Nothing to do, publish already created
return

Expand Down Expand Up @@ -228,7 +233,7 @@ def publish_cmd_create(cfg, publish_name, publish_config, ignore_existing=False)
conf_value = " ".join(conf_value.split("/"))
source_args.append("snapshot")
try:
sources = state_reader.state.publish_map[conf_value]
sources = state_reader.state_reader().publish_map[conf_value]
except KeyError:
lg.critical(
(
Expand Down
6 changes: 3 additions & 3 deletions pyaptly/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ def repo(cfg, args):
]

for cmd in command.Command.order_commands(
commands, state_reader.state.has_dependency
commands, state_reader.state_reader().has_dependency
):
cmd.execute()

else:
if args.repo_name in cfg["repo"]:
commands = [cmd_repo(cfg, args.repo_name, cfg["repo"][args.repo_name])]
for cmd in command.Command.order_commands(
commands, state_reader.state.has_dependency
commands, state_reader.state_reader().has_dependency
):
cmd.execute()
else:
Expand All @@ -57,7 +57,7 @@ def repo_cmd_create(cfg, repo_name, repo_config):
:param repo_config: Configuration of the repo from the toml file.
:type repo_config: dict
"""
if repo_name in state_reader.state.repos: # pragma: no cover
if repo_name in state_reader.state_reader().repos: # pragma: no cover
# Nothing to do, repo already created
return

Expand Down
Loading

0 comments on commit 5ecb270

Please sign in to comment.