From 07f9110674142df8dcd99af1f1fcd81d3459ec76 Mon Sep 17 00:00:00 2001 From: Luca <102960244+Luca-Timo@users.noreply.github.com> Date: Mon, 1 Jun 2026 00:31:08 +0200 Subject: [PATCH] chore(migrations): renumber 108_add_backup_paths to 109 to avoid upstream collision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit upstream/beta independently shipped 108_seed_sl_email_template_translations.js (Slovenian email template translations) using the migration number this branch had already claimed for 108_add_backup_paths.js. Knex's filename-based ordering would have caused both to attempt the slot at merge time. Renamed via `git mv` so file history is preserved. All five references updated in lockstep: - backend/src/services/_backupPathsBoot.js (require + comments) - backend/src/services/backupService.js (LEGACY_BACKUP_PATHS comment) - 3 integration test files (require + "migration 108" prose) - migration's own header comment, with a paragraph explaining the rename so reviewers don't wonder why the number jumped **No data-migration impact for installs that already ran the 108-named version** (Ralf's beta, primarily): the migration's body is idempotent — createTable is guarded by `hasTable`, and the seed uses `onConflict('path').ignore()`. So when 109 runs against an install whose backup_paths table is already populated, both the schema step and the seed step no-op cleanly. The orphaned `108_add_backup_paths.js` row in the `migrations` tracking table sits harmlessly alongside the new `109_add_backup_paths.js` row. No data lost, no double-insert, no schema drift. Mechanical rename ahead of the PR opening. --- .../__tests__/integration/adminBackupCoverage.test.js | 2 +- .../backupService.configurableWalker.test.js | 6 +++--- .../integration/backupService.perPathStats.test.js | 6 +++--- ...108_add_backup_paths.js => 109_add_backup_paths.js} | 10 +++++++++- backend/src/services/_backupPathsBoot.js | 10 +++++----- backend/src/services/backupService.js | 2 +- 6 files changed, 22 insertions(+), 14 deletions(-) rename backend/migrations/core/{108_add_backup_paths.js => 109_add_backup_paths.js} (91%) diff --git a/backend/__tests__/integration/adminBackupCoverage.test.js b/backend/__tests__/integration/adminBackupCoverage.test.js index ab10c160..18fe862a 100644 --- a/backend/__tests__/integration/adminBackupCoverage.test.js +++ b/backend/__tests__/integration/adminBackupCoverage.test.js @@ -72,7 +72,7 @@ describe('GET /api/admin/system-health/backup-coverage', () => { async function restoreDefaultPaths() { await db('backup_paths').del(); - const { DEFAULT_PATHS } = require('../../migrations/core/108_add_backup_paths'); + const { DEFAULT_PATHS } = require('../../migrations/core/109_add_backup_paths'); await db('backup_paths').insert(DEFAULT_PATHS.map((row) => ({ ...row, created_at: new Date(), diff --git a/backend/__tests__/integration/backupService.configurableWalker.test.js b/backend/__tests__/integration/backupService.configurableWalker.test.js index 65683046..583ab504 100644 --- a/backend/__tests__/integration/backupService.configurableWalker.test.js +++ b/backend/__tests__/integration/backupService.configurableWalker.test.js @@ -1,7 +1,7 @@ /** * Pins the Stage-B refactor that lifted the file-backup walker's * subdirectory list out of hard-coded JS into the `backup_paths` - * table seeded by migration 108. + * table seeded by migration 109. * * Scenarios: * 1. Walker reads canonical seed → all 7 default subdirs walked @@ -53,7 +53,7 @@ describe('backupService — configurable walker (backup_paths)', () => { await db('backup_paths').del(); const { DEFAULT_PATHS, - } = require('../../migrations/core/108_add_backup_paths'); + } = require('../../migrations/core/109_add_backup_paths'); await db('backup_paths').insert(DEFAULT_PATHS.map((row) => ({ ...row, created_at: new Date(), @@ -61,7 +61,7 @@ describe('backupService — configurable walker (backup_paths)', () => { }))); }); - it('migration 108 seeds the canonical 7 paths', async () => { + it('migration 109 seeds the canonical 7 paths', async () => { const rows = await db('backup_paths').orderBy('display_order', 'asc').select(); expect(rows.map((r) => r.path)).toEqual([ 'events/active', diff --git a/backend/__tests__/integration/backupService.perPathStats.test.js b/backend/__tests__/integration/backupService.perPathStats.test.js index 10a4366e..3e65ca12 100644 --- a/backend/__tests__/integration/backupService.perPathStats.test.js +++ b/backend/__tests__/integration/backupService.perPathStats.test.js @@ -60,8 +60,8 @@ describe('backupService — per-Stage-B-path statistics', () => { ]).onConflict('setting_key').merge(); fs.mkdirSync(path.join(storagePath, 'destination'), { recursive: true }); - // Restore canonical backup_paths from migration 108 - const { DEFAULT_PATHS } = require('../../migrations/core/108_add_backup_paths'); + // Restore canonical backup_paths from migration 109 + const { DEFAULT_PATHS } = require('../../migrations/core/109_add_backup_paths'); await db('backup_paths').del(); await db('backup_paths').insert(DEFAULT_PATHS.map((row) => ({ ...row, @@ -170,7 +170,7 @@ describe('backupService — per-Stage-B-path statistics', () => { // — once via each path. Per-path stats then attribute the file to the // longest-prefix-matching path BOTH times, producing inflated counts. // -// The canonical seed in migration 108 contains no overlapping pairs, +// The canonical seed in migration 109 contains no overlapping pairs, // so this isn't exercised in practice. But an admin who hand-adds a // broad row that overlaps an existing nested one will see double // counts in their next backup's statistics + the destination will diff --git a/backend/migrations/core/108_add_backup_paths.js b/backend/migrations/core/109_add_backup_paths.js similarity index 91% rename from backend/migrations/core/108_add_backup_paths.js rename to backend/migrations/core/109_add_backup_paths.js index 5fddccf6..544f0151 100644 --- a/backend/migrations/core/108_add_backup_paths.js +++ b/backend/migrations/core/109_add_backup_paths.js @@ -1,5 +1,13 @@ /** - * Migration 108 — config-driven backup walker. + * Migration 109 — config-driven backup walker. + * + * (Originally numbered 108 on bugfix/crm-backup. Renumbered to 109 + * before merge because upstream/beta independently shipped + * 108_seed_sl_email_template_translations.js. The createTable is + * idempotent via `hasTable` guard and the seed uses + * `onConflict('path').ignore()`, so beta installs that ran the + * 108-named version of this file get a harmless no-op when 109 + * runs against the already-seeded table.) * * Stage B of the three-stage backup-hardening plan. The file-backup * walker (`getFilesToBackupInternal` in backupService.js) historically diff --git a/backend/src/services/_backupPathsBoot.js b/backend/src/services/_backupPathsBoot.js index d1c8ece1..a3942fbd 100644 --- a/backend/src/services/_backupPathsBoot.js +++ b/backend/src/services/_backupPathsBoot.js @@ -4,7 +4,7 @@ * **Why this exists** * * Knex won't re-run an applied migration, so once migration - * 108_add_backup_paths.js has run, any later default we want to add + * 109_add_backup_paths.js has run, any later default we want to add * (a new subdirectory shipped by a future feature) would never reach * already-deployed installs. The historical fix for this kind of * "schema is fine, seed drifted" problem is the boot-time self-heal @@ -14,7 +14,7 @@ * * **Authoritative list** * - * The list of defaults lives on migration 108 itself + * The list of defaults lives on migration 109 itself * (`DEFAULT_PATHS` export) — one source of truth that both the * migration and this seeder read. Tests assert these two stay in * lockstep. @@ -22,14 +22,14 @@ * **Failure semantics** * * If the table doesn't exist yet (migrations haven't run, fresh - * install before migration 108 lands, etc.) we no-op and log. The + * install before migration 109 lands, etc.) we no-op and log. The * walker has a hard-coded `LEGACY_DEFAULTS` fallback for the same * reason — defense in depth so "Run Backup Now" can never silently * ship a files-only manifest because of a seed issue. See * `backupService.js` getFilesToBackupInternal. */ -const { DEFAULT_PATHS } = require('../../migrations/core/108_add_backup_paths'); +const { DEFAULT_PATHS } = require('../../migrations/core/109_add_backup_paths'); let booted = false; @@ -45,7 +45,7 @@ async function seedBackupPathsAtBoot(db, logger) { if (booted) return { seeded: [] }; if (!(await db.schema.hasTable('backup_paths'))) { - log.warn('backup_paths table missing at boot — self-heal skipped (migration 108 may not have run yet)'); + log.warn('backup_paths table missing at boot — self-heal skipped (migration 109 may not have run yet)'); return { seeded: [] }; } diff --git a/backend/src/services/backupService.js b/backend/src/services/backupService.js index dd90332c..6e83ba94 100644 --- a/backend/src/services/backupService.js +++ b/backend/src/services/backupService.js @@ -419,7 +419,7 @@ async function scanDirectory(dirPath, fileList, basePath, excludePatterns = []) /** * Hard-coded fallback when `backup_paths` is missing/empty. Mirrors - * the canonical seed in migration 108 — kept here as defense in depth + * the canonical seed in migration 109 — kept here as defense in depth * so the walker can never silently degrade to "no directories scanned" * because of a seed problem. *