fix(auto-replace): use regex replace to avoid infinite loops

This commit is contained in:
Rhys Arkins 2020-03-01 20:52:43 +01:00
parent 435096d97e
commit 6f46153e09
2 changed files with 27 additions and 22 deletions

View file

@ -2,6 +2,7 @@ import { logger } from '../../logger';
import { get } from '../../manager'; import { get } from '../../manager';
import { WORKER_FILE_UPDATE_FAILED } from '../../constants/error-messages'; import { WORKER_FILE_UPDATE_FAILED } from '../../constants/error-messages';
import { matchAt, replaceAt } from '../../util/string'; import { matchAt, replaceAt } from '../../util/string';
import { regEx } from '../../util/regex';
export async function confirmIfDepUpdated( export async function confirmIfDepUpdated(
upgrade, upgrade,
@ -59,6 +60,10 @@ export async function checkBranchDepsMatchBaseDeps(
} }
} }
function escapeRegExp(input: string): string {
return input.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
export async function doAutoReplace( export async function doAutoReplace(
upgrade, upgrade,
existingContent: string, existingContent: string,
@ -77,27 +82,33 @@ export async function doAutoReplace(
return existingContent; return existingContent;
} }
const { const {
depName,
currentValue, currentValue,
newValue, newValue,
currentDigest, currentDigest,
newDigest, newDigest,
autoReplaceData, autoReplaceData = {},
} = upgrade; } = upgrade;
const replaceString = autoReplaceData.replaceString || currentValue; const replaceString = autoReplaceData.replaceString || currentValue;
logger.trace({ depName, replaceString }, 'autoReplace replaceString');
let searchIndex = existingContent.indexOf(replaceString);
if (searchIndex === -1) {
logger.error(
{ depName },
'Cannot find replaceString in current file content'
);
throw new Error(WORKER_FILE_UPDATE_FAILED);
}
try { try {
let newString = replaceString; let newString = replaceString.replace(
do { regEx(escapeRegExp(currentValue), 'g'),
newString = newString.replace(currentValue, newValue); newValue
} while (newString.includes(currentValue)); );
if (currentDigest) { if (currentDigest && newDigest) {
do { newString = newString.replace(
newString = newString.replace(currentDigest, newDigest); regEx(escapeRegExp(currentDigest), 'g'),
} while (newString.includes(currentDigest)); newDigest
} );
let searchIndex = existingContent.indexOf(replaceString);
if (searchIndex === -1) {
logger.error('Cannot find replaceString in current file content');
throw new Error(WORKER_FILE_UPDATE_FAILED);
} }
logger.debug(`Starting search at index ${searchIndex}`); logger.debug(`Starting search at index ${searchIndex}`);
// Iterate through the rest of the file // Iterate through the rest of the file
@ -117,9 +128,9 @@ export async function doAutoReplace(
} }
} }
} }
} catch (err) { } catch (err) /* istanbul ignore next */ {
logger.debug({ err }, 'doAutoReplace error'); logger.debug({ err }, 'doAutoReplace error');
} }
logger.error('Could not autoReplace'); // istanbul ignore next
throw new Error(WORKER_FILE_UPDATE_FAILED); throw new Error(WORKER_FILE_UPDATE_FAILED);
} }

View file

@ -21,11 +21,6 @@ describe('workers/branch/auto-replace', () => {
}; };
parentBranch = undefined; parentBranch = undefined;
}); });
it('throws on error', async () => {
await expect(
doAutoReplace(upgrade, 'existing content', parentBranch)
).rejects.toThrow();
});
it('rebases if the deps list has changed', async () => { it('rebases if the deps list has changed', async () => {
upgrade.baseDeps = extractPackageFile(sampleHtml).deps; upgrade.baseDeps = extractPackageFile(sampleHtml).deps;
parentBranch = 'some existing branch'; parentBranch = 'some existing branch';
@ -54,7 +49,6 @@ describe('workers/branch/auto-replace', () => {
upgrade.newValue = '7.1.1'; upgrade.newValue = '7.1.1';
upgrade.autoReplaceData = { upgrade.autoReplaceData = {
depIndex: 0, depIndex: 0,
replaceString: script,
}; };
const res = await doAutoReplace(upgrade, src, parentBranch); const res = await doAutoReplace(upgrade, src, parentBranch);
expect(res).toMatchSnapshot(); expect(res).toMatchSnapshot();