fix(backups): preserve relative include/exclude pattern behavior
This commit is contained in:
parent
30685c8481
commit
7879d88a54
2 changed files with 33 additions and 29 deletions
|
|
@ -21,15 +21,17 @@ afterEach(() => {
|
|||
});
|
||||
|
||||
describe("executeBackup - include / exclude patterns", () => {
|
||||
test("should correctly build include and exclude patterns by joining with volume path", async () => {
|
||||
test("should correctly build include and exclude patterns", async () => {
|
||||
// arrange
|
||||
const volume = await createTestVolume();
|
||||
const repository = await createTestRepository();
|
||||
const volumePath = getVolumePath(volume);
|
||||
|
||||
const schedule = await createTestBackupSchedule({
|
||||
volumeId: volume.id,
|
||||
repositoryId: repository.id,
|
||||
includePatterns: ["include1", "subdir/include2"],
|
||||
excludePatterns: ["exclude1", "subdir/exclude2"],
|
||||
includePatterns: ["*.zip", "/Photos", "!/Temp", "!*.log"],
|
||||
excludePatterns: [".DS_Store", "/Config", "!/Important", "!*.tmp"],
|
||||
excludeIfPresent: [".nobackup"],
|
||||
});
|
||||
|
||||
|
|
@ -37,14 +39,12 @@ describe("executeBackup - include / exclude patterns", () => {
|
|||
await backupsService.executeBackup(schedule.id);
|
||||
|
||||
// assert
|
||||
const volumePath = getVolumePath(volume);
|
||||
|
||||
expect(backupMock).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
volumePath,
|
||||
expect.objectContaining({
|
||||
include: [path.join(volumePath, "include1"), path.join(volumePath, "subdir/include2")],
|
||||
exclude: [path.join(volumePath, "exclude1"), path.join(volumePath, "subdir/exclude2")],
|
||||
include: ["*.zip", path.join(volumePath, "Photos"), `!${path.join(volumePath, "Temp")}`, "!*.log"],
|
||||
exclude: [".DS_Store", path.join(volumePath, "Config"), `!${path.join(volumePath, "Important")}`, "!*.tmp"],
|
||||
excludeIfPresent: [".nobackup"],
|
||||
}),
|
||||
);
|
||||
|
|
@ -88,11 +88,12 @@ describe("executeBackup - include / exclude patterns", () => {
|
|||
|
||||
const alreadyJoinedInclude = path.join(volumePath, "already/joined");
|
||||
const relativeInclude = "relative/include";
|
||||
const anchoredInclude = "/anchored/include";
|
||||
|
||||
const schedule = await createTestBackupSchedule({
|
||||
volumeId: volume.id,
|
||||
repositoryId: repository.id,
|
||||
includePatterns: [alreadyJoinedInclude, relativeInclude],
|
||||
includePatterns: [alreadyJoinedInclude, relativeInclude, anchoredInclude],
|
||||
});
|
||||
|
||||
// act
|
||||
|
|
@ -103,7 +104,7 @@ describe("executeBackup - include / exclude patterns", () => {
|
|||
expect.anything(),
|
||||
volumePath,
|
||||
expect.objectContaining({
|
||||
include: [alreadyJoinedInclude, path.join(volumePath, relativeInclude)],
|
||||
include: [alreadyJoinedInclude, relativeInclude, path.join(volumePath, "anchored/include")],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,6 +33,27 @@ const calculateNextRun = (cronExpression: string): number => {
|
|||
}
|
||||
};
|
||||
|
||||
const processPattern = (pattern: string, volumePath: string): string => {
|
||||
let isNegated = false;
|
||||
let p = pattern;
|
||||
|
||||
if (p.startsWith("!")) {
|
||||
isNegated = true;
|
||||
p = p.slice(1);
|
||||
}
|
||||
|
||||
if (p.startsWith(volumePath)) {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if (p.startsWith("/")) {
|
||||
const processed = path.join(volumePath, p.slice(1));
|
||||
return isNegated ? `!${processed}` : processed;
|
||||
}
|
||||
|
||||
return pattern;
|
||||
};
|
||||
|
||||
const listSchedules = async () => {
|
||||
const schedules = await db.query.backupSchedulesTable.findMany({
|
||||
with: {
|
||||
|
|
@ -259,16 +280,7 @@ const executeBackup = async (scheduleId: number, manual = false) => {
|
|||
};
|
||||
|
||||
if (schedule.excludePatterns && schedule.excludePatterns.length > 0) {
|
||||
const excludePatterns = [];
|
||||
for (const pattern of schedule.excludePatterns) {
|
||||
if (!pattern.startsWith(volumePath)) {
|
||||
excludePatterns.push(path.join(volumePath, pattern));
|
||||
} else {
|
||||
excludePatterns.push(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
backupOptions.exclude = excludePatterns;
|
||||
backupOptions.exclude = schedule.excludePatterns.map((p) => processPattern(p, volumePath));
|
||||
}
|
||||
|
||||
if (schedule.excludeIfPresent && schedule.excludeIfPresent.length > 0) {
|
||||
|
|
@ -276,16 +288,7 @@ const executeBackup = async (scheduleId: number, manual = false) => {
|
|||
}
|
||||
|
||||
if (schedule.includePatterns && schedule.includePatterns.length > 0) {
|
||||
const includePatterns = [];
|
||||
for (const pattern of schedule.includePatterns) {
|
||||
if (!pattern.startsWith(volumePath)) {
|
||||
includePatterns.push(path.join(volumePath, pattern));
|
||||
} else {
|
||||
includePatterns.push(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
backupOptions.include = includePatterns;
|
||||
backupOptions.include = schedule.includePatterns.map((p) => processPattern(p, volumePath));
|
||||
}
|
||||
|
||||
const releaseBackupLock = await repoMutex.acquireShared(repository.id, `backup:${volume.name}`);
|
||||
|
|
|
|||
Loading…
Reference in a new issue