From 5f36b14f939e1ab626af9944ceb0a92abbcedf2b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 21:45:26 +0000 Subject: [PATCH] mattermost: add PikaPods migration helper (DB dump + files import) New opt-in prompt on fresh/new installs (skipped on "update" reruns, where an existing instance is already in real use and importing over it would be destructive): "Migrating from an existing Mattermost instance (e.g. PikaPods)?" -- if yes, generates migrate-from-pikapods.sh in the instance's own directory, same generated-helper pattern as Immich's import-photos.sh. Checked PikaPods' own docs before writing this rather than guessing at their export mechanics: they expose per-pod SFTP (file access) and a Database-access toggle that hands you an Adminer link for a full SQL dump -- their own documented backup/migration flow is stop the pod, SFTP the files, export the DB via Adminer. The generated script assumes that shape (plain-text SQL dump + a files directory) and says so in its header, including that PikaPods' exact SFTP layout wasn't verified against a live pod so the files-argument path needs the user's own confirmation. What the script does: stops the mattermost container (leaves the DB container running), drops and recreates the database owned by the same existing role -- so .env's credentials are never touched or regenerated, avoiding the "restored data, mismatched password" bug class fixed elsewhere in this repo -- imports the dump via psql, rsyncs the files directory into ./data, restarts. Requires typing "YES" to proceed since it's destructive to whatever's currently in the fresh instance's database. Correctly parameterized per-instance: pulled from install_mattermost's own MM_CONTAINER/DB_CONTAINER variables, so it's already correct for either the first instance or an additional named one. Verified end-to-end: prompt fires correctly at the right point in the flow, generated script is syntactically valid, and the container names/paths it's parameterized with match the actual instance being installed. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01TBtExJcqxnokyZZKmphdug --- services/mattermost.sh | 120 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/services/mattermost.sh b/services/mattermost.sh index 390d496..486fe97 100644 --- a/services/mattermost.sh +++ b/services/mattermost.sh @@ -592,6 +592,126 @@ MD log_warning "WebRTC (voice/video calls) requires HTTPS. Configure Caddy and update SITE_URL." fi + # ── Migration helper (new/fresh installs only — not "update" reruns, + # where an existing instance is already in real use and importing over + # it would be destructive) ──────────────────────────────────────────── + if [ "$MODE" != "update" ]; then + echo "" + local MIGRATING="" + prompt_yn "Migrating from an existing Mattermost instance (e.g. PikaPods)? (y/n):" "n" MIGRATING + if [[ "$MIGRATING" =~ ^[Yy]$ ]]; then + cat > "$DIR/migrate-from-pikapods.sh" << 'MIGRATE_HEAD' +#!/bin/bash +################################################################################ +# migrate-from-pikapods.sh — generated by ubuntu-post-install +# +# Imports a Mattermost database dump + file storage exported from another +# instance (e.g. PikaPods) into THIS freshly-created instance, replacing its +# empty database and populating its file storage. +# +# PikaPods export procedure (Pod Settings): enable SFTP + Database access, +# STOP the pod first (flushes anything still in memory to disk), SFTP the +# pod's files down, then use the Adminer link PikaPods gives you to export +# the database as a plain SQL dump. See docs.pikapods.com/manage/backup. +# +# This script assumes a PLAIN-TEXT SQL dump (what Adminer produces by +# default). If you have a custom-format pg_dump instead, use `pg_restore` +# in place of the `psql < dump` step below. +# +# IMPORTANT: point the files argument at the SUBDIRECTORY that holds +# Mattermost's own file storage inside whatever you downloaded via SFTP +# (commonly named `data`), not the whole SFTP root — PikaPods' exact +# layout wasn't verified against a live pod, so confirm this yourself +# before running. +# +# Usage: +# ./migrate-from-pikapods.sh +################################################################################ + +MIGRATE_HEAD + + cat >> "$DIR/migrate-from-pikapods.sh" << MIGRATE_VARS +PROJECT_DIR="$DIR" +MM_CONTAINER="$MM_CONTAINER" +DB_CONTAINER="$DB_CONTAINER" +DB_NAME="mattermost" +DB_USER="mattermost" +MIGRATE_VARS + + cat >> "$DIR/migrate-from-pikapods.sh" << 'MIGRATE_BODY' +set -uo pipefail +cd "$PROJECT_DIR" || exit 1 + +SQL_DUMP="${1:-}" +FILES_DIR="${2:-}" + +if [ -z "$SQL_DUMP" ] || [ -z "$FILES_DIR" ]; then + echo "Usage: $0 " + exit 1 +fi +[ -f "$SQL_DUMP" ] || { echo "SQL dump not found: $SQL_DUMP"; exit 1; } +[ -d "$FILES_DIR" ] || { echo "Files directory not found: $FILES_DIR"; exit 1; } +[ -f "docker-compose.yml" ] || { echo "Run this from $PROJECT_DIR (docker-compose.yml not found here)."; exit 1; } + +echo "" +echo "┌─────────────────────────────────────────────────────────────────┐" +echo "│ MATTERMOST MIGRATION — THIS REPLACES THE CURRENT DATABASE │" +echo "└─────────────────────────────────────────────────────────────────┘" +echo "" +echo " Target instance: $PROJECT_DIR" +echo " SQL dump: $SQL_DUMP" +echo " Files: $FILES_DIR (copied into ./data)" +echo "" +read -r -p " Type YES to proceed: " CONFIRM +[ "$CONFIRM" = "YES" ] || { echo "Aborted — no changes made."; exit 0; } + +echo "" +echo "Stopping $MM_CONTAINER (keeping $DB_CONTAINER running)..." +docker compose stop mattermost + +echo "Waiting for $DB_CONTAINER to accept connections..." +tries=0 +until docker exec "$DB_CONTAINER" pg_isready -U "$DB_USER" >/dev/null 2>&1 || [ "$tries" -ge 30 ]; do + sleep 1; tries=$((tries + 1)) +done + +echo "Dropping and recreating '$DB_NAME' (owned by the existing '$DB_USER' role — .env credentials are untouched)..." +if ! docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d postgres -c "DROP DATABASE IF EXISTS $DB_NAME;" \ + || ! docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d postgres -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;"; then + echo "Failed to reset the database — check: docker compose logs db" + exit 1 +fi + +echo "Importing $SQL_DUMP..." +if ! docker exec -i "$DB_CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" < "$SQL_DUMP" > /tmp/mm-migrate-import.log 2>&1; then + echo "Import reported errors — check /tmp/mm-migrate-import.log before continuing." + echo "(Some warnings, e.g. about extensions already existing, are expected and harmless." + echo " Look for actual failures — missing tables, permission errors — before deciding.)" +fi + +echo "Copying files into ./data..." +mkdir -p ./data +rsync -a "$FILES_DIR"/ ./data/ 2>/dev/null || cp -a "$FILES_DIR"/. ./data/ + +echo "Starting Mattermost..." +docker compose up -d + +echo "" +echo "Done. Verify before treating this as live:" +echo " - Open the site and confirm you can log in as an existing (migrated) user" +echo " - Spot-check a channel with history and a message that has an attached file" +echo " - Check System Console → users/teams counts look right" +echo "" +echo "Import log: /tmp/mm-migrate-import.log" +MIGRATE_BODY + + chmod +x "$DIR/migrate-from-pikapods.sh" + chown "$ACTUAL_USER:$ACTUAL_USER" "$DIR/migrate-from-pikapods.sh" + log_success "Migration helper written: $DIR/migrate-from-pikapods.sh" + log_info "Run it once you have both a SQL dump and the files directory from PikaPods." + fi + fi + local START="" prompt_yn "Start Mattermost now? (y/n):" "y" START if [ "$START" = "y" ] || [ "$START" = "Y" ]; then