import argparse import dotenv import subprocess import publish_sparkd from core.argparse_utils import add_toggle_opt from core.file_utils import SparkPaths def _get_arg_parser(): parser = argparse.ArgumentParser( description= "Deploy sparkd, pubsub, md-publisher, webapp, and api server") parser.add_argument( "--cmake-jobs", type=int, help="The number of cmake parallel jobs", ) parser.add_argument( "--profile", help="The configuration profile dirs to use, relative to each service") parser.add_argument("--compose-file", help="The docker compose (yaml) file to use") parser.add_argument( "--clean", action="store_true", help="Perform a clean build (remove artifacts from previous build)", ) parser.add_argument("--env-files", action="append", help=".env files to pass to docker compose") parser.add_argument( "--verbosity", metavar="{0-5}", choices=range(1, 6), type=int, help="The output verbosity level", ) add_toggle_opt(parser, "build", help="Whether or not to build the services first") parser.add_argument( "--dry-run", action="store_true", help="Preview the build commands without running them", ) # TODO: add more services (api-server, pubsub, dsqa web server, etc.) all_services = ["sparkd"] parser.add_argument("services", help="this selects services defined in compose.yaml", nargs="*") parser.set_defaults( configuration="release", services=all_services, build=True, clean=False, profile="dev", verbosity=2, ) return parser def _get_env_files(services, profile): root = SparkPaths.root() files = [] def add(file): if file.exists(): files.append(file) add(root.joinpath(".env")) add(root.joinpath(".env.local")) if profile is not None: if "sparkd" in services or "sparkd-symbols" in services: profile_dir=root.joinpath("src/app_sparkd/configurations", profile) add(profile_dir.joinpath(".env")) add(profile_dir.joinpath(".env.local")) return files def deploy(args): def run(cmd): print(cmd) if args.dry_run: return subprocess.run(cmd, shell=True).check_returncode() services: list[str] = args.services or [] if args.build: should_build = len(services) == 0 or any( filter(lambda s: s.startswith("sparkd"), services)) if should_build: publish_sparkd.publish( publish_sparkd.PublishArgs( configuration="release", cmake_jobs=args.cmake_jobs, clean=args.clean, verbosity=args.verbosity, dry_run=args.dry_run, )) service_args = " ".join(services) compose_cmd = "sudo docker compose" if args.compose_file: compose_cmd += f" -f {args.compose_file}" env_files = _get_env_files(args.services, args.profile) if len(env_files) > 0: compose_cmd += " " + " ".join([f"--env-file {f}" for f in env_files]) for ef in env_files: if args.verbosity > 2: print(f"Loading env file {ef}") if args.verbosity > 3: env_vars = dotenv.dotenv_values(ef) for k, v in env_vars.items(): print(f" {k}={v}") dotenv.load_dotenv(ef, override=True) run(f"{compose_cmd} down {service_args}") run(f"{compose_cmd} build {service_args}") run(f"{compose_cmd} up {service_args} -d") run(f"{compose_cmd} logs {service_args} --follow") def _main(): parser = _get_arg_parser() args = parser.parse_args() deploy(args) if __name__ == "__main__": try: _main() except Exception as e: print(f"deploy.py failed: {e}") exit(1)