feat: better patch application

This commit is contained in:
Eli Bosley
2025-02-04 10:24:08 -05:00
parent a12181a5e0
commit 445f3b50b1
5 changed files with 28 additions and 26 deletions

View File

@@ -68,7 +68,7 @@ export abstract class FileModification {
} }
} }
private async applyPatch(patchContents: string): Promise<void> { private async applyPatch(patchContents: string): Promise<string> {
if (!patchContents.trim()) { if (!patchContents.trim()) {
throw new Error('Patch contents are empty'); throw new Error('Patch contents are empty');
} }
@@ -82,22 +82,28 @@ export abstract class FileModification {
throw new Error(`Failed to apply patch to ${this.filePath}`); throw new Error(`Failed to apply patch to ${this.filePath}`);
} }
await writeFile(this.filePath, results); await writeFile(this.filePath, results);
return results;
} }
async apply(): Promise<void> { /**
* Apply the patch for the target file
* @returns
*/
async apply(): Promise<string> {
try { try {
const savedPatch = await this.loadPatchedFilePatch(this.filePath); // First attempt to rollback an existing patch saved on disk (if the file has already been modified by us, unclean shutdown)
if (savedPatch) { await this.rollback(true).catch((err) => {
// Rollback the saved patch before applying the new patch this.logger.debug(
await this.rollback(); `Failed to rollback patch for ${this.id}: ${err}, may not have been applied yet`
} );
});
// First attempt to apply the patch that was generated // First attempt to apply the patch that was generated
const staticPatch = await this.getPregeneratedPatch(); const staticPatch = await this.getPregeneratedPatch();
if (staticPatch) { if (staticPatch) {
try { try {
await this.applyPatch(staticPatch); await this.applyPatch(staticPatch);
await this.savePatch(staticPatch); await this.savePatch(staticPatch);
return; return staticPatch;
} catch (error) { } catch (error) {
this.logger.error( this.logger.error(
`Failed to apply static patch to ${this.filePath}, continuing with dynamic patch` `Failed to apply static patch to ${this.filePath}, continuing with dynamic patch`
@@ -107,25 +113,35 @@ export abstract class FileModification {
const patchContents = await this.generatePatch(); const patchContents = await this.generatePatch();
await this.applyPatch(patchContents); await this.applyPatch(patchContents);
await this.savePatch(patchContents); await this.savePatch(patchContents);
return patchContents;
} catch (err) { } catch (err) {
this.logger.error(`Failed to apply patch to ${this.filePath}: ${err}`); this.logger.error(`Failed to apply patch to ${this.filePath}: ${err}`);
throw err;
} }
} }
async rollback(): Promise<void> { /**
let patch: string; * Rollback the patch for the target file
* @param useSavedPatchOnly - If true, only use the saved patch file if it exists, otherwise use the file or generate a new patch
*/
async rollback(useSavedPatchOnly: boolean = false): Promise<void> {
let patch: string | null = null;
// Try to load saved patch first // Try to load saved patch first
const savedPatch = await this.loadPatchedFilePatch(this.filePath); const savedPatch = await this.loadPatchedFilePatch(this.filePath);
if (savedPatch) { if (savedPatch) {
this.logger.debug(`Using saved patch file for ${this.id}`); this.logger.debug(`Using saved patch file for ${this.id}`);
patch = savedPatch; patch = savedPatch;
} else { } else if (!useSavedPatchOnly) {
this.logger.debug(`No saved patch found for ${this.id}, generating new patch`); this.logger.debug(`No saved patch found for ${this.id}, generating new patch`);
const patchContents = await this.generatePatch(); const patchContents = await this.generatePatch();
patch = patchContents; patch = patchContents;
} }
if (!patch) {
throw new Error(`No patch found to rollback for ${this.id}`);
}
const currentContent = await readFile(this.filePath, 'utf8'); const currentContent = await readFile(this.filePath, 'utf8');
const parsedPatch = parsePatch(patch)[0]; const parsedPatch = parsePatch(patch)[0];

View File

@@ -1,9 +0,0 @@
Index: text-patch-file.txt
===================================================================
--- text-patch-file.txt
+++ text-patch-file.txt
@@ -1,1 +1,1 @@
-original
\ No newline at end of file
+modified
\ No newline at end of file

View File

@@ -132,12 +132,6 @@ describe.sequential('FileModificationService', () => {
afterEach(async () => { afterEach(async () => {
await service.rollbackAll(); await service.rollbackAll();
// Clean up the fixture file
try {
await fs.unlink(FIXTURE_PATH);
} catch (error) {
console.error('Failed to clean up fixture file', error);
}
vi.clearAllMocks(); vi.clearAllMocks();
}); });
}); });