mirror of
https://github.com/unraid/api.git
synced 2026-01-10 18:50:11 -06:00
feat: better patch application
This commit is contained in:
@@ -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];
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
original
|
||||||
@@ -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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user