diff --git a/scripts/setup/officer-setup.sh b/scripts/setup/officer-setup.sh index baba6c80..fd52f59d 100755 --- a/scripts/setup/officer-setup.sh +++ b/scripts/setup/officer-setup.sh @@ -446,6 +446,11 @@ if ! skip; then echo " network: ${OFFICER_NETWORK} (already there)" fi + # The client goes on the HOST, before any of the container work, because it is the half + # that is not in the container. A member has their own Postgres role and no access to the + # owner's Docker socket, so `docker exec … psql` is the owner's tool, not theirs. + install_pg_client || ERRORS+=("psql: client not installed — members have no Postgres CLI") + echo " compose file: $(pg_compose_exists && echo "$(pg_compose_file)" || echo 'not written yet')" echo " container: $(pg_container_running && echo "${PG_CONTAINER} running" || echo 'not running')" echo " port ${PG_PORT}: $(pg_port_in_use && echo 'something is listening' || echo 'free')" diff --git a/scripts/setup/officer-setup/lib/postgres.sh b/scripts/setup/officer-setup/lib/postgres.sh index a8da29d0..17ee4882 100644 --- a/scripts/setup/officer-setup/lib/postgres.sh +++ b/scripts/setup/officer-setup/lib/postgres.sh @@ -39,6 +39,73 @@ PG_DATABASE="${PG_DATABASE:-officer}" PG_CONTAINER="${PG_CONTAINER:-officer-postgres}" PG_PORT="${PG_PORT:-5432}" +# ── The CLIENT, on the host, matching the server in the container ── +# +# `psql` was on no install. The server runs in Docker, so nothing ever put a client on the +# host, and `docker exec officer-postgres psql` is not a substitute for a member: they have +# their own Postgres role (`provisionPostgresRole` for Developers) and no access to the +# owner's Docker socket. +# +# The version is derived from PG_IMAGE rather than typed again, because the pairing is not +# cosmetic: **pg_dump refuses a server newer than itself** ("server version 18.6, pg_dump +# version 16.x — aborting"). Ubuntu 24.04 ships client 16 against this 18 server, so the +# archive package is not merely old, it is unusable for dumps. That is also why this lives +# beside the server definition rather than in machine-setup's package list — one constant, +# one place to bump. +pg_client_major() { sed -E 's/^postgres:([0-9]+).*/\1/' <<<"$PG_IMAGE"; } + +pg_client_installed() { + command -v psql >/dev/null 2>&1 && [[ "$(psql --version | grep -oE '[0-9]+' | head -1)" == "$(pg_client_major)" ]] +} + +# PGDG, added the same way docker.sh adds Docker's: key to its own file, one sources.list.d +# entry, no add-apt-repository. Non-fatal — an install without psql is a working platform, +# just a more annoying one to operate. +install_pg_client() { + local major codename + major="$(pg_client_major)" + [[ -n "$major" ]] || { + warn "could not read a major version out of PG_IMAGE=${PG_IMAGE} — skipping the client" + return 1 + } + + if pg_client_installed; then + ok "psql ${major} already installed" + return 0 + fi + + codename="$(. /etc/os-release && echo "${VERSION_CODENAME:-}")" + [[ -n "$codename" ]] || { + warn "could not work out this release's codename — cannot add the PostgreSQL repository" + return 1 + } + + install -d -m 0755 /usr/share/postgresql-common/pgdg + curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \ + -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc || { + warn "could not fetch the PostgreSQL signing key" + return 1 + } + chmod a+r /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc + echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt ${codename}-pgdg main" \ + >/etc/apt/sources.list.d/pgdg.list + + DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt-get update -qq || true + DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt-get install -y -qq "postgresql-client-${major}" || { + warn "postgresql-client-${major} did not install" + return 1 + } + + # The exit status is not the gate — same lesson as rootless Docker and the claude CLI: what + # matters is whether the binary is there AND is the version we asked for, because apt can + # succeed while holding an older client back. + pg_client_installed || { + warn "psql is not version ${major} after installing — check: apt-cache policy postgresql-client-${major}" + return 1 + } + ok "psql $(psql --version | grep -oE '[0-9]+\.[0-9]+' | head -1) installed for every account on this machine" +} + docker_network_exists() { docker network inspect "$OFFICER_NETWORK" &>/dev/null; } ensure_docker_network() { docker_network_exists && return 1