mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-12 15:06:27 +00:00
feat(cache): retain fingerprints for all matched managers (#20138)
This commit is contained in:
parent
83d9daf237
commit
cf6be1719e
6 changed files with 72 additions and 13 deletions
1
lib/util/cache/repository/types.ts
vendored
1
lib/util/cache/repository/types.ts
vendored
|
@ -8,6 +8,7 @@ import type { RepoInitConfig } from '../../../workers/repository/init/types';
|
||||||
export interface BaseBranchCache {
|
export interface BaseBranchCache {
|
||||||
sha: string; // branch commit sha
|
sha: string; // branch commit sha
|
||||||
configHash: string; // object hash of config
|
configHash: string; // object hash of config
|
||||||
|
extractionFingerprints: Record<string, string | undefined>; // matching manager fingerprints
|
||||||
packageFiles: Record<string, PackageFile[]>; // extract result
|
packageFiles: Record<string, PackageFile[]>; // extract result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ describe('workers/repository/extract/index', () => {
|
||||||
config.enabledManagers = ['npm'];
|
config.enabledManagers = ['npm'];
|
||||||
managerFiles.getManagerPackageFiles.mockResolvedValue([{} as never]);
|
managerFiles.getManagerPackageFiles.mockResolvedValue([{} as never]);
|
||||||
const res = await extractAllDependencies(config);
|
const res = await extractAllDependencies(config);
|
||||||
expect(res).toEqual({ packageFiles: { npm: [{}] } });
|
expect(res).toMatchObject({ packageFiles: { npm: [{}] } });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('warns if no packages found for a enabled manager', async () => {
|
it('warns if no packages found for a enabled manager', async () => {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import is from '@sindresorhus/is';
|
||||||
import { getManagerConfig, mergeChildConfig } from '../../../config';
|
import { getManagerConfig, mergeChildConfig } from '../../../config';
|
||||||
import type { ManagerConfig, RenovateConfig } from '../../../config/types';
|
import type { ManagerConfig, RenovateConfig } from '../../../config/types';
|
||||||
import { logger } from '../../../logger';
|
import { logger } from '../../../logger';
|
||||||
import { getManagerList } from '../../../modules/manager';
|
import { getManagerList, hashMap } from '../../../modules/manager';
|
||||||
import { getFileList } from '../../../util/git';
|
import { getFileList } from '../../../util/git';
|
||||||
import type { ExtractResult, WorkerExtractConfig } from '../../types';
|
import type { ExtractResult, WorkerExtractConfig } from '../../types';
|
||||||
import { getMatchingFiles } from './file-match';
|
import { getMatchingFiles } from './file-match';
|
||||||
|
@ -43,8 +43,15 @@ export async function extractAllDependencies(
|
||||||
|
|
||||||
const extractResult: ExtractResult = {
|
const extractResult: ExtractResult = {
|
||||||
packageFiles: {},
|
packageFiles: {},
|
||||||
|
extractionFingerprints: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Store the fingerprint of all managers which match any file (even if they do not find any dependencies)
|
||||||
|
// The cached result needs to be invalidated if the fingerprint of any matching manager changes
|
||||||
|
for (const { manager } of extractList) {
|
||||||
|
extractResult.extractionFingerprints[manager] = hashMap.get(manager);
|
||||||
|
}
|
||||||
|
|
||||||
const extractResults = await Promise.all(
|
const extractResults = await Promise.all(
|
||||||
extractList.map(async (managerConfig) => {
|
extractList.map(async (managerConfig) => {
|
||||||
const packageFiles = await getManagerPackageFiles(managerConfig);
|
const packageFiles = await getManagerPackageFiles(managerConfig);
|
||||||
|
|
|
@ -87,6 +87,7 @@ describe('workers/repository/process/extract-update', () => {
|
||||||
master: {
|
master: {
|
||||||
sha: '123test',
|
sha: '123test',
|
||||||
configHash: fingerprint(generateFingerprintConfig(config)),
|
configHash: fingerprint(generateFingerprintConfig(config)),
|
||||||
|
extractionFingerprints: {},
|
||||||
packageFiles,
|
packageFiles,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -99,19 +100,23 @@ describe('workers/repository/process/extract-update', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isCacheExtractValid()', () => {
|
describe('isCacheExtractValid()', () => {
|
||||||
let cachedExtract: BaseBranchCache = undefined as never;
|
let cachedExtract: BaseBranchCache;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cachedExtract = {
|
||||||
|
sha: 'sha',
|
||||||
|
configHash: undefined as never,
|
||||||
|
extractionFingerprints: {},
|
||||||
|
packageFiles: {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
it('undefined cache', () => {
|
it('undefined cache', () => {
|
||||||
expect(isCacheExtractValid('sha', 'hash', cachedExtract)).toBe(false);
|
expect(isCacheExtractValid('sha', 'hash', undefined)).toBe(false);
|
||||||
expect(logger.logger.debug).toHaveBeenCalledTimes(0);
|
expect(logger.logger.debug).toHaveBeenCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('partial cache', () => {
|
it('partial cache', () => {
|
||||||
cachedExtract = {
|
|
||||||
sha: 'sha',
|
|
||||||
configHash: undefined as never,
|
|
||||||
packageFiles: {},
|
|
||||||
};
|
|
||||||
expect(isCacheExtractValid('sha', 'hash', cachedExtract)).toBe(false);
|
expect(isCacheExtractValid('sha', 'hash', cachedExtract)).toBe(false);
|
||||||
expect(logger.logger.debug).toHaveBeenCalledTimes(0);
|
expect(logger.logger.debug).toHaveBeenCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
@ -126,7 +131,6 @@ describe('workers/repository/process/extract-update', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('config change', () => {
|
it('config change', () => {
|
||||||
cachedExtract.sha = 'sha';
|
|
||||||
cachedExtract.configHash = 'hash';
|
cachedExtract.configHash = 'hash';
|
||||||
expect(isCacheExtractValid('sha', 'new_hash', cachedExtract)).toBe(false);
|
expect(isCacheExtractValid('sha', 'new_hash', cachedExtract)).toBe(false);
|
||||||
expect(logger.logger.debug).toHaveBeenCalledWith(
|
expect(logger.logger.debug).toHaveBeenCalledWith(
|
||||||
|
@ -135,8 +139,30 @@ describe('workers/repository/process/extract-update', () => {
|
||||||
expect(logger.logger.debug).toHaveBeenCalledTimes(1);
|
expect(logger.logger.debug).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('invalid if no extractionFingerprints', () => {
|
||||||
|
cachedExtract.configHash = 'hash';
|
||||||
|
const { extractionFingerprints, ...restOfCache } = cachedExtract;
|
||||||
|
expect(
|
||||||
|
isCacheExtractValid(
|
||||||
|
'sha',
|
||||||
|
'hash',
|
||||||
|
restOfCache as never as BaseBranchCache
|
||||||
|
)
|
||||||
|
).toBe(false);
|
||||||
|
expect(logger.logger.debug).toHaveBeenCalledWith(
|
||||||
|
'Cached extract is missing extractionFingerprints, so cannot be used'
|
||||||
|
);
|
||||||
|
expect(logger.logger.debug).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid if changed fingerprints', () => {
|
||||||
|
cachedExtract.configHash = 'hash';
|
||||||
|
cachedExtract.extractionFingerprints = { npm: 'old-fingerprint' };
|
||||||
|
expect(isCacheExtractValid('sha', 'hash', cachedExtract)).toBe(false);
|
||||||
|
expect(logger.logger.debug).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
it('valid cache and config', () => {
|
it('valid cache and config', () => {
|
||||||
cachedExtract.sha = 'sha';
|
|
||||||
cachedExtract.configHash = 'hash';
|
cachedExtract.configHash = 'hash';
|
||||||
expect(isCacheExtractValid('sha', 'hash', cachedExtract)).toBe(true);
|
expect(isCacheExtractValid('sha', 'hash', cachedExtract)).toBe(true);
|
||||||
expect(logger.logger.debug).toHaveBeenCalledWith(
|
expect(logger.logger.debug).toHaveBeenCalledWith(
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import is from '@sindresorhus/is';
|
import is from '@sindresorhus/is';
|
||||||
import type { RenovateConfig } from '../../../config/types';
|
import type { RenovateConfig } from '../../../config/types';
|
||||||
import { logger } from '../../../logger';
|
import { logger } from '../../../logger';
|
||||||
|
import { hashMap } from '../../../modules/manager';
|
||||||
import type { PackageFile } from '../../../modules/manager/types';
|
import type { PackageFile } from '../../../modules/manager/types';
|
||||||
import { getCache } from '../../../util/cache/repository';
|
import { getCache } from '../../../util/cache/repository';
|
||||||
import type { BaseBranchCache } from '../../../util/cache/repository/types';
|
import type { BaseBranchCache } from '../../../util/cache/repository/types';
|
||||||
|
@ -80,6 +81,27 @@ export function isCacheExtractValid(
|
||||||
logger.debug('Cached extract result cannot be used due to config change');
|
logger.debug('Cached extract result cannot be used due to config change');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!cachedExtract.extractionFingerprints) {
|
||||||
|
logger.debug(
|
||||||
|
'Cached extract is missing extractionFingerprints, so cannot be used'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const changedManagers = new Set();
|
||||||
|
for (const [manager, fingerprint] of Object.entries(
|
||||||
|
cachedExtract.extractionFingerprints
|
||||||
|
)) {
|
||||||
|
if (fingerprint !== hashMap.get(manager)) {
|
||||||
|
changedManagers.add(manager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changedManagers.size > 0) {
|
||||||
|
logger.debug(
|
||||||
|
{ changedManagers: [...changedManagers] },
|
||||||
|
'Manager fingerprint(s) have changed, extract cache cannot be reused'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Cached extract for sha=${baseBranchSha} is valid and can be used`
|
`Cached extract for sha=${baseBranchSha} is valid and can be used`
|
||||||
);
|
);
|
||||||
|
@ -114,12 +136,14 @@ export async function extract(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await checkoutBranch(baseBranch!);
|
await checkoutBranch(baseBranch!);
|
||||||
const extractResult = await extractAllDependencies(config);
|
const extractResult = (await extractAllDependencies(config)) || {};
|
||||||
packageFiles = extractResult?.packageFiles;
|
packageFiles = extractResult.packageFiles;
|
||||||
|
const { extractionFingerprints } = extractResult;
|
||||||
// TODO: fix types (#7154)
|
// TODO: fix types (#7154)
|
||||||
cache.scan[baseBranch!] = {
|
cache.scan[baseBranch!] = {
|
||||||
sha: baseBranchSha!,
|
sha: baseBranchSha!,
|
||||||
configHash,
|
configHash,
|
||||||
|
extractionFingerprints,
|
||||||
packageFiles,
|
packageFiles,
|
||||||
};
|
};
|
||||||
// Clean up cached branch extracts
|
// Clean up cached branch extracts
|
||||||
|
|
|
@ -188,5 +188,6 @@ export interface UpgradeFingerprintConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtractResult {
|
export interface ExtractResult {
|
||||||
|
extractionFingerprints: Record<string, string | undefined>;
|
||||||
packageFiles: Record<string, PackageFile[]>;
|
packageFiles: Record<string, PackageFile[]>;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue