Files
LMS/scripts/ldd-migration-upgrade.sh
Vladislav Kopnin 1e1e9ae0e4 feat: закрыть остаточный backlog
- keyset-пагинация аудита с курсором (occurred_at, id) и UI «Показать ещё»
- пакетный /api/v1/labs/details и переход progress на один запрос
- абсолютный потолок сессии 30 дней (identity V12 + web), очистка refresh
- админская инвентаризация legacy-активов (/admin/legacy-assets, скрипт)
- E2E в Chromium/Firefox/WebKit (18/18), CI preflight
- LDD: 51 claim, 346 тестов, docs и handoff обновлены
2026-10-29 12:01:44 +03:00

208 lines
10 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# LDD migration evidence: master-схема + данные -> миграции ветки.
# Одноразовая БД, рабочие данные не затрагиваются.
set -euo pipefail
cd "$(dirname "$0")/.."
DB="lms_ldd_mig_$(date -u +%H%M%S)_$$"
EVIDENCE="build/evidence/migration.json"
POSTGRES_WAS_RUNNING=0
FAILED=0
log() { echo ">> $*"; }
cleanup() {
local code=$?
docker exec -i lms-postgres psql -U lms -d postgres -v ON_ERROR_STOP=1 \
-c "DROP DATABASE IF EXISTS $DB;" >/dev/null 2>&1 || true
if ((POSTGRES_WAS_RUNNING == 0)); then
docker compose stop postgres >/dev/null 2>&1 || true
fi
exit $code
}
trap cleanup EXIT INT TERM
command -v docker >/dev/null 2>&1 || { echo "Нужен docker" >&2; exit 1; }
git rev-parse --verify master >/dev/null 2>&1 || { echo "Нужна ветка master в git" >&2; exit 1; }
if docker compose ps --status running --services 2>/dev/null | grep -qx postgres; then
POSTGRES_WAS_RUNNING=1
fi
docker compose up -d postgres >/dev/null
for _ in $(seq 1 40); do
docker exec lms-postgres pg_isready -U lms -d postgres >/dev/null 2>&1 && break
sleep 1
done
docker exec lms-postgres pg_isready -U lms -d postgres >/dev/null
docker exec -i lms-postgres psql -U lms -d postgres -v ON_ERROR_STOP=1 \
-c "CREATE DATABASE $DB;" >/dev/null
mkdir -p build/evidence
apply() {
docker exec -i lms-postgres psql -U lms -d "$DB" -v ON_ERROR_STOP=1 >/tmp/lms-ldd-migration.log
}
log "Разворачиваю master-схему и синтетические данные..."
{
git show master:services/identity-service/src/main/resources/db/migration/V1__init.sql
echo 'SET search_path TO identity;'
git show master:services/identity-service/src/main/resources/db/migration/V2__accounts.sql
git show master:services/identity-service/src/main/resources/db/migration/V3__login_attempts.sql
cat <<'SQL'
INSERT INTO accounts (id, login, password_hash, role, must_change_password) VALUES
('00000000-0000-7000-8000-000000001001','admin','x','ADMIN',false),
('00000000-0000-7000-8000-000000001002','s1','x','STUDENT',false);
INSERT INTO students (account_id, student_no, last_name, first_name) VALUES
('00000000-0000-7000-8000-000000001002','S1','Иванов','Иван');
INSERT INTO groups (id, name) VALUES ('00000000-0000-7000-8000-000000002001','ПИ-11БО');
INSERT INTO enrollments (group_id, student_id) VALUES
('00000000-0000-7000-8000-000000002001','00000000-0000-7000-8000-000000001002');
SQL
echo 'SET search_path TO academic;'
git show master:services/academic-service/src/main/resources/db/migration/V1__init.sql
git show master:services/academic-service/src/main/resources/db/migration/V2__schema.sql
git show master:services/academic-service/src/main/resources/db/migration/V3__seed.sql
git show master:services/academic-service/src/main/resources/db/migration/V4__content.sql
git show master:services/academic-service/src/main/resources/db/migration/V5__content_published.sql
cat <<'SQL'
INSERT INTO labs (id, number, title, semester_id) VALUES
('00000000-0000-7000-8000-000000003001',1,'ЛР 1','00000000-0000-7000-8000-000000000001');
INSERT INTO chapters (id, title, slug) VALUES
('00000000-0000-7000-8000-000000003002','Глава','osnovy-c');
SQL
echo 'SET search_path TO progress;'
git show master:services/progress-service/src/main/resources/db/migration/V1__init.sql
git show master:services/progress-service/src/main/resources/db/migration/V2__schema.sql
git show master:services/progress-service/src/main/resources/db/migration/V3__grades.sql
git show master:services/progress-service/src/main/resources/db/migration/V4__submissions.sql
git show master:services/progress-service/src/main/resources/db/migration/V5__defense_sessions.sql
git show master:services/progress-service/src/main/resources/db/migration/V6__grade_comments.sql
git show master:services/progress-service/src/main/resources/db/migration/V7__grade_publications.sql
git show master:services/progress-service/src/main/resources/db/migration/V8__grade_publication_students.sql
git show master:services/progress-service/src/main/resources/db/migration/V9__grade_general_comment.sql
git show master:services/progress-service/src/main/resources/db/migration/V11__attestations.sql
git show master:services/progress-service/src/main/resources/db/migration/V12__attestation_semester_and_publication.sql
cat <<'SQL'
INSERT INTO lab_grades (student_id, lab_id, score) VALUES
('00000000-0000-7000-8000-000000001002','00000000-0000-7000-8000-000000003001',4.5);
INSERT INTO grade_comments (student_id, lab_id, text) VALUES
('00000000-0000-7000-8000-000000001002','00000000-0000-7000-8000-000000003001','ok');
SQL
} | apply
log "Применяю миграции ветки..."
{
echo 'SET search_path TO identity;'
cat services/identity-service/src/main/resources/db/migration/V4__roles.sql
cat services/identity-service/src/main/resources/db/migration/V5__groups_cohorts.sql
cat services/identity-service/src/main/resources/db/migration/V6__student_statuses.sql
cat services/identity-service/src/main/resources/db/migration/V7__promotion.sql
cat services/identity-service/src/main/resources/db/migration/V8__membership_history.sql
cat services/identity-service/src/main/resources/db/migration/V9__account_statuses.sql
cat services/identity-service/src/main/resources/db/migration/V10__audit_events.sql
cat services/identity-service/src/main/resources/db/migration/V11__refresh_tokens.sql
cat services/identity-service/src/main/resources/db/migration/V12__refresh_absolute.sql
cat services/identity-service/src/main/resources/db/migration/V13__audit_keyset_indexes.sql
echo 'SET search_path TO academic;'
cat services/academic-service/src/main/resources/db/migration/V6__courses.sql
cat services/academic-service/src/main/resources/db/migration/V7__labs_course.sql
cat services/academic-service/src/main/resources/db/migration/V8__chapters_course.sql
cat services/academic-service/src/main/resources/db/migration/V9__article_assets_indexes.sql
cat services/academic-service/src/main/resources/db/migration/V10__course_title_unique.sql
cat services/academic-service/src/main/resources/db/migration/V11__global_audience_all.sql
cat services/academic-service/src/main/resources/db/migration/V12__asset_links_backfill.sql
echo 'SET search_path TO progress;'
cat services/progress-service/src/main/resources/db/migration/V13__profile_authors.sql
} | apply
log "Проверяю postflight..."
CHECK_SQL=$(cat <<'SQL'
SET search_path TO identity;
SELECT 'audit_events=' || count(*) FROM information_schema.tables
WHERE table_schema='identity' AND table_name='audit_events';
SELECT 'refresh_tokens=' || count(*) FROM information_schema.tables
WHERE table_schema='identity' AND table_name='refresh_tokens';
SELECT 'refresh_absolute=' || count(*) FROM information_schema.columns
WHERE table_schema='identity' AND table_name='refresh_tokens' AND column_name='absolute_expires_at';
SELECT 'audit_keyset_idx=' || count(*) FROM pg_indexes
WHERE schemaname='identity' AND indexname='audit_events_occurred_id_idx';
SELECT 'students=' || count(*) FROM students;
SELECT 'groups=' || count(*) FROM groups;
SELECT 'history=' || count(*) FROM membership_history;
SET search_path TO academic;
SELECT 'labs_course_id=' || count(*) FROM information_schema.columns
WHERE table_schema='academic' AND table_name='labs' AND column_name='course_id';
SELECT 'chapters_course_id=' || count(*) FROM information_schema.columns
WHERE table_schema='academic' AND table_name='chapters' AND column_name='course_id';
SELECT 'article_assets=' || count(*) FROM information_schema.tables
WHERE table_schema='academic' AND table_name='article_assets';
SELECT 'labs=' || count(*) FROM labs;
SELECT 'chapters=' || count(*) FROM chapters;
SET search_path TO progress;
SELECT 'grade_author=' || count(*) FROM information_schema.columns
WHERE table_schema='progress' AND table_name='grade_comments' AND column_name='author_account_id';
SELECT 'grade_updated_by=' || count(*) FROM information_schema.columns
WHERE table_schema='progress' AND table_name='lab_grades' AND column_name='updated_by_account_id';
SELECT 'grades=' || count(*) FROM lab_grades;
SELECT 'comments=' || count(*) FROM grade_comments;
SQL
)
RESULT=$(docker exec -i lms-postgres psql -U lms -d "$DB" -At -v ON_ERROR_STOP=1 -c "$CHECK_SQL")
# V13 повторно: идемпотентность.
if { echo 'SET search_path TO progress;'; cat services/progress-service/src/main/resources/db/migration/V13__profile_authors.sql; } | apply; then
V13_TWICE="passed"
else
V13_TWICE="failed"
FAILED=1
fi
printf '%s\n' "$RESULT" | sed 's/^/ /'
python3 - "$EVIDENCE" "$RESULT" "$V13_TWICE" <<'PY'
import json
import sys
out, result, v13 = sys.argv[1], sys.argv[2], sys.argv[3]
required = {
"audit_events=1",
"refresh_tokens=1",
"refresh_absolute=1",
"audit_keyset_idx=1",
"students=1",
"groups=1",
"history=1",
"labs_course_id=1",
"chapters_course_id=1",
"article_assets=1",
"labs=1",
"chapters=1",
"grade_author=1",
"grade_updated_by=1",
"grades=1",
"comments=1",
}
lines = {line.strip() for line in result.splitlines() if line.strip()}
missing = sorted(required - lines)
failed = bool(missing) or v13 != "passed"
if missing:
status = "failed"
detail = "postflight не подтвердил: " + ", ".join(missing)
elif v13 != "passed":
status = "failed"
detail = "V13 не применилась повторно"
else:
status = "passed"
detail = "master->branch: все postflight-проверки и повторная V13 успешны"
checks = [{"id": "migration.master_upgrade", "status": status, "detail": detail}]
with open(out, "w", encoding="utf-8") as fh:
now = __import__("datetime").datetime.now(__import__("datetime").UTC).isoformat()
json.dump({"generatedAt": now, "checks": checks}, fh, ensure_ascii=False, indent=2)
print(("PASS " if not failed else "FAIL ") + "migration.master_upgrade: " + detail)
sys.exit(1 if failed else 0)
PY
log "Migration evidence: $EVIDENCE"