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:
1 parent
8d5475cfbf
commit
54e1e201a1
9 files changed
+376
-6
No files matched your search
@@ -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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user