/** * Snapshot-Based Plugin Rollback Workflow * * REBUILD (roadmap Phase 4.3, assignment B4). The previous rollback system * (VersionHistoryService entries + `.bak` files in ~/.pluginator/backups/ * rollback/) was dead end-to-end — no production code ever recorded history * or created backups (audit workflows-infra.md §3.1) — so every real rollback * returned "Version X not found in history". This rebuild restores from the * pre-update snapshots created by `src/core/snapshot-store.ts` instead. * * Safety contract (every rule from the audit's anti-pattern list): * - PROD is read-only by default: `target` defaults to `'test'` and the * caller must explicitly pass `'prod'` (the old CLI restore defaulted to * PROD with overwrite — the opposite of the house principle). * - Validation BEFORE mutation: the snapshot's recorded sha256 must match * the archived JAR, and the data archive must list cleanly, before * anything in the server directory is touched. Mismatch → abort untouched. * - NEVER delete: this module performs ZERO file deletions (grep-pinned in * tests). The current JAR and data folder are renamed aside to * `.pre-rollback-` — recoverable on ANY failure. On mid-flight * failure every rename is reverted and partially-restored artifacts are * renamed to `.failed-rollback-` for inspection. * - Journaled via `withJournal('rollback', ...)` so a crash mid-rollback is * detected by startup recovery. * - No silent failure: skipped data restores carry an explicit reason in the * result; config-file update failures are listed per-path. * * @since v2.12.4 (rebuilt; original dead implementation since v1.22.0) */ import { type ConfigDiff } from '../core/config-sync/config-diff.js'; import { type SnapshotManifest, type SnapshotStore } from '../core/snapshot-store.js'; /** Which server the rollback mutates. TEST is the active workspace (house principle). */ export type RollbackTarget = 'test' | 'prod'; /** Options for {@link rollbackPlugin} */ export interface RollbackPluginOptions { /** Snapshot to restore (default: the newest snapshot for the plugin) */ snapshotId?: string; /** Restore the data folder too (default true — jar-only rollback is silently lossy after config migrations) */ restoreData?: boolean; /** * Target server — selects which plugins config file gets the version * update and labels the result. Defaults to 'test'; 'prod' must be an * explicit caller decision. */ target?: RollbackTarget; /** Plugins directory of the target server (where the JAR + data folder live) */ pluginsDir: string; /** Update the plugins config file(s) with the restored version (default true) */ updateConfigVersion?: boolean; /** Override the config files to update (tests; defaults derived from target) */ configPaths?: string[]; /** Snapshot store override (tests pin a temp-rooted store) */ store?: SnapshotStore; } /** Result of {@link rollbackPlugin} — never throws; failures carry a reason */ export interface RollbackPluginResult { success: boolean; pluginName: string; target: RollbackTarget; /** Snapshot that was (or was attempted to be) restored */ snapshotId?: string; /** Version restored by the rollback (the snapshot's pre-update version) */ restoredVersion?: string; /** Version the rollback replaced (the update the snapshot preceded) */ replacedVersion?: string; /** Path of the restored JAR in the target plugins dir */ restoredJarPath?: string; /** Path of the restored data folder */ restoredDataDir?: string; /** Where the previously-current JAR was renamed aside (never deleted) */ asideJarPath?: string; /** Where the previously-current data folder was renamed aside (never deleted) */ asideDataDir?: string; /** Whether the data folder was restored */ dataRestored: boolean; /** Explicit reason when data was not restored (no silent skips) */ dataSkippedReason?: string; /** Config files whose version field was updated */ configFilesUpdated: string[]; /** Per-file config update failures (non-fatal, but never silent) */ configUpdateErrors: string[]; /** * True when the rolled-back version's promotion record was marked * superseded (TEST rollbacks only — promotion records track the TEST * server). False when there was nothing to supersede; the reason is * logged, never fatal (Phase 5 live-E2E find). */ promotionRecordSuperseded?: boolean; error?: string; } /** File-level change between a snapshot's config files and the current data folder */ export interface SnapshotConfigFileChange { /** Path relative to the data folder root */ file: string; /** added = created since the snapshot; removed = deleted since the snapshot */ status: 'changed' | 'added' | 'removed'; /** Line-level diff for changed files (added lines = added since the snapshot) */ diff?: ConfigDiff; } /** Result of {@link diffConfigChanges} */ export interface SnapshotConfigDiffResult { pluginName: string; snapshotId: string; /** False when the snapshot is jar-only (see summary/error for the reason) */ dataIncluded: boolean; files: SnapshotConfigFileChange[]; counts: { changed: number; added: number; removed: number; unchanged: number; }; summary: string; error?: string; } /** Config-like file extensions compared by {@link diffConfigChanges} */ export declare const DEFAULT_CONFIG_DIFF_EXTENSIONS: string[]; /** * List available snapshots, newest first (all plugins when no name given). */ export declare function listSnapshots(pluginName?: string, store?: SnapshotStore): Promise; /** * Roll a plugin back to a pre-update snapshot. * * Restores the archived JAR (after verifying it still matches the * manifest's sha256) and, by default, the data folder. The current JAR and * data folder are renamed aside — never deleted — so any failure leaves a * fully recoverable state. */ export declare function rollbackPlugin(pluginName: string, options: RollbackPluginOptions): Promise; /** * Post-update config-change surfacing (roadmap 4.3): report which config * files the new version changed compared to a pre-update snapshot. Intended * to run after the update is applied and the server has run (caller-invoked). * * Reuses `config-diff.ts` line diffing; in the per-file diff, `added` lines * are lines added SINCE the snapshot and `removed` lines existed in the * snapshot but are gone now. Reads files only — mutates nothing. */ export declare function diffConfigChanges(pluginName: string, snapshotId: string, options: { currentDataDir: string; store?: SnapshotStore; extensions?: string[]; }): Promise; //# sourceMappingURL=rollback.d.ts.map