mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
feat(db): auto-migrate both DBs on serve via scripts/migrate.sh
This commit is contained in:
1
TODO.md
1
TODO.md
@@ -2,5 +2,6 @@
|
|||||||
|
|
||||||
## Fixes
|
## Fixes
|
||||||
- [x] Fix CSV import UNIQUE constraint crash: skip rows whose identifier already exists in DB
|
- [x] Fix CSV import UNIQUE constraint crash: skip rows whose identifier already exists in DB
|
||||||
|
- [x] Auto-migrate both test.db and posterg.db on `just serve` via scripts/migrate.sh
|
||||||
- [x] Fix wrong `require_once` depth in `public/admin/actions/page.php` (`../../` → `../../../`)
|
- [x] Fix wrong `require_once` depth in `public/admin/actions/page.php` (`../../` → `../../../`)
|
||||||
- [x] Fix same path depth bug in `formulaire.php` and `publish.php`
|
- [x] Fix same path depth bug in `formulaire.php` and `publish.php`
|
||||||
|
|||||||
15
justfile
15
justfile
@@ -12,7 +12,7 @@ setup:
|
|||||||
@bash scripts/setup-dev.sh
|
@bash scripts/setup-dev.sh
|
||||||
|
|
||||||
[group('dev')]
|
[group('dev')]
|
||||||
serve:
|
serve: migrate
|
||||||
@php -S 127.0.0.1:8000 -t public/
|
@php -S 127.0.0.1:8000 -t public/
|
||||||
|
|
||||||
[group('dev')]
|
[group('dev')]
|
||||||
@@ -110,6 +110,19 @@ syntax:
|
|||||||
# Database
|
# Database
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
|
[group('database')]
|
||||||
|
migrate:
|
||||||
|
@echo "Running migrations…"
|
||||||
|
@bash scripts/migrate.sh both
|
||||||
|
|
||||||
|
[group('database')]
|
||||||
|
migrate-test:
|
||||||
|
@bash scripts/migrate.sh test
|
||||||
|
|
||||||
|
[group('database')]
|
||||||
|
migrate-prod:
|
||||||
|
@bash scripts/migrate.sh prod
|
||||||
|
|
||||||
[group('database')]
|
[group('database')]
|
||||||
init-db:
|
init-db:
|
||||||
@sqlite3 storage/test.db < storage/schema.sql
|
@sqlite3 storage/test.db < storage/schema.sql
|
||||||
|
|||||||
124
scripts/migrate.sh
Executable file
124
scripts/migrate.sh
Executable file
@@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Apply pending SQL migrations to one or both SQLite databases.
|
||||||
|
# Usage:
|
||||||
|
# scripts/migrate.sh # migrates both test.db and posterg.db
|
||||||
|
# scripts/migrate.sh test # migrates storage/test.db only
|
||||||
|
# scripts/migrate.sh prod # migrates storage/posterg.db only
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
MIGRATIONS_DIR="$REPO_ROOT/storage/migrations"
|
||||||
|
TEST_DB="$REPO_ROOT/storage/test.db"
|
||||||
|
PROD_DB="$REPO_ROOT/storage/posterg.db"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Check whether a migration's effects are already present in the DB so that
|
||||||
|
# legacy databases (created before the migrations table existed) can be
|
||||||
|
# bootstrapped correctly without re-running non-idempotent SQL.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
already_applied_structurally() {
|
||||||
|
local db="$1"
|
||||||
|
local name="$2"
|
||||||
|
|
||||||
|
case "$name" in
|
||||||
|
001_rename_keywords_to_tags.sql)
|
||||||
|
# Effect: table 'tags' and 'thesis_tags' exist
|
||||||
|
count=$(sqlite3 "$db" "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name IN ('tags','thesis_tags');")
|
||||||
|
[ "$count" -eq 2 ]
|
||||||
|
;;
|
||||||
|
002_add_visibility.sql)
|
||||||
|
# Effect: access_types seed rows (always safe to re-run with OR IGNORE, but mark done if rows exist)
|
||||||
|
count=$(sqlite3 "$db" "SELECT COUNT(*) FROM access_types WHERE id IN (1,2,3);" 2>/dev/null || echo 0)
|
||||||
|
[ "$count" -eq 3 ]
|
||||||
|
;;
|
||||||
|
003_seed_license_types.sql)
|
||||||
|
# Effect: at least one row in license_types
|
||||||
|
count=$(sqlite3 "$db" "SELECT COUNT(*) FROM license_types;" 2>/dev/null || echo 0)
|
||||||
|
[ "$count" -gt 0 ]
|
||||||
|
;;
|
||||||
|
004_jury_roles.sql)
|
||||||
|
# Effect: 'role' column on thesis_supervisors
|
||||||
|
count=$(sqlite3 "$db" "SELECT COUNT(*) FROM pragma_table_info('thesis_supervisors') WHERE name='role';")
|
||||||
|
[ "$count" -eq 1 ]
|
||||||
|
;;
|
||||||
|
005_add_banner.sql)
|
||||||
|
# Effect: 'banner_path' column on theses
|
||||||
|
count=$(sqlite3 "$db" "SELECT COUNT(*) FROM pragma_table_info('theses') WHERE name='banner_path';")
|
||||||
|
[ "$count" -eq 1 ]
|
||||||
|
;;
|
||||||
|
006_add_composite_index.sql)
|
||||||
|
# Effect: index idx_theses_pub_year exists (CREATE INDEX IF NOT EXISTS — safe to re-run anyway)
|
||||||
|
count=$(sqlite3 "$db" "SELECT COUNT(*) FROM sqlite_master WHERE type='index' AND name='idx_theses_pub_year';")
|
||||||
|
[ "$count" -eq 1 ]
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Unknown migration — assume not applied
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
migrate_db() {
|
||||||
|
local db="$1"
|
||||||
|
local label="$2"
|
||||||
|
|
||||||
|
if [ ! -f "$db" ]; then
|
||||||
|
echo " [$label] database not found, skipping: $db"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure tracking table exists
|
||||||
|
sqlite3 "$db" "CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||||
|
name TEXT PRIMARY KEY,
|
||||||
|
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
|
);"
|
||||||
|
|
||||||
|
local applied=0
|
||||||
|
local seeded=0
|
||||||
|
local skipped=0
|
||||||
|
|
||||||
|
for migration in "$MIGRATIONS_DIR"/*.sql; do
|
||||||
|
name="$(basename "$migration")"
|
||||||
|
already=$(sqlite3 "$db" "SELECT COUNT(*) FROM schema_migrations WHERE name='$name';")
|
||||||
|
|
||||||
|
if [ "$already" -eq 1 ]; then
|
||||||
|
skipped=$((skipped + 1))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Not in tracking table — check if it was already applied before we started tracking
|
||||||
|
if already_applied_structurally "$db" "$name"; then
|
||||||
|
sqlite3 "$db" "INSERT OR IGNORE INTO schema_migrations (name) VALUES ('$name');"
|
||||||
|
seeded=$((seeded + 1))
|
||||||
|
echo " [$label] seeded $name (already applied)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " [$label] applying $name …"
|
||||||
|
if sqlite3 "$db" < "$migration"; then
|
||||||
|
sqlite3 "$db" "INSERT OR IGNORE INTO schema_migrations (name) VALUES ('$name');"
|
||||||
|
applied=$((applied + 1))
|
||||||
|
else
|
||||||
|
echo " [$label] ERROR applying $name — aborting" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo " [$label] done — $applied applied, $seeded seeded, $skipped already up-to-date"
|
||||||
|
}
|
||||||
|
|
||||||
|
TARGET="${1:-both}"
|
||||||
|
|
||||||
|
case "$TARGET" in
|
||||||
|
test) migrate_db "$TEST_DB" "test.db" ;;
|
||||||
|
prod) migrate_db "$PROD_DB" "posterg.db" ;;
|
||||||
|
both)
|
||||||
|
migrate_db "$TEST_DB" "test.db"
|
||||||
|
migrate_db "$PROD_DB" "posterg.db"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 [test|prod|both]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Binary file not shown.
BIN
storage/test.db
BIN
storage/test.db
Binary file not shown.
Reference in New Issue
Block a user