fix: redirect stdin to /dev/null in Docker commands to suppress unwanted input prompts

This commit is contained in:
Anish Sarkar 2026-02-27 21:22:15 +05:30
parent 9124b19ee9
commit fbef820377
2 changed files with 30 additions and 30 deletions

View file

@ -62,11 +62,11 @@ command -v docker >/dev/null 2>&1 \
|| error "Docker is not installed. Install it at: https://docs.docker.com/get-docker/" || error "Docker is not installed. Install it at: https://docs.docker.com/get-docker/"
success "Docker found." success "Docker found."
docker info >/dev/null 2>&1 \ docker info >/dev/null 2>&1 < /dev/null \
|| error "Docker daemon is not running. Please start Docker and try again." || error "Docker daemon is not running. Please start Docker and try again."
success "Docker daemon is running." success "Docker daemon is running."
if docker compose version >/dev/null 2>&1; then if docker compose version >/dev/null 2>&1 < /dev/null; then
DC="docker compose" DC="docker compose"
elif command -v docker-compose >/dev/null 2>&1; then elif command -v docker-compose >/dev/null 2>&1; then
DC="docker-compose" DC="docker-compose"
@ -82,7 +82,7 @@ wait_for_pg() {
local attempt=0 local attempt=0
info "Waiting for PostgreSQL to accept connections..." info "Waiting for PostgreSQL to accept connections..."
until (cd "${INSTALL_DIR}" && ${DC} exec -T db pg_isready -U "${db_user}" -q 2>/dev/null); do until (cd "${INSTALL_DIR}" && ${DC} exec -T db pg_isready -U "${db_user}" -q 2>/dev/null) < /dev/null; do
attempt=$((attempt + 1)) attempt=$((attempt + 1))
if [[ $attempt -ge $max_attempts ]]; then if [[ $attempt -ge $max_attempts ]]; then
error "PostgreSQL did not become ready after $((max_attempts * 2)) seconds.\nCheck logs: cd ${INSTALL_DIR} && ${DC} logs db" error "PostgreSQL did not become ready after $((max_attempts * 2)) seconds.\nCheck logs: cd ${INSTALL_DIR} && ${DC} logs db"
@ -125,7 +125,7 @@ success "All files downloaded to ${INSTALL_DIR}/"
# If a dump already exists (from a previous partial run) skip extraction and # If a dump already exists (from a previous partial run) skip extraction and
# go straight to restore — this makes re-runs safe and idempotent. # go straight to restore — this makes re-runs safe and idempotent.
if docker volume ls --format '{{.Name}}' 2>/dev/null | grep -q "^${OLD_VOLUME}$"; then if docker volume ls --format '{{.Name}}' 2>/dev/null < /dev/null | grep -q "^${OLD_VOLUME}$"; then
MIGRATION_MODE=true MIGRATION_MODE=true
if [[ -f "${DUMP_FILE}" ]]; then if [[ -f "${DUMP_FILE}" ]]; then
@ -191,7 +191,7 @@ if $MIGRATION_MODE; then
DB_NAME="${DB_NAME:-surfsense}" DB_NAME="${DB_NAME:-surfsense}"
step "Starting PostgreSQL 17" step "Starting PostgreSQL 17"
(cd "${INSTALL_DIR}" && ${DC} up -d db) (cd "${INSTALL_DIR}" && ${DC} up -d db) < /dev/null
wait_for_pg "${DB_USER}" wait_for_pg "${DB_USER}"
step "Restoring database" step "Restoring database"
@ -226,7 +226,7 @@ if $MIGRATION_MODE; then
-e PGPASSWORD="${DB_PASS}" \ -e PGPASSWORD="${DB_PASS}" \
db psql -U "${DB_USER}" -d "${DB_NAME}" -t \ db psql -U "${DB_USER}" -d "${DB_NAME}" -t \
-c "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public';" \ -c "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public';" \
2>/dev/null | tr -d ' \n' || echo "0" 2>/dev/null < /dev/null | tr -d ' \n' || echo "0"
) )
if [[ "${TABLE_COUNT}" == "0" || -z "${TABLE_COUNT}" ]]; then if [[ "${TABLE_COUNT}" == "0" || -z "${TABLE_COUNT}" ]]; then
warn "Smoke test: no tables found after restore." warn "Smoke test: no tables found after restore."
@ -236,7 +236,7 @@ if $MIGRATION_MODE; then
fi fi
step "Starting all SurfSense services" step "Starting all SurfSense services"
(cd "${INSTALL_DIR}" && ${DC} up -d) (cd "${INSTALL_DIR}" && ${DC} up -d) < /dev/null
success "All services started." success "All services started."
# Key file is no longer needed — SECRET_KEY is now in .env # Key file is no longer needed — SECRET_KEY is now in .env
@ -244,7 +244,7 @@ if $MIGRATION_MODE; then
else else
step "Starting SurfSense" step "Starting SurfSense"
(cd "${INSTALL_DIR}" && ${DC} up -d) (cd "${INSTALL_DIR}" && ${DC} up -d) < /dev/null
success "All services started." success "All services started."
fi fi
@ -253,14 +253,14 @@ fi
if $SETUP_WATCHTOWER; then if $SETUP_WATCHTOWER; then
step "Setting up Watchtower (auto-updates every $((WATCHTOWER_INTERVAL / 3600))h)" step "Setting up Watchtower (auto-updates every $((WATCHTOWER_INTERVAL / 3600))h)"
WT_STATE=$(docker inspect -f '{{.State.Running}}' "${WATCHTOWER_CONTAINER}" 2>/dev/null || echo "missing") WT_STATE=$(docker inspect -f '{{.State.Running}}' "${WATCHTOWER_CONTAINER}" 2>/dev/null < /dev/null || echo "missing")
if [[ "${WT_STATE}" == "true" ]]; then if [[ "${WT_STATE}" == "true" ]]; then
success "Watchtower is already running — skipping." success "Watchtower is already running — skipping."
else else
if [[ "${WT_STATE}" != "missing" ]]; then if [[ "${WT_STATE}" != "missing" ]]; then
info "Removing stopped Watchtower container..." info "Removing stopped Watchtower container..."
docker rm -f "${WATCHTOWER_CONTAINER}" >/dev/null 2>&1 || true docker rm -f "${WATCHTOWER_CONTAINER}" >/dev/null 2>&1 < /dev/null || true
fi fi
docker run -d \ docker run -d \
--name "${WATCHTOWER_CONTAINER}" \ --name "${WATCHTOWER_CONTAINER}" \
@ -268,7 +268,7 @@ if $SETUP_WATCHTOWER; then
-v /var/run/docker.sock:/var/run/docker.sock \ -v /var/run/docker.sock:/var/run/docker.sock \
nickfedor/watchtower \ nickfedor/watchtower \
--label-enable \ --label-enable \
--interval "${WATCHTOWER_INTERVAL}" >/dev/null 2>&1 \ --interval "${WATCHTOWER_INTERVAL}" >/dev/null 2>&1 < /dev/null \
&& success "Watchtower started — labeled SurfSense containers will auto-update." \ && success "Watchtower started — labeled SurfSense containers will auto-update." \
|| warn "Could not start Watchtower. You can set it up manually or use: docker compose pull && docker compose up -d" || warn "Could not start Watchtower. You can set it up manually or use: docker compose pull && docker compose up -d"
fi fi

View file

@ -98,10 +98,10 @@ confirm() {
# ── Cleanup trap — always remove the temp container ────────────────────────── # ── Cleanup trap — always remove the temp container ──────────────────────────
cleanup() { cleanup() {
local exit_code=$? local exit_code=$?
if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -q "^${TEMP_CONTAINER}$"; then if docker ps -a --format '{{.Names}}' 2>/dev/null < /dev/null | grep -q "^${TEMP_CONTAINER}$"; then
info "Cleaning up temporary container '${TEMP_CONTAINER}'..." info "Cleaning up temporary container '${TEMP_CONTAINER}'..."
docker stop "${TEMP_CONTAINER}" >/dev/null 2>&1 || true docker stop "${TEMP_CONTAINER}" >/dev/null 2>&1 < /dev/null || true
docker rm "${TEMP_CONTAINER}" >/dev/null 2>&1 || true docker rm "${TEMP_CONTAINER}" >/dev/null 2>&1 < /dev/null || true
fi fi
if [[ $exit_code -ne 0 ]]; then if [[ $exit_code -ne 0 ]]; then
printf "\n${RED}[SurfSense]${NC} Migration data extraction failed (exit code %s).\n" "${exit_code}" >&2 printf "\n${RED}[SurfSense]${NC} Migration data extraction failed (exit code %s).\n" "${exit_code}" >&2
@ -120,7 +120,7 @@ wait_for_pg() {
local attempt=0 local attempt=0
info "Waiting for ${label} to accept connections..." info "Waiting for ${label} to accept connections..."
until docker exec "${container}" pg_isready -U "${user}" -q 2>/dev/null; do until docker exec "${container}" pg_isready -U "${user}" -q 2>/dev/null < /dev/null; do
attempt=$((attempt + 1)) attempt=$((attempt + 1))
if [[ $attempt -ge $max_attempts ]]; then if [[ $attempt -ge $max_attempts ]]; then
error "${label} did not become ready after $((max_attempts * 2)) seconds. Check: docker logs ${container}" error "${label} did not become ready after $((max_attempts * 2)) seconds. Check: docker logs ${container}"
@ -142,23 +142,23 @@ command -v docker >/dev/null 2>&1 \
|| error "Docker is not installed. Install it at: https://docs.docker.com/get-docker/" || error "Docker is not installed. Install it at: https://docs.docker.com/get-docker/"
# Docker daemon # Docker daemon
docker info >/dev/null 2>&1 \ docker info >/dev/null 2>&1 < /dev/null \
|| error "Docker daemon is not running. Please start Docker and try again." || error "Docker daemon is not running. Please start Docker and try again."
# Old volume must exist # Old volume must exist
docker volume ls --format '{{.Name}}' | grep -q "^${OLD_VOLUME}$" \ docker volume ls --format '{{.Name}}' < /dev/null | grep -q "^${OLD_VOLUME}$" \
|| error "Legacy volume '${OLD_VOLUME}' not found.\n Are you sure you ran the old all-in-one SurfSense container?" || error "Legacy volume '${OLD_VOLUME}' not found.\n Are you sure you ran the old all-in-one SurfSense container?"
success "Found legacy volume: ${OLD_VOLUME}" success "Found legacy volume: ${OLD_VOLUME}"
# Detect and stop any container currently using the old volume # Detect and stop any container currently using the old volume
# (mounting a live PG volume into a second container causes the new container's # (mounting a live PG volume into a second container causes the new container's
# entrypoint to chown the data files, breaking the running container's access) # entrypoint to chown the data files, breaking the running container's access)
OLD_CONTAINER=$(docker ps --filter "volume=${OLD_VOLUME}" --format '{{.Names}}' | head -n1 || true) OLD_CONTAINER=$(docker ps --filter "volume=${OLD_VOLUME}" --format '{{.Names}}' < /dev/null | head -n1 || true)
if [[ -n "${OLD_CONTAINER}" ]]; then if [[ -n "${OLD_CONTAINER}" ]]; then
warn "Container '${OLD_CONTAINER}' is running and using the '${OLD_VOLUME}' volume." warn "Container '${OLD_CONTAINER}' is running and using the '${OLD_VOLUME}' volume."
warn "It must be stopped before migration to prevent data file corruption." warn "It must be stopped before migration to prevent data file corruption."
confirm "Stop '${OLD_CONTAINER}' now and proceed with data extraction?" confirm "Stop '${OLD_CONTAINER}' now and proceed with data extraction?"
docker stop "${OLD_CONTAINER}" >/dev/null 2>&1 \ docker stop "${OLD_CONTAINER}" >/dev/null 2>&1 < /dev/null \
|| error "Failed to stop '${OLD_CONTAINER}'. Try: docker stop ${OLD_CONTAINER}" || error "Failed to stop '${OLD_CONTAINER}'. Try: docker stop ${OLD_CONTAINER}"
success "Container '${OLD_CONTAINER}' stopped." success "Container '${OLD_CONTAINER}' stopped."
fi fi
@ -172,10 +172,10 @@ if [[ -f "${DUMP_FILE}" ]]; then
fi fi
# Clean up any stale temp container from a previous failed run # Clean up any stale temp container from a previous failed run
if docker ps -a --format '{{.Names}}' | grep -q "^${TEMP_CONTAINER}$"; then if docker ps -a --format '{{.Names}}' < /dev/null | grep -q "^${TEMP_CONTAINER}$"; then
warn "Stale migration container '${TEMP_CONTAINER}' found — removing it." warn "Stale migration container '${TEMP_CONTAINER}' found — removing it."
docker stop "${TEMP_CONTAINER}" >/dev/null 2>&1 || true docker stop "${TEMP_CONTAINER}" >/dev/null 2>&1 < /dev/null || true
docker rm "${TEMP_CONTAINER}" >/dev/null 2>&1 || true docker rm "${TEMP_CONTAINER}" >/dev/null 2>&1 < /dev/null || true
fi fi
# Disk space (warn if < 500 MB free) # Disk space (warn if < 500 MB free)
@ -205,7 +205,7 @@ confirm "Start data extraction? (Your original data will not be deleted or modif
step "1" "Starting temporary PostgreSQL 14 container" step "1" "Starting temporary PostgreSQL 14 container"
info "Pulling ${PG14_IMAGE}..." info "Pulling ${PG14_IMAGE}..."
docker pull "${PG14_IMAGE}" >/dev/null 2>&1 \ docker pull "${PG14_IMAGE}" >/dev/null 2>&1 < /dev/null \
|| warn "Could not pull ${PG14_IMAGE} — using cached image if available." || warn "Could not pull ${PG14_IMAGE} — using cached image if available."
# Detect the UID that owns the existing data files and run the temp container # Detect the UID that owns the existing data files and run the temp container
@ -214,7 +214,7 @@ docker pull "${PG14_IMAGE}" >/dev/null 2>&1 \
# re-own the files to UID 999 and break any subsequent access by the original # re-own the files to UID 999 and break any subsequent access by the original
# container's postgres process (which may run as a different UID). # container's postgres process (which may run as a different UID).
DATA_UID=$(docker run --rm -v "${OLD_VOLUME}:/data" alpine \ DATA_UID=$(docker run --rm -v "${OLD_VOLUME}:/data" alpine \
stat -c '%u' /data/postgres 2>/dev/null || echo "") stat -c '%u' /data/postgres 2>/dev/null < /dev/null || echo "")
if [[ -z "${DATA_UID}" || "${DATA_UID}" == "0" ]]; then if [[ -z "${DATA_UID}" || "${DATA_UID}" == "0" ]]; then
warn "Could not detect data directory UID — falling back to default (may chown files)." warn "Could not detect data directory UID — falling back to default (may chown files)."
USER_FLAG="" USER_FLAG=""
@ -231,7 +231,7 @@ docker run -d \
-e POSTGRES_PASSWORD="${OLD_DB_PASSWORD}" \ -e POSTGRES_PASSWORD="${OLD_DB_PASSWORD}" \
-e POSTGRES_DB="${OLD_DB_NAME}" \ -e POSTGRES_DB="${OLD_DB_NAME}" \
${USER_FLAG} \ ${USER_FLAG} \
"${PG14_IMAGE}" >/dev/null "${PG14_IMAGE}" >/dev/null < /dev/null
success "Temporary container '${TEMP_CONTAINER}' started." success "Temporary container '${TEMP_CONTAINER}' started."
wait_for_pg "${TEMP_CONTAINER}" "${OLD_DB_USER}" "PostgreSQL 14" wait_for_pg "${TEMP_CONTAINER}" "${OLD_DB_USER}" "PostgreSQL 14"
@ -245,7 +245,7 @@ if ! docker exec \
-e PGPASSWORD="${OLD_DB_PASSWORD}" \ -e PGPASSWORD="${OLD_DB_PASSWORD}" \
"${TEMP_CONTAINER}" \ "${TEMP_CONTAINER}" \
pg_dump -U "${OLD_DB_USER}" --no-password "${OLD_DB_NAME}" \ pg_dump -U "${OLD_DB_USER}" --no-password "${OLD_DB_NAME}" \
> "${DUMP_FILE}" 2>/tmp/surfsense_pgdump_err; then > "${DUMP_FILE}" 2>/tmp/surfsense_pgdump_err < /dev/null; then
cat /tmp/surfsense_pgdump_err >&2 cat /tmp/surfsense_pgdump_err >&2
error "pg_dump failed. See above for details." error "pg_dump failed. See above for details."
fi fi
@ -268,8 +268,8 @@ success "Dump complete: ${DUMP_SIZE} (${DUMP_LINES} lines) → ${DUMP_FILE}"
# Stop the temp container (trap will also handle it on unexpected exit) # Stop the temp container (trap will also handle it on unexpected exit)
info "Stopping temporary PostgreSQL 14 container..." info "Stopping temporary PostgreSQL 14 container..."
docker stop "${TEMP_CONTAINER}" >/dev/null 2>&1 || true docker stop "${TEMP_CONTAINER}" >/dev/null 2>&1 < /dev/null || true
docker rm "${TEMP_CONTAINER}" >/dev/null 2>&1 || true docker rm "${TEMP_CONTAINER}" >/dev/null 2>&1 < /dev/null || true
success "Temporary container removed." success "Temporary container removed."
# ── Step 3: Recover SECRET_KEY ──────────────────────────────────────────────── # ── Step 3: Recover SECRET_KEY ────────────────────────────────────────────────
@ -279,10 +279,10 @@ RECOVERED_KEY=""
if docker run --rm -v "${OLD_VOLUME}:/data" alpine \ if docker run --rm -v "${OLD_VOLUME}:/data" alpine \
sh -c 'test -f /data/.secret_key && cat /data/.secret_key' \ sh -c 'test -f /data/.secret_key && cat /data/.secret_key' \
2>/dev/null | grep -q .; then 2>/dev/null < /dev/null | grep -q .; then
RECOVERED_KEY=$( RECOVERED_KEY=$(
docker run --rm -v "${OLD_VOLUME}:/data" alpine \ docker run --rm -v "${OLD_VOLUME}:/data" alpine \
cat /data/.secret_key 2>/dev/null | tr -d '[:space:]' cat /data/.secret_key 2>/dev/null < /dev/null | tr -d '[:space:]'
) )
success "Recovered SECRET_KEY from '${OLD_VOLUME}'." success "Recovered SECRET_KEY from '${OLD_VOLUME}'."
else else