diff --git a/update_immich.yml b/update_immich.yml index 1822e8b..d265c52 100644 --- a/update_immich.yml +++ b/update_immich.yml @@ -14,11 +14,13 @@ # Immich settings immich_project: immich immich_port: 2283 - immich_compose_files: - docker-compose-immich.yml - docker-compose-immich.override.yml + # Remote .env handling + immich_env_backup: "{{ compose_remote_base }}/immich.env.backup" + tasks: - name: Ensure remote base directory exists ansible.builtin.file: @@ -26,6 +28,15 @@ state: directory mode: "0755" + # --- Preserve .env across staging refresh (because .env is usually not in git) --- + - name: Backup existing Immich .env on remote (if present) + ansible.builtin.copy: + src: "{{ compose_remote_dir }}/.env" + dest: "{{ immich_env_backup }}" + remote_src: true + mode: "0600" + ignore_errors: true + - name: Create local archive of docker-compose directory (controller) ansible.builtin.archive: path: "{{ compose_local_dir }}/" @@ -57,6 +68,114 @@ dest: "{{ compose_remote_dir }}" remote_src: true + - name: Restore Immich .env on remote (if backup exists) + ansible.builtin.copy: + src: "{{ immich_env_backup }}" + dest: "{{ compose_remote_dir }}/.env" + remote_src: true + mode: "0600" + ignore_errors: true + + - name: Ensure Immich .env exists (reconstruct from running containers if missing) + ansible.builtin.command: + argv: + - bash + - -lc + - | + set -euo pipefail + cd "{{ compose_remote_dir }}" + + if [ -f .env ]; then + exit 0 + fi + + python3 - <<'PY' + import json + import subprocess + from pathlib import Path + + env_path = Path(".env") + + def run(cmd): + p = subprocess.run(cmd, capture_output=True, text=True) + return p.returncode, p.stdout, p.stderr + + rc, out, err = run(["bash", "-lc", "command docker inspect immich_postgres immich_server"]) + if rc != 0 or not out.strip(): + print("ERROR: .env is missing and cannot inspect running containers (immich_postgres/immich_server).", flush=True) + print("Create the .env on the VM or ensure the containers exist.", flush=True) + raise SystemExit(1) + + data = json.loads(out) + by_name = {} + for c in data: + name = (c.get("Name") or "").lstrip("/") + by_name[name] = c + + pg = by_name.get("immich_postgres") + srv = by_name.get("immich_server") + if not pg or not srv: + print("ERROR: Could not find immich_postgres and immich_server in docker inspect output.", flush=True) + raise SystemExit(1) + + def env_map(container): + m = {} + for kv in (container.get("Config", {}).get("Env") or []): + if "=" in kv: + k, v = kv.split("=", 1) + m[k] = v + return m + + def find_mount_source(container, dest): + for m in (container.get("Mounts") or []): + if m.get("Destination") == dest: + return m.get("Source") + return "" + + pg_env = env_map(pg) + db_user = pg_env.get("POSTGRES_USER", "") + db_pass = pg_env.get("POSTGRES_PASSWORD", "") + db_name = pg_env.get("POSTGRES_DB", "") + + db_data = find_mount_source(pg, "/var/lib/postgresql/data") + upload_loc = find_mount_source(srv, "/usr/src/app/upload") + + immich_version = "" + image = (srv.get("Config", {}).get("Image") or "") + if ":" in image and "@" not in image: + immich_version = image.rsplit(":", 1)[-1] + elif ":" in image and "@" in image: + immich_version = image.split("@", 1)[0].rsplit(":", 1)[-1] + + missing = [] + for k, v in [ + ("DB_USERNAME", db_user), + ("DB_PASSWORD", db_pass), + ("DB_DATABASE_NAME", db_name), + ("DB_DATA_LOCATION", db_data), + ("UPLOAD_LOCATION", upload_loc), + ]: + if not v: + missing.append(k) + + if missing: + print("ERROR: Could not reconstruct these .env values from containers: " + ", ".join(missing), flush=True) + raise SystemExit(1) + + lines = [ + f"UPLOAD_LOCATION={upload_loc}", + f"DB_DATA_LOCATION={db_data}", + f"DB_USERNAME={db_user}", + f"DB_PASSWORD={db_pass}", + f"DB_DATABASE_NAME={db_name}", + ] + if immich_version: + lines.append(f"IMMICH_VERSION={immich_version}") + + env_path.write_text("\n".join(lines) + "\n", encoding="utf-8") + print("Created .env from running containers.", flush=True) + PY + - name: Pull latest Immich images community.docker.docker_compose_v2: project_name: "{{ immich_project }}" @@ -80,5 +199,6 @@ - name: Check Immich HTTP endpoint ansible.builtin.uri: - url: "http://127.0.0.1:{{ immich_port }}/" + url: "http://127.0.0.1:{{ immich_port }}/api/server/ping" status_code: 200 + return_content: true