Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(manager/pip-compile): Add lockedVersion to package file deps #27242

Merged
59 changes: 56 additions & 3 deletions lib/modules/manager/pip-compile/extract.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Fixtures } from '../../../../test/fixtures';
import { fs } from '../../../../test/util';
import { logger } from '../../../logger';
import { extractAllPackageFiles, extractPackageFile } from '.';

jest.mock('../../../util/fs');
Expand Down Expand Up @@ -130,11 +131,63 @@ describe('modules/manager/pip-compile/extract', () => {
['foo==1.0.1'],
),
);
fs.readLocalFile.mockResolvedValueOnce('!@#$');
fs.readLocalFile.mockResolvedValueOnce('');
fs.readLocalFile.mockResolvedValueOnce('!@#$'); // malformed.in
fs.readLocalFile.mockResolvedValueOnce(''); // empty.in
fs.readLocalFile.mockResolvedValueOnce(
getSimpleRequirementsFile(
'pip-compile --output-file=headerOnly.txt reqs.in',
[],
),
);

const lockFiles = ['empty.txt', 'noHeader.txt', 'badSource.txt'];
const lockFiles = [
'empty.txt',
'noHeader.txt',
'badSource.txt',
'headerOnly.txt',
];
const packageFiles = await extractAllPackageFiles({}, lockFiles);
expect(packageFiles).toBeNull();
});

it('adds lockedVersion to deps in package file', async () => {
fs.readLocalFile.mockResolvedValueOnce(
getSimpleRequirementsFile(
'pip-compile --output-file=requirements.txt requirements.in',
['friendly-bard==1.0.1'],
),
);
// also check if normalized name is used
fs.readLocalFile.mockResolvedValueOnce('FrIeNdLy-._.-bArD>=1.0.0');

const lockFiles = ['requirements.txt'];
const packageFiles = await extractAllPackageFiles({}, lockFiles);
expect(packageFiles).toBeDefined();
const packageFile = packageFiles!.pop();
expect(packageFile!.deps).toHaveLength(1);
expect(packageFile!.deps.pop()).toMatchObject({
currentValue: '>=1.0.0',
depName: 'FrIeNdLy-._.-bArD',
lockedVersion: '1.0.1',
});
});

it('warns if dependency has no locked version', async () => {
fs.readLocalFile.mockResolvedValueOnce(
getSimpleRequirementsFile(
'pip-compile --output-file=requirements.txt requirements.in',
['foo==1.0.1'],
),
);
fs.readLocalFile.mockResolvedValueOnce('foo>=1.0.0\nbar');

const lockFiles = ['requirements.txt'];
const packageFiles = await extractAllPackageFiles({}, lockFiles);
expect(packageFiles).toBeDefined();
const packageFile = packageFiles!.pop();
expect(packageFile!.deps).toHaveLength(2);
expect(logger.warn).toHaveBeenCalledWith(
'pip-compile: `bar` not found in lock file `requirements.txt`',
);
});
});
25 changes: 23 additions & 2 deletions lib/modules/manager/pip-compile/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
PipCompileArgs,
SupportedManagers,
} from './types';
import { normalizeDepName } from './utils';

function matchManager(filename: string): SupportedManagers | 'unknown' {
if (filename.endsWith('setup.py')) {
Expand Down Expand Up @@ -95,8 +96,14 @@ export async function extractAllPackageFiles(
type: 'constraint',
});
}
// TODO(not7cd): handle locked deps
// const lockedDeps = extractRequirementsFile(content);
const lockedDeps = extractRequirementsFile(fileContent)?.deps;
if (!lockedDeps) {
logger.warn(
not7cd marked this conversation as resolved.
Show resolved Hide resolved
{ fileMatch },
'pip-compile: Failed to extract dependencies from lock file',
);
continue;
}
for (const packageFile of pipCompileArgs.sourceFiles) {
depsBetweenFiles.push({
sourceFile: packageFile,
Expand Down Expand Up @@ -130,6 +137,20 @@ export async function extractAllPackageFiles(
config,
);
if (packageFileContent) {
for (const dep of packageFileContent.deps) {
const lockedVersion = lockedDeps?.find(
(lockedDep) =>
normalizeDepName(lockedDep.depName!) ===
normalizeDepName(dep.depName!),
)?.currentVersion;
if (lockedVersion) {
dep.lockedVersion = lockedVersion;
} else {
logger.warn(
`pip-compile: \`${dep.depName}\` not found in lock file \`${fileMatch}\``,
not7cd marked this conversation as resolved.
Show resolved Hide resolved
);
}
}
packageFiles.set(packageFile, {
...packageFileContent,
lockFiles: [fileMatch],
Expand Down
4 changes: 4 additions & 0 deletions lib/modules/manager/pip-compile/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// https://packaging.python.org/en/latest/specifications/name-normalization/
export function normalizeDepName(name: string): string {
return name.replace(/[-_.]+/g, '-').toLowerCase();
}
not7cd marked this conversation as resolved.
Show resolved Hide resolved
Loading