mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
fix(migrations): let force-retry escape completed ledger entries (#2616)
statusForVersion short-circuited on any 'complete' entry before checking the trailing 'retry' marker, so --force-retry appended an inert row and a version marked complete with zero work done could never be re-run without hand-editing completed.jsonl. Check retry-latest first: an explicit --force-retry now yields 'pending' even past an earlier 'complete', while a stray 'partial' after 'complete' still cannot regress the version.
This commit is contained in:
@@ -133,14 +133,15 @@ function indexCompleted(entries: CompletedMigrationEntry[]): CompletedIndex {
|
||||
* Returns the resolved status for a migration based on its entries.
|
||||
*
|
||||
* Semantics (Bug 3 — keep "complete wins" safety):
|
||||
* - If any entry is `complete`, the version is complete. Terminal state.
|
||||
* - Otherwise, if the latest entry is `retry`, the version is pending
|
||||
* (user requested a fresh attempt).
|
||||
* - If the latest entry is `retry`, the version is pending. This is the
|
||||
* explicit escape hatch written by `--force-retry`, and it overrides an
|
||||
* earlier `complete` entry without hand-editing the ledger.
|
||||
* - Otherwise, if any entry is `complete`, the version is complete.
|
||||
* - Otherwise, if any entry is `partial`, the version is partial.
|
||||
* - Otherwise, pending.
|
||||
*
|
||||
* `complete` never regresses. A later accidental `partial` append cannot
|
||||
* undo a completed migration.
|
||||
* `complete` never regresses accidentally. A later `partial` append cannot
|
||||
* undo a completed migration; only a trailing, explicit `retry` marker can.
|
||||
*/
|
||||
function statusForVersion(
|
||||
version: string,
|
||||
@@ -148,9 +149,9 @@ function statusForVersion(
|
||||
): 'complete' | 'partial' | 'pending' | 'wedged' {
|
||||
const entries = idx.byVersion.get(version) ?? [];
|
||||
if (entries.length === 0) return 'pending';
|
||||
if (entries.some(e => e.status === 'complete')) return 'complete';
|
||||
const latest = entries[entries.length - 1];
|
||||
if (latest.status === 'retry') return 'pending';
|
||||
if (entries.some(e => e.status === 'complete')) return 'complete';
|
||||
// Bug 3 attempt cap — count consecutive partials from the end (stopping
|
||||
// at any 'retry' or 'complete'). If we hit MAX_CONSECUTIVE_PARTIALS,
|
||||
// the migration is wedged and needs explicit --force-retry to try again.
|
||||
|
||||
@@ -167,6 +167,41 @@ describe('buildPlan — diff against completed + installed VERSION', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('force-retry escape hatch', () => {
|
||||
test("complete then retry-latest → pending and buildPlan lists the version as pending", () => {
|
||||
const idx = indexCompleted([
|
||||
{ version: '0.11.0', status: 'complete' },
|
||||
{ version: '0.11.0', status: 'retry' },
|
||||
]);
|
||||
|
||||
expect(statusForVersion('0.11.0', idx)).toBe('pending');
|
||||
const plan = buildPlan(idx, '0.11.1', '0.11.0');
|
||||
expect(plan.pending.map(m => m.version)).toEqual(['0.11.0']);
|
||||
expect(plan.applied).toEqual([]);
|
||||
expect(plan.partial).toEqual([]);
|
||||
expect(plan.wedged).toEqual([]);
|
||||
});
|
||||
|
||||
test('complete then stray partial without retry → still complete', () => {
|
||||
const idx = indexCompleted([
|
||||
{ version: '0.11.0', status: 'complete' },
|
||||
{ version: '0.11.0', status: 'partial' },
|
||||
]);
|
||||
|
||||
expect(statusForVersion('0.11.0', idx)).toBe('complete');
|
||||
});
|
||||
|
||||
test('retry followed by a newer complete → complete', () => {
|
||||
const idx = indexCompleted([
|
||||
{ version: '0.11.0', status: 'complete' },
|
||||
{ version: '0.11.0', status: 'retry' },
|
||||
{ version: '0.11.0', status: 'complete' },
|
||||
]);
|
||||
|
||||
expect(statusForVersion('0.11.0', idx)).toBe('complete');
|
||||
});
|
||||
});
|
||||
|
||||
// v0.36.1.x (cherry-pick #1062): list, dry-run, and "all migrations up to
|
||||
// date" paths must exit 0 so shell scripts gating on the exit code work.
|
||||
// Pre-fix, these `return` statements left the CLI dispatcher's implicit
|
||||
|
||||
Reference in New Issue
Block a user