172 lines
5.5 KiB
Bash
Executable File
172 lines
5.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "${repo_root}"
|
|
|
|
project_name="${MINEROPS_TIMELINE_MIGRATION_PROJECT:-minerops-timeline-migration-$$}"
|
|
empty_database="minerops_timeline_empty"
|
|
compose=(
|
|
docker compose
|
|
--project-name "${project_name}"
|
|
--env-file .env.dev
|
|
-f compose.dev.yaml
|
|
)
|
|
|
|
cleanup() {
|
|
status=$?
|
|
if (( status != 0 )); then
|
|
echo
|
|
echo "Repair Timeline migration test failed. Recent PostgreSQL logs:"
|
|
"${compose[@]}" logs --no-color --tail 120 postgres || true
|
|
fi
|
|
"${compose[@]}" down --volumes --remove-orphans >/dev/null 2>&1 || true
|
|
exit "${status}"
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
|
|
if [[ ! -f .env.dev ]]; then
|
|
echo "Missing .env.dev. Run ./scripts/dev-env-init.sh first." >&2
|
|
exit 1
|
|
fi
|
|
|
|
run_alembic() {
|
|
"${compose[@]}" run --rm --no-deps backend alembic "$@"
|
|
}
|
|
|
|
echo "Starting disposable PostgreSQL..."
|
|
"${compose[@]}" up --detach postgres
|
|
for attempt in $(seq 1 60); do
|
|
if "${compose[@]}" exec -T postgres sh -ec \
|
|
'pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"' \
|
|
>/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
if (( attempt == 60 )); then
|
|
echo "PostgreSQL did not become ready." >&2
|
|
exit 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
echo "Building migration image..."
|
|
"${compose[@]}" build backend
|
|
|
|
echo "Upgrading the previous head to Repair Timeline..."
|
|
run_alembic upgrade 0010_begin_repair
|
|
previous_revision="$(run_alembic current 2>/dev/null)"
|
|
[[ "${previous_revision}" == *"0010_begin_repair"* ]]
|
|
run_alembic upgrade 0011_repair_timeline
|
|
current_revision="$(run_alembic current 2>/dev/null)"
|
|
[[ "${current_revision}" == *"0011_repair_timeline"* ]]
|
|
|
|
echo "Checking repairs.read permission and role assignments..."
|
|
"${compose[@]}" exec -T postgres sh -ec \
|
|
'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"' \
|
|
<<'SQL'
|
|
DO $$
|
|
DECLARE
|
|
v_read_roles TEXT[];
|
|
v_create_roles TEXT[];
|
|
BEGIN
|
|
IF to_regclass('public.repairs') IS NULL
|
|
OR to_regclass('public.repair_events') IS NULL THEN
|
|
RAISE EXCEPTION 'Repair tables changed during timeline migration';
|
|
END IF;
|
|
|
|
IF (SELECT count(*) FROM permissions WHERE key = 'repairs.read') <> 1 THEN
|
|
RAISE EXCEPTION 'repairs.read permission was not registered once';
|
|
END IF;
|
|
|
|
SELECT array_agg(roles.name ORDER BY roles.name)
|
|
INTO v_read_roles
|
|
FROM role_permissions
|
|
JOIN roles ON roles.id = role_permissions.role_id
|
|
JOIN permissions ON permissions.id = role_permissions.permission_id
|
|
WHERE permissions.key = 'repairs.read';
|
|
IF v_read_roles <> ARRAY[
|
|
'Admin', 'Manager', 'Read Only', 'Technician'
|
|
] THEN
|
|
RAISE EXCEPTION 'Unexpected repairs.read role matrix: %', v_read_roles;
|
|
END IF;
|
|
|
|
SELECT array_agg(roles.name ORDER BY roles.name)
|
|
INTO v_create_roles
|
|
FROM role_permissions
|
|
JOIN roles ON roles.id = role_permissions.role_id
|
|
JOIN permissions ON permissions.id = role_permissions.permission_id
|
|
WHERE permissions.key = 'repairs.create';
|
|
IF v_create_roles <> ARRAY['Admin', 'Manager', 'Technician'] THEN
|
|
RAISE EXCEPTION
|
|
'repairs.create role matrix changed: %',
|
|
v_create_roles;
|
|
END IF;
|
|
|
|
INSERT INTO permissions (key, description)
|
|
VALUES ('repairs.read', 'View repair details and timeline events')
|
|
ON CONFLICT (key) DO UPDATE
|
|
SET description = EXCLUDED.description;
|
|
INSERT INTO role_permissions (role_id, permission_id)
|
|
SELECT roles.id, permissions.id
|
|
FROM roles
|
|
JOIN permissions ON permissions.key = 'repairs.read'
|
|
WHERE roles.name IN ('Admin', 'Manager', 'Technician', 'Read Only')
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
IF (SELECT count(*) FROM permissions WHERE key = 'repairs.read') <> 1
|
|
OR (
|
|
SELECT count(*)
|
|
FROM role_permissions
|
|
JOIN permissions
|
|
ON permissions.id = role_permissions.permission_id
|
|
WHERE permissions.key = 'repairs.read'
|
|
) <> 4 THEN
|
|
RAISE EXCEPTION 'repairs.read seeding is not idempotent';
|
|
END IF;
|
|
END
|
|
$$;
|
|
SQL
|
|
|
|
echo "Downgrading and re-upgrading Repair Timeline..."
|
|
run_alembic downgrade 0010_begin_repair
|
|
downgrade_state="$(
|
|
"${compose[@]}" exec -T postgres sh -ec \
|
|
'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" -At' \
|
|
<<'SQL'
|
|
SELECT
|
|
NOT EXISTS (
|
|
SELECT 1 FROM permissions WHERE key = 'repairs.read'
|
|
)
|
|
AND EXISTS (
|
|
SELECT 1 FROM permissions WHERE key = 'repairs.create'
|
|
)
|
|
AND to_regclass('public.repairs') IS NOT NULL
|
|
AND to_regclass('public.repair_events') IS NOT NULL;
|
|
SQL
|
|
)"
|
|
[[ "${downgrade_state}" == "t" ]]
|
|
downgraded_revision="$(run_alembic current 2>/dev/null)"
|
|
[[ "${downgraded_revision}" == *"0010_begin_repair"* ]]
|
|
|
|
run_alembic upgrade 0011_repair_timeline
|
|
reupgraded_revision="$(run_alembic current 2>/dev/null)"
|
|
[[ "${reupgraded_revision}" == *"0011_repair_timeline"* ]]
|
|
|
|
echo "Migrating an empty database from base through Repair Timeline..."
|
|
"${compose[@]}" exec -T postgres sh -ec \
|
|
'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d postgres \
|
|
-c "CREATE DATABASE minerops_timeline_empty"' >/dev/null
|
|
"${compose[@]}" run --rm --no-deps \
|
|
-e "POSTGRES_DB=${empty_database}" \
|
|
backend alembic upgrade 0011_repair_timeline
|
|
|
|
empty_revision="$(
|
|
"${compose[@]}" run --rm --no-deps \
|
|
-e "POSTGRES_DB=${empty_database}" \
|
|
backend alembic current 2>/dev/null
|
|
)"
|
|
[[ "${empty_revision}" == *"0011_repair_timeline"* ]]
|
|
|
|
echo "Repair Timeline migration test passed."
|