Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions lib/workers/repository/update/branch/auto-replace.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ describe('workers/repository/update/branch/auto-replace', () => {
await expect(res).rejects.toThrow(WORKER_FILE_UPDATE_FAILED);
});

it('fails with digest mismatch', async () => {
it('updates digest when only digest changes and no replaceString is set', async () => {
const dockerfile = codeBlock`
FROM java:11@sha256-1234 as build
`;
Expand All @@ -277,8 +277,8 @@ describe('workers/repository/update/branch/auto-replace', () => {
upgrade.newValue = '11';
upgrade.newDigest = 'sha256-5678';
upgrade.packageFile = 'Dockerfile';
const res = doAutoReplace(upgrade, dockerfile, reuseExistingBranch);
await expect(res).rejects.toThrow(WORKER_FILE_UPDATE_FAILED);
const res = await doAutoReplace(upgrade, dockerfile, reuseExistingBranch);
expect(res).toBe('FROM java:11@sha256-5678 as build');
});

it('updates with docker replacement', async () => {
Expand Down Expand Up @@ -1430,6 +1430,30 @@ describe('workers/repository/update/branch/auto-replace', () => {
);
});

it('jsonata: update currentDigest with currentValue captured', async () => {
const source =
'[ { "version": "1.2.3", "digest": "abcdef", "package": "foo" } ]';
upgrade.manager = 'jsonata';
upgrade.depName = 'foo';
upgrade.currentValue = '1.2.3';
upgrade.currentDigest = 'abcdef';
upgrade.newDigest = 'badbeef';
upgrade.depIndex = 0;
upgrade.packageFile = 'deps.json';
// @ts-expect-error -- TODO: improve typing
upgrade.fileFormat = 'json';
// @ts-expect-error -- TODO: improve typing
upgrade.datasourceTemplate = 'github-releases';
// @ts-expect-error -- TODO: improve typing
upgrade.matchStrings = [
'*.{"depName": package, "currentDigest": digest, "currentValue": version }',
];
const res = await doAutoReplace(upgrade, source, reuseExistingBranch);
expect(res).toBe(
'[ { "version": "1.2.3", "digest": "badbeef", "package": "foo" } ]',
);
});

it('github-actions: updates with newValue only', async () => {
const githubAction = codeBlock`
jobs:
Expand Down
66 changes: 24 additions & 42 deletions lib/workers/repository/update/branch/auto-replace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,28 +197,6 @@ async function checkExistingBranch(
return existingContent;
}

/**
* Check if an update from `current` to `newString` should be performed and return 1 if so.
*
* @remarks
* Useful for counting the number of updates to do.
*
* @param current The current value (if undefined then no update is required)
* @param newString The new value (if undefined then no update is required)
*
* @returns 1 if `current !== newString` and 0 if they are equal or at least one is undefined.
*/
function updatedToInt(
current: string | undefined | null,
newString: string | undefined | null,
): number {
if (current && newString && newString !== current) {
return 1;
} else {
return 0;
}
}

export async function doAutoReplace(
upgrade: BranchUpgradeConfig,
existingContent: string,
Expand Down Expand Up @@ -247,27 +225,31 @@ export async function doAutoReplace(
if (reuseExistingBranch) {
return await checkExistingBranch(upgrade, existingContent);
}

// count how many strings need to be updated
const changedCount =
updatedToInt(depName, newName) +
updatedToInt(currentValue, newValue) +
updatedToInt(currentDigest, newDigest);
if (changedCount > 1) {
logger.debug(
{ packageFile, depName, changedCount },
'Multiple changed values, might need special handling (#36461)',
);
const valueChanging =
isString(currentValue) && isString(newValue) && currentValue !== newValue;
const digestChanging =
isString(currentDigest) &&
isString(newDigest) &&
currentDigest !== newDigest;
let replaceWithoutReplaceString =
isString(newName) &&
newName !== depName &&
(isUndefined(upgrade.replaceString) ||
!upgrade.replaceString?.includes(depName!));
// fallback must contain the field being updated, else the replacement is
// a no-op for managers where value and digest live in separate tokens
let replaceString = upgrade.replaceString;
if (isUndefined(replaceString)) {
if (valueChanging && digestChanging) {
// no single fallback covers both — use the per-field path
replaceWithoutReplaceString = true;
replaceString = currentValue;
} else if (digestChanging) {
replaceString = currentDigest;
} else {
replaceString = currentValue ?? currentDigest;
}
}

const replaceWithoutReplaceString =
(isString(newName) &&
newName !== depName &&
(isUndefined(upgrade.replaceString) ||
!upgrade.replaceString?.includes(depName!))) ||
// for jsonata manager, fixes #36461
(isUndefined(upgrade.replaceString) && changedCount > 1);
const replaceString = upgrade.replaceString ?? currentValue ?? currentDigest;
logger.trace({ depName, replaceString }, 'autoReplace replaceString');
let searchIndex: number;
if (replaceWithoutReplaceString) {
Expand Down
Loading