Switches the database from SQL Server to MariaDB
The target Pi only has MariaDB, and SQL Server has no ARM64 build at all - not a config problem, a real gap discovered during deployment setup. Swapped the EF Core provider, regenerated every migration, updated connection strings and the backup script everywhere they appear. Took two tries to land on a provider that actually works: Pomelo builds fine against this project's EF Core 10 packages but fails at runtime (it's compiled against 9's internal API surface, which moved in 10 wherever Identity/DataProtection force the newer packages). Oracle's official provider builds and migrates fine but has a real MariaDB bug in its own migration-lock code, reproduced against a live database. Kept Oracle's provider and worked around just that one broken method - everything else it does is correct - rather than give up more of the stack to chase a workaround. Verified against a real local MariaDB end to end: all three migrations applied, both hosts start clean, full suite still green.
This commit is contained in:
+32
-18
@@ -20,8 +20,9 @@ assumes already exists — `deploy-scp.yaml` (U6) never creates any of it.
|
||||
### 1.1 Prerequisites
|
||||
- .NET 10 runtime installed on the Pi (ASM-03) — the publish is framework-dependent
|
||||
(`infrastructure-design.md` § 1), so the Pi needs the runtime, not the full SDK
|
||||
- `sqlcmd` installed, for the backup script (§ 4) — e.g. `mssql-tools18` / `unixodbc` on Debian-based
|
||||
Raspberry Pi OS
|
||||
- `mariadb-client` (or `mariadb-dump`/`mysqldump` specifically) installed, for the backup script (§ 4)
|
||||
— already present on most Raspberry Pi OS images that also run `mariadb-server`; install
|
||||
`mariadb-client` explicitly if the dump tool isn't already there
|
||||
|
||||
### 1.2 Account Model (revised — `webadmin` is not the deploy account)
|
||||
|
||||
@@ -106,7 +107,7 @@ below):
|
||||
```ini
|
||||
ASPNETCORE_ENVIRONMENT=Production
|
||||
ASPNETCORE_URLS=http://localhost:<port>
|
||||
ConnectionStrings__DefaultConnection=Server=127.0.0.1,1433;User ID=<user>;Password=<password>;Database=SlpSoftware<Env>;TrustServerCertificate=True
|
||||
ConnectionStrings__DefaultConnection=Server=127.0.0.1;Port=3306;Database=SlpSoftware<Env>;Uid=<user>;Pwd=<password>
|
||||
JwtSettings__Secret=<secure-long-random-secret>
|
||||
JwtSettings__Issuer=SlpModularCms
|
||||
JwtSettings__Audience=SlpModularCmsPortal
|
||||
@@ -213,14 +214,15 @@ touch ~/.config/slpsoftware-db-backup.env
|
||||
chmod 600 ~/.config/slpsoftware-db-backup.env
|
||||
```
|
||||
```ini
|
||||
DB_SERVER=127.0.0.1,1433
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_NAME=SlpSoftwareProduction
|
||||
DB_USER=<a-login-with-backup-database-permission>
|
||||
DB_USER=<a-login-with-just-SELECT-LOCK-TABLES-permission>
|
||||
DB_PASSWORD=<password>
|
||||
```
|
||||
Kept **separate** from `shared/env` (§ 1.5) deliberately — the backup script needs its own
|
||||
credential, ideally scoped to just `BACKUP DATABASE` permission rather than the application's own
|
||||
data-access login.
|
||||
credential, ideally scoped to just read access (`SELECT`, `LOCK TABLES` — everything `mariadb-dump`
|
||||
needs) rather than the application's own data-access login.
|
||||
|
||||
### 1.9 Gitea Actions Variables and Secrets
|
||||
Set once, in this repository's Gitea Actions settings. This is exactly where the real path lives —
|
||||
@@ -286,22 +288,29 @@ if [[ ! -f "$CREDENTIALS_FILE" ]]; then
|
||||
fi
|
||||
# shellcheck source=/dev/null
|
||||
source "$CREDENTIALS_FILE"
|
||||
: "${DB_SERVER:?}" "${DB_NAME:?}" "${DB_USER:?}" "${DB_PASSWORD:?}"
|
||||
: "${DB_HOST:?}" "${DB_PORT:?}" "${DB_NAME:?}" "${DB_USER:?}" "${DB_PASSWORD:?}"
|
||||
|
||||
BACKUP_DIR="$HOME/backups/slpsoftware/${ENVIRONMENT}"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
TIMESTAMP=$(date -u +%Y%m%d%H%M%S)
|
||||
BACKUP_FILE="$BACKUP_DIR/${DB_NAME}-${TIMESTAMP}.bak"
|
||||
BACKUP_FILE="$BACKUP_DIR/${DB_NAME}-${TIMESTAMP}.sql.gz"
|
||||
|
||||
sqlcmd -S "$DB_SERVER" -U "$DB_USER" -P "$DB_PASSWORD" -C -Q \
|
||||
"BACKUP DATABASE [$DB_NAME] TO DISK = N'$BACKUP_FILE' WITH INIT, COMPRESSION"
|
||||
# --single-transaction: consistent snapshot without locking the tables for the whole dump duration
|
||||
# (InnoDB only — every table here is, since that's EF Core's MySQL-provider default).
|
||||
mariadb-dump \
|
||||
-h "$DB_HOST" -P "$DB_PORT" -u "$DB_USER" -p"$DB_PASSWORD" \
|
||||
--single-transaction --routines --triggers \
|
||||
"$DB_NAME" | gzip > "$BACKUP_FILE"
|
||||
|
||||
echo "Backup written to $BACKUP_FILE"
|
||||
|
||||
# Retention: keep the 7 most recent backups for this environment
|
||||
ls -1t "$BACKUP_DIR"/*.bak 2>/dev/null | tail -n +8 | xargs -r rm -f
|
||||
ls -1t "$BACKUP_DIR"/*.sql.gz 2>/dev/null | tail -n +8 | xargs -r rm -f
|
||||
```
|
||||
|
||||
`mariadb-dump` is MariaDB's own name for the tool (present since MariaDB 10.4-ish); if the host only
|
||||
has the older `mysqldump` name, substitute it — same tool, same flags.
|
||||
|
||||
```bash
|
||||
chmod +x ~/scripts/backup-slpmodularcms-db.sh
|
||||
```
|
||||
@@ -310,9 +319,13 @@ chmod +x ~/scripts/backup-slpmodularcms-db.sh
|
||||
```bash
|
||||
~/scripts/backup-slpmodularcms-db.sh production
|
||||
```
|
||||
Confirm a `.bak` file appears under `~/backups/slpsoftware/production/` and that `sqlcmd` didn't
|
||||
silently fail (the script uses `set -euo pipefail`, so a real SQL error does propagate as a non-zero
|
||||
exit — which fails the calling `deploy-scp.yaml` step, correctly blocking the deploy).
|
||||
Confirm a `.sql.gz` file appears under `~/backups/slpsoftware/production/` and that the dump didn't
|
||||
silently fail (the script uses `set -euo pipefail`, so a real error does propagate as a non-zero
|
||||
exit — which fails the calling `deploy-scp.yaml` step, correctly blocking the deploy). Worth a
|
||||
one-time restore rehearsal too — an untested backup is not a verified one:
|
||||
```bash
|
||||
gunzip -c ~/backups/slpsoftware/production/<file>.sql.gz | mariadb -h 127.0.0.1 -u root -p <a-scratch-database>
|
||||
```
|
||||
|
||||
## 5. Future: Switching Production to FTPS (Shared Hosting)
|
||||
|
||||
@@ -336,9 +349,10 @@ than the transport:
|
||||
`wwwroot/web/` persistence (FR-08, ASM-01) would need a **different** mechanism on such a host —
|
||||
e.g. never touching a specific subfolder during upload, rather than linking a persistent directory
|
||||
outside a swapped release tree, since "outside the release tree" may not be an available concept
|
||||
- **Database backup** — shared hosting frequently does not expose direct `sqlcmd`/SSH access at all;
|
||||
the backup step in `deploy-scp.yaml` (§ 4 script) would need to become either a provider-specific
|
||||
API call or a documented manual pre-production step (the FR-20 fallback U6 already designed for)
|
||||
- **Database backup** — shared hosting frequently does not expose direct `mariadb-dump`/SSH access
|
||||
at all; the backup step in `deploy-scp.yaml` (§ 4 script) would need to become either a
|
||||
provider-specific API call (e.g. a hosting-panel database backup feature) or a documented manual
|
||||
pre-production step (the FR-20 fallback U6 already designed for)
|
||||
|
||||
### 5.3 What building `deploy-ftps.yaml` would actually require
|
||||
1. A new reusable workflow implementing the **same five required inputs**
|
||||
|
||||
+1
-2
@@ -46,8 +46,7 @@ A backup is taken before every **production** deploy (`operations/deployment/dep
|
||||
§ 4, invoked by `deploy-scp.yaml` when `run_db_backup: true`). To restore:
|
||||
|
||||
```bash
|
||||
sqlcmd -S <server> -U <user> -P <password> -C -Q \
|
||||
"RESTORE DATABASE [SlpModularCmsProduction] FROM DISK = N'<path-to-backup>.bak' WITH REPLACE"
|
||||
gunzip -c <path-to-backup>.sql.gz | mariadb -h <server> -u <user> -p SlpSoftwareProduction
|
||||
```
|
||||
|
||||
Restoring a database backup and rolling back the application release are **independent actions** —
|
||||
|
||||
Reference in New Issue
Block a user