diff --git a/docker/scripts/install.sh b/docker/scripts/install.sh index 6dbae20b0..91b5ea1e6 100644 --- a/docker/scripts/install.sh +++ b/docker/scripts/install.sh @@ -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/" 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." 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" elif command -v docker-compose >/dev/null 2>&1; then DC="docker-compose" @@ -82,7 +82,7 @@ wait_for_pg() { local attempt=0 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)) 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" @@ -125,7 +125,7 @@ success "All files downloaded to ${INSTALL_DIR}/" # If a dump already exists (from a previous partial run) skip extraction and # 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 if [[ -f "${DUMP_FILE}" ]]; then @@ -191,7 +191,7 @@ if $MIGRATION_MODE; then DB_NAME="${DB_NAME:-surfsense}" 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}" step "Restoring database" @@ -226,7 +226,7 @@ if $MIGRATION_MODE; then -e PGPASSWORD="${DB_PASS}" \ db psql -U "${DB_USER}" -d "${DB_NAME}" -t \ -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 warn "Smoke test: no tables found after restore." @@ -236,7 +236,7 @@ if $MIGRATION_MODE; then fi step "Starting all SurfSense services" - (cd "${INSTALL_DIR}" && ${DC} up -d) + (cd "${INSTALL_DIR}" && ${DC} up -d) < /dev/null success "All services started." # Key file is no longer needed — SECRET_KEY is now in .env @@ -244,7 +244,7 @@ if $MIGRATION_MODE; then else step "Starting SurfSense" - (cd "${INSTALL_DIR}" && ${DC} up -d) + (cd "${INSTALL_DIR}" && ${DC} up -d) < /dev/null success "All services started." fi @@ -253,14 +253,14 @@ fi if $SETUP_WATCHTOWER; then 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 success "Watchtower is already running — skipping." else if [[ "${WT_STATE}" != "missing" ]]; then 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 docker run -d \ --name "${WATCHTOWER_CONTAINER}" \ @@ -268,7 +268,7 @@ if $SETUP_WATCHTOWER; then -v /var/run/docker.sock:/var/run/docker.sock \ nickfedor/watchtower \ --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." \ || warn "Could not start Watchtower. You can set it up manually or use: docker compose pull && docker compose up -d" fi diff --git a/docker/scripts/migrate-database.sh b/docker/scripts/migrate-database.sh index 26b276dad..9bc1d90b7 100755 --- a/docker/scripts/migrate-database.sh +++ b/docker/scripts/migrate-database.sh @@ -98,10 +98,10 @@ confirm() { # ── Cleanup trap — always remove the temp container ────────────────────────── cleanup() { 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}'..." - docker stop "${TEMP_CONTAINER}" >/dev/null 2>&1 || true - docker rm "${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 < /dev/null || true fi if [[ $exit_code -ne 0 ]]; then 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 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)) if [[ $attempt -ge $max_attempts ]]; then 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/" # 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." # 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?" success "Found legacy volume: ${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 # 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 warn "Container '${OLD_CONTAINER}' is running and using the '${OLD_VOLUME}' volume." warn "It must be stopped before migration to prevent data file corruption." 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}" success "Container '${OLD_CONTAINER}' stopped." fi @@ -172,10 +172,10 @@ if [[ -f "${DUMP_FILE}" ]]; then fi # 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." - docker stop "${TEMP_CONTAINER}" >/dev/null 2>&1 || true - docker rm "${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 < /dev/null || true fi # 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" 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." # 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 # container's postgres process (which may run as a different UID). 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 warn "Could not detect data directory UID — falling back to default (may chown files)." USER_FLAG="" @@ -231,7 +231,7 @@ docker run -d \ -e POSTGRES_PASSWORD="${OLD_DB_PASSWORD}" \ -e POSTGRES_DB="${OLD_DB_NAME}" \ ${USER_FLAG} \ - "${PG14_IMAGE}" >/dev/null + "${PG14_IMAGE}" >/dev/null < /dev/null success "Temporary container '${TEMP_CONTAINER}' started." wait_for_pg "${TEMP_CONTAINER}" "${OLD_DB_USER}" "PostgreSQL 14" @@ -245,7 +245,7 @@ if ! docker exec \ -e PGPASSWORD="${OLD_DB_PASSWORD}" \ "${TEMP_CONTAINER}" \ 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 error "pg_dump failed. See above for details." 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) info "Stopping temporary PostgreSQL 14 container..." -docker stop "${TEMP_CONTAINER}" >/dev/null 2>&1 || true -docker rm "${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 < /dev/null || true success "Temporary container removed." # ── Step 3: Recover SECRET_KEY ──────────────────────────────────────────────── @@ -279,10 +279,10 @@ RECOVERED_KEY="" if docker run --rm -v "${OLD_VOLUME}:/data" alpine \ 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=$( 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}'." else