update: the api backs up before migrating; scripts/update.sh for compose installs

Docker only, never bare metal (owner, 2026-10-21). A new version with pending migrations backs
up first (database, voicemail, sounds, MOH) and stops if it can't (SKIP_BACKUP_BEFORE_MIGRATE
to override). update.sh: checks, changelog, backup (node dist/backup-cli.js), log compaction,
build while the old version runs, restart, self-check, and rollback with restore. Container
logs capped in compose. Admin guide: Updating, for compose and Portainer.
This commit is contained in:
iamdoubz committed 2026-10-21 11:15:40 -05:00
1 parent 8d5475cfbf
commit 54e1e201a1
9 files changed
+376 -6

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// migrationsPending (08 §3.1): the api backs up before running new migrations, not on every start.
import { cp, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { sql } from 'drizzle-orm';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { migrationsPending } from '../src/db/client.ts';
import { createTestDb, hasTestDb } from './db.ts';
const MIGRATIONS = fileURLToPath(new URL('../src/db/migrations', import.meta.url));
describe.skipIf(!hasTestDb)('migrationsPending', () => {
let t: Awaited<ReturnType<typeof createTestDb>>;
let newer: string;
beforeAll(async () => {
t = await createTestDb();
// This image's migrations plus one the database hasn't run: a newer version.
newer = await mkdtemp(join(tmpdir(), 'sp-mig-'));
await cp(MIGRATIONS, newer, { recursive: true });
const file = join(newer, 'meta', '_journal.json');
const j = JSON.parse(await readFile(file, 'utf8'));
j.entries.push({ ...j.entries.at(-1), idx: j.entries.length, tag: '9999_next' });
await writeFile(file, JSON.stringify(j));
});
afterAll(async () => {
await t?.drop();
await rm(newer, { recursive: true, force: true });
});
it('false when up to date, true when the image has more, false for an empty database', async () => {
expect(await migrationsPending(t.db)).toBe(false);
expect(await migrationsPending(t.db, newer)).toBe(true);
await t.db.execute(sql`drop schema drizzle cascade`);
expect(await migrationsPending(t.db, newer)).toBe(false);
});
});