diff --git a/update_docker_project.py b/update_docker_project.py new file mode 100644 index 0000000..cab9016 --- /dev/null +++ b/update_docker_project.py @@ -0,0 +1,106 @@ + +#!/usr/bin/env python3 + +import subprocess +import argparse +import os +import shutil +import sys +import re + +# Sprawdzenie PyYAML +try: + import yaml +except ImportError: + print("Błąd: brak modułu 'pyyaml'.") + print("Zainstaluj go komendą: pip install pyyaml / zypper in python312-pyaml / apt install python3-yaml") + sys.exit(1) + +def detect_compose_command(): + if shutil.which("docker-compose"): + return "docker-compose" + elif shutil.which("docker"): + result = subprocess.run("docker compose version", shell=True, stdout=subprocess.DEVNULL) + if result.returncode == 0: + return "docker compose" + print("Nie znaleziono docker compose ani docker-compose.") + sys.exit(1) + +def run(cmd, capture_output=False): + result = subprocess.run(cmd, shell=True, capture_output=capture_output, text=True) + if result.returncode != 0: + print(f"Błąd: {cmd}") + if result.stderr: + print(result.stderr) + return result.stdout.strip() if capture_output else None + +def stop_containers(compose_cmd, compose_file, project_name): + print("Zatrzymywanie kontenerów...") + run(f"{compose_cmd} -p {project_name} -f {compose_file} down") + +def remove_conflicting_containers(images): + print("Usuwanie kolidujących kontenerów (jeśli istnieją)...") + for image in images: + name = image.split(":")[0].split("/")[-1] + run(f"docker rm -f {name}", capture_output=True) + +def prune_images(): + print("Czyszczenie nieużywanych obrazów...") + run("docker image prune -f") + +def start_containers(compose_cmd, compose_file, project_name): + print("Uruchamianie kontenerów...") + run(f"{compose_cmd} -p {project_name} -f {compose_file} up -d") + +def resolve_env_var(value: str) -> str: + pattern = re.compile(r'\$\{([^}:\-]+)(?::-([^}]+))?\}') + def replacer(match): + var, default = match.groups() + return os.environ.get(var, default or "") + return pattern.sub(replacer, value) + +def get_images_from_compose(compose_path): + with open(compose_path, "r") as f: + data = yaml.safe_load(f) + services = data.get("services", {}) + resolved_images = [] + for svc in services.values(): + image = svc.get("image") + if image: + resolved_images.append(resolve_env_var(image)) + return resolved_images + +def main(): + parser = argparse.ArgumentParser(description="Pełna aktualizacja i restart docker-compose projektu.") + parser.add_argument('--project-folder', required=True, help='Ścieżka do folderu projektu') + parser.add_argument('--compose-file', required=True, help='Ścieżka do pliku docker-compose.yml') + parser.add_argument('--project-name', required=True, help='Nazwa projektu docker-compose') + parser.add_argument('--only-updated', action='store_true', help='Restart tylko jeśli obrazy zostały zaktualizowane') + + args = parser.parse_args() + os.chdir(args.project_folder) + + compose_cmd = detect_compose_command() + images = get_images_from_compose(args.compose_file) + + updated = False + print("Pobieranie najnowszych wersji obrazów...") + for image in images: + print(f"- docker pull {image}") + pull_result = run(f"docker pull {image}", capture_output=True) + if pull_result and "Downloaded newer image" in pull_result: + updated = True + + if args.only_updated and not updated: + print("Obrazy aktualne. Stack nie zostanie zrestartowany (--only-updated).") + else: + stop_containers(compose_cmd, args.compose_file, args.project_name) + remove_conflicting_containers(images) + prune_images() + start_containers(compose_cmd, args.compose_file, args.project_name) + print("Stack zrestartowany.") + + print("Zakończono.") + +if __name__ == "__main__": + main()