Wire pre-migration auto-backup with named API and manifest (B2/backup precursor)
Before running any pending migration, take a synchronous snapshot of the profile to backups/pre-migration-v<target>-<ts>/ and write a backup-manifest.json recording reason, schema version, app version, and git SHA. A failed migration now has a well-labelled recovery point instead of a half-upgraded profile. - createAutomaticBackup(reason): synchronous snapshot via VACUUM INTO + recursive cpSync of logs/artifacts, plus JSON manifest. No fallback — failure throws so the caller refuses to touch the schema. - runMigrations accepts createAutomaticBackup and invokes it when any migration is pending. No optional chaining; callers must supply it. - AppDatabase constructor takes appVersion so main-runtime stamps the manifest with app.getVersion().
This commit is contained in:
1 parent
08427931b6
commit
dade5eb333
5 files changed
+125
-6
No files matched your search
@@ -43,6 +43,7 @@ export class AppDatabaseBase {
|
||||
readonly logsDir: string;
|
||||
readonly artifactsDir: string;
|
||||
readonly backupsDir: string;
|
||||
readonly appVersion: string;
|
||||
readonly vault: SecretVault;
|
||||
protected db: DatabaseSync;
|
||||
|
||||
@@ -76,12 +77,16 @@ export class AppDatabaseBase {
|
||||
public readonly credentialsTable: ReturnType<typeof buildCredentialMethods>;
|
||||
public readonly modelCapabilities: ReturnType<typeof buildModelCapabilityMethods>;
|
||||
|
||||
constructor(rootDir: string, options: { isPackaged?: boolean } = {}) {
|
||||
constructor(rootDir: string, options: { isPackaged?: boolean; appVersion?: string } = {}) {
|
||||
this.profileDir = rootDir;
|
||||
this.dbPath = join(rootDir, "profile.sqlite");
|
||||
this.logsDir = join(rootDir, "logs");
|
||||
this.artifactsDir = join(rootDir, "artifacts");
|
||||
this.backupsDir = join(rootDir, "backups");
|
||||
// Stamped into backup manifests so an operator looking at an old
|
||||
// snapshot can tell which build produced it. Callers (bootstrap)
|
||||
// pass `electron.app.getVersion()`; tests omit it.
|
||||
this.appVersion = options.appVersion ?? "unknown";
|
||||
mkdirSync(rootDir, { recursive: true });
|
||||
mkdirSync(this.logsDir, { recursive: true });
|
||||
mkdirSync(this.artifactsDir, { recursive: true });
|
||||
@@ -159,6 +164,7 @@ export class AppDatabaseBase {
|
||||
profileDir: this.profileDir, dbPath: this.dbPath,
|
||||
logsDir: this.logsDir, artifactsDir: this.artifactsDir,
|
||||
backupsDir: this.backupsDir,
|
||||
appVersion: this.appVersion,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -167,7 +173,7 @@ export class AppDatabaseBase {
|
||||
init() {
|
||||
const ra = <T>(sql: string, ...params: SQLInputValue[]) => this.readAll<T>(sql, ...params);
|
||||
const tx = <T>(op: () => T) => this.transaction(op);
|
||||
initializeDatabase(this.db, ra, tx);
|
||||
initializeDatabase(this.db, ra, tx, (reason) => this.backupMod.createAutomaticBackup(reason));
|
||||
this.connectorsCrud.seedConnectors();
|
||||
this.modelCapabilities.seedModelCapabilities(MODEL_CAPABILITY_SEED);
|
||||
// Crash-recovery: a run still at `running` on boot means the worker
|
||||
|
||||
Reference in new issue
Block a user