From b2e526eb0549c6ead3bb948ba88787ee2e250af7 Mon Sep 17 00:00:00 2001 From: johnson2427 Date: Sat, 26 Oct 2024 05:45:17 -0500 Subject: [PATCH] feat: add build_utils file --- silverback/_build_utils.py | 119 +++++++++++++++++++++++++++++++++++++ silverback/_cli.py | 53 +---------------- 2 files changed, 121 insertions(+), 51 deletions(-) create mode 100644 silverback/_build_utils.py diff --git a/silverback/_build_utils.py b/silverback/_build_utils.py new file mode 100644 index 00000000..7a87460c --- /dev/null +++ b/silverback/_build_utils.py @@ -0,0 +1,119 @@ +from typing import Union + +import click + +from functools import singledispatchmethod +from pathlib import Path + + +DOCKERFILE_CONTENT = """ +FROM ghcr.io/apeworx/silverback:stable +USER root +WORKDIR /app +RUN chown harambe:harambe /app +USER harambe +""" + + +class BasePath(Path): + _flavour = type(Path())._flavour + _parse_args = type(Path())._parse_args + + +class FilePath(BasePath): + """A subclass of Path representing a file.""" + + +class DirPath(BasePath): + """A subclass of Path representing a path""" + + +def generate_path(path: Path): + if path.is_file(): + return FilePath(str(path)) + elif path.is_dir(): + return DirPath(str(path)) + else: + raise ValueError( + f"{path} is neither a file nor a directory" + ) + + +PathType = Union[ + "FilePath", + "DirPath" +] + +def generate_dockerfiles(path: Path): + path = generate_path(path) + dg = DockerfileGenerator() + breakpoint() + dg.generate_dockerfiles(path) + +class DockerfileGenerator: + + @property + def dockerfile_name(self): + return self._dockerfile_name + + @dockerfile_name.setter + def dockerfile_name(self, name): + self._dockerfile_name = name + + @singledispatchmethod + def generate_dockerfiles(self, path: PathType): + """ + Will generate a file based on path type + """ + + @generate_dockerfiles.register + def _(self, path: FilePath): + dockerfile_content = self._check_for_requirements(DOCKERFILE_CONTENT) + self.dockerfile_name = f"Dockerfile.{path.parent.name}-bot" + dockerfile_content += f"COPY {path.name}/ /app/bot.py\n" + self._build_helper(dockerfile_content) + + @generate_dockerfiles.register + def _(self, path: DirPath): + bots = self._get_all_bot_files(path) + for bot in bots: + dockerfile_content = self._check_for_requirements(DOCKERFILE_CONTENT) + if bot.name == "__init__.py" or bot.name == "bot.py": + self.dockerfile_name = f"Dockerfile.{bot.parent.parent.name}-bot" + dockerfile_content += f"COPY {path.name}/ /app/bot\n" + else: + self.dockerfile_name = f"Dockerfile.{bot.name.replace('.py', '')}" + dockerfile_content += f"COPY {path.name}/{bot.name} /app/bot.py\n" + self._build_helper(dockerfile_content) + + def _build_helper(self, dockerfile_c: str): + """ + Used in multiple places in build. + """ + dockerfile_path = Path.cwd() / ".silverback-images" / self.dockerfile_name + dockerfile_path.parent.mkdir(exist_ok=True) + dockerfile_path.write_text(dockerfile_c.strip() + "\n") + click.echo(f"Generated {dockerfile_path}") + + def _check_for_requirements(self, dockerfile_content): + if (Path.cwd() / "requirements.txt").exists(): + dockerfile_content += "COPY requirements.txt .\n" + dockerfile_content += ( + "RUN pip install --upgrade pip && pip install -r requirements.txt\n" + ) + + if (Path.cwd() / "ape-config.yaml").exists(): + dockerfile_content += "COPY ape-config.yaml .\n" + dockerfile_content += "RUN ape plugins install -U .\n" + + return dockerfile_content + + def _get_all_bot_files(self, path: DirPath): + files = sorted({file for file in path.iterdir() if file.is_file()}, reverse=True) + bots = [] + for file in files: + if file.name == "__init__.py" or file.name == "bot.py": + bots = [file] + break + bots.append(file) + return bots diff --git a/silverback/_cli.py b/silverback/_cli.py index 7756cb75..1941a74b 100644 --- a/silverback/_cli.py +++ b/silverback/_cli.py @@ -19,6 +19,7 @@ from ape.exceptions import Abort, ApeException from fief_client.integrations.cli import FiefAuth +from silverback._build_utils import generate_dockerfiles from silverback._click_ext import ( SectionedHelpGroup, auth_required, @@ -35,14 +36,6 @@ from silverback.runner import PollingRunner, WebsocketRunner from silverback.worker import run_worker -DOCKERFILE_CONTENT = """ -FROM ghcr.io/apeworx/silverback:stable -USER root -WORKDIR /app -RUN chown harambe:harambe /app -USER harambe -""" - @click.group(cls=SectionedHelpGroup) def cli(): @@ -63,16 +56,6 @@ def _account_callback(ctx, param, val): return val -def _build_helper(docker_fn: str, dockerfile_c: str): - """ - Used in multiple places in build. - """ - dockerfile_path = Path.cwd() / ".silverback-images" / docker_fn - dockerfile_path.parent.mkdir(exist_ok=True) - dockerfile_path.write_text(dockerfile_c.strip() + "\n") - click.echo(f"Generated {dockerfile_path}") - - # TODO: Make `silverback.settings.Settings` (to remove having to set envvars) # TODO: Use `envvar=...` to be able to set the value of options from correct envvar def _network_callback(ctx, param, val): @@ -154,39 +137,7 @@ def build(generate, path): f"You should have a '{path}/' or 'bot/' folder, or a 'bot.py' file in the root " "of your project." ) - if path.is_file(): - dockerfile_content = DOCKERFILE_CONTENT - docker_filename = f"Dockerfile.{path.parent.name}-bot" - dockerfile_content += f"COPY {path.name}/ /app/bot\n" - _build_helper(docker_filename, dockerfile_content) - - else: - files = sorted({file for file in path.iterdir() if file.is_file()}, reverse=True) - bots = [] - for file in files: - if file.name == "__init__.py" or file.name == "bot.py": - bots = [file] - break - bots.append(file) - for bot in bots: - dockerfile_content = DOCKERFILE_CONTENT - if (Path.cwd() / "requirements.txt").exists(): - dockerfile_content += "COPY requirements.txt .\n" - dockerfile_content += ( - "RUN pip install --upgrade pip && pip install -r requirements.txt\n" - ) - - if (Path.cwd() / "ape-config.yaml").exists(): - dockerfile_content += "COPY ape-config.yaml .\n" - dockerfile_content += "RUN ape plugins install -U .\n" - - if bot.name == "__init__.py" or bot.name == "bot.py": - docker_filename = f"Dockerfile.{bot.parent.parent.name}-bot" - dockerfile_content += f"COPY {path.name}/ /app/bot\n" - else: - docker_filename = f"Dockerfile.{bot.name.replace('.py', '')}" - dockerfile_content += f"COPY {path.name}/{bot.name} /app/bot.py\n" - _build_helper(docker_filename, dockerfile_content) + generate_dockerfiles(path) if not (path := Path.cwd() / ".silverback-images").exists(): raise FileNotFoundError(