feat(migrations): add migration system for v3→v4 config format

- Create abstract BaseMigration class with order, shouldRun(), migrate()
- Add MigrationRunner to execute migrations in order
- Add Migration v1→v2 (snmp → upsDevices)
- Add Migration v3→v4 (upsList → upsDevices)
- Update INupstConfig with version field
- Update loadConfig() to run migrations automatically
- Update saveConfig() to ensure version field and remove legacy fields
- Update Docker test scripts to use real UPS data from .nogit/env.json
- Remove colors.bright (not available in @std/fmt/colors)

Config migrations allow users to jump versions (e.g., v1→v4) with all
intermediate migrations running automatically. Version field tracks
config format for future migrations.
This commit is contained in:
2025-10-19 20:41:09 +00:00
parent 49f7a7da8b
commit 016681b77b
9 changed files with 310 additions and 64 deletions

View File

@@ -0,0 +1,54 @@
/**
* Abstract base class for configuration migrations
*
* Each migration represents an upgrade from one config version to another.
* Migrations run in order based on the `order` field, allowing users to jump
* multiple versions (e.g., v1 → v4 runs migrations 2, 3, and 4).
*/
export abstract class BaseMigration {
/**
* Migration order number
* - Order 2: v1 → v2
* - Order 3: v2 → v3
* - Order 4: v3 → v4
* etc.
*/
abstract readonly order: number;
/**
* Source version this migration upgrades from
* e.g., "1.x", "3.x"
*/
abstract readonly fromVersion: string;
/**
* Target version this migration upgrades to
* e.g., "2.0", "4.0"
*/
abstract readonly toVersion: string;
/**
* Check if this migration should run on the given config
*
* @param config - Raw configuration object to check
* @returns True if migration should run, false otherwise
*/
abstract shouldRun(config: any): Promise<boolean>;
/**
* Perform the migration on the given config
*
* @param config - Raw configuration object to migrate
* @returns Migrated configuration object
*/
abstract migrate(config: any): Promise<any>;
/**
* Get human-readable name for this migration
*
* @returns Migration name
*/
getName(): string {
return `Migration ${this.fromVersion}${this.toVersion}`;
}
}