rollbackMaterialization function
RecoveryReport
rollbackMaterialization({
- required ChangePlan plan,
- required String canonicalDestination,
- ApplyFileSystem fileSystem = const ApplyFileSystem(),
- int maxAttempts = 3,
- Duration initialBackoff = const Duration(milliseconds: 50),
- void sleep(
- Duration delay
Rolls back 0.1's materialize-only Apply into a fresh destination.
Every materialized target is newly created, so FR-117–FR-121 recovery only deletes and verifies those targets; there are no original bytes to restore.
Implementation
RecoveryReport rollbackMaterialization({
required ChangePlan plan,
required String canonicalDestination,
ApplyFileSystem fileSystem = const ApplyFileSystem(),
int maxAttempts = 3,
Duration initialBackoff = const Duration(milliseconds: 50),
void Function(Duration delay)? sleep,
}) {
if (maxAttempts < 1) {
throw ArgumentError.value(maxAttempts, 'maxAttempts', 'must be at least 1');
}
final pause = sleep ?? io.sleep;
final removed = <String>[];
final unresolved = <String>[];
final actions = <String>[];
final operations =
plan.operations
.where((operation) => operation.ownershipClass.isMaterialized)
.toList()
..sort((a, b) => a.logicalPath.compareTo(b.logicalPath));
for (final operation in operations) {
final target = applyPath(canonicalDestination, operation.logicalPath);
if (!fileSystem.fileExists(target)) continue;
var delay = initialBackoff;
var deleted = false;
for (var attempt = 0; attempt < maxAttempts; attempt++) {
try {
fileSystem.deleteFile(target);
} on io.FileSystemException {
// Verification below decides the outcome; deletion errors are retryable.
}
if (!fileSystem.fileExists(target)) {
removed.add(operation.logicalPath);
deleted = true;
break;
}
if (attempt + 1 < maxAttempts) {
pause(delay);
delay *= 2;
}
}
if (!deleted) {
unresolved.add(operation.logicalPath);
actions.add('Manually remove $target.');
}
}
return buildRecoveryReport(
restoredPaths: const [],
removedCreatedPaths: removed,
unresolvedPaths: unresolved,
manualActions: actions,
);
}