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/pipenv): Support custom environment variable usage in Pipfile source URLs #28062

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d7f79ad
feat(26447): Support custom environment variable usage in Pipfile sou…
fivetide Mar 21, 2024
4fa4221
Merge branch 'main' into feature/26447-Support-custom-environment-var…
fivetide Mar 21, 2024
5c970bc
refactor for readability, improve coverage
fivetide Mar 22, 2024
05d95dd
Update lib/modules/manager/pipenv/artifacts.spec.ts
fivetide Mar 22, 2024
9d4f93e
reversed import order
fivetide Mar 22, 2024
9a240cc
prefer for of loop
rarkins Mar 23, 2024
ad737da
refactor log messages
rarkins Mar 23, 2024
3c7a788
improved code according to review suggestions
fivetide Mar 25, 2024
bd469a7
Merge branch 'main' into feature/26447-Support-custom-environment-var…
fivetide Mar 28, 2024
115ac2e
added test coverage
fivetide Mar 28, 2024
5ee3daa
Merge branch 'main' into feature/26447-Support-custom-environment-var…
rarkins Mar 28, 2024
f4456c8
Merge branch 'main' into feature/26447-Support-custom-environment-var…
fivetide Apr 2, 2024
7c642ad
simplify code a littly
fivetide Apr 2, 2024
67ea6ee
reduce complexity / cover partial covered lines
fivetide Apr 3, 2024
f8f7bfe
fix linter issues
fivetide Apr 3, 2024
3ebd550
Apply suggestions from code review
rarkins Apr 13, 2024
22fb470
Apply suggestions from code review
rarkins Apr 13, 2024
168bd7a
fix issues after review changes
fivetide Apr 17, 2024
fd1ae8d
Merge branch 'main' into feature/26447-Support-custom-environment-var…
fivetide Apr 22, 2024
35e42a2
remove unused import
fivetide Apr 22, 2024
0b1fba0
more suggestions from code review
fivetide Apr 23, 2024
33f4bcf
use tagged template syntax
fivetide Apr 23, 2024
1ecc660
lint-fix
fivetide Apr 23, 2024
0bf8e8b
Update lib/modules/manager/pipenv/artifacts.spec.ts
fivetide Apr 23, 2024
afc39f3
lint-fix
fivetide Apr 23, 2024
4f6e5a5
Merge branch 'main' into feature/26447-Support-custom-environment-var…
fivetide Apr 26, 2024
ae2178a
Merge branch 'main' into feature/26447-Support-custom-environment-var…
fivetide Apr 28, 2024
efc4ac2
Merge branch 'main' into feature/26447-Support-custom-environment-var…
fivetide Apr 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 22 additions & 2 deletions lib/modules/manager/pipenv/artifacts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import type { RepoGlobalConfig } from '../../../config/types';
import * as docker from '../../../util/exec/docker';
import type { StatusResult } from '../../../util/git/types';
import { find as _find } from '../../../util/host-rules';
import { getPkgReleases as _getPkgReleases } from '../../datasource';
import * as _datasource from '../../datasource';
import { getPkgReleases as _getPkgReleases } from '../../datasource';
fivetide marked this conversation as resolved.
Show resolved Hide resolved
import type { UpdateArtifactsConfig } from '../types';
import { extractEnvironmentVariableName } from './artifacts';
import type { PipfileLockSchema } from './schema';
import { updateArtifacts } from '.';

Expand Down Expand Up @@ -628,7 +629,19 @@ describe('modules/manager/pipenv/artifacts', () => {
]);
});

it('does not pass private credential environment vars if variable names differ from allowed', async () => {
it('extracts correct environment variable from credential placeholder', () => {
[
['$USERNAME', 'USERNAME'],
['$', null],
['${USERNAME}', 'USERNAME'],
['${USERNAME:-default}', 'USERNAME'],
['${COMPLEX_NAME_1:-default}', 'COMPLEX_NAME_1'],
].every((testCase) => {
expect(extractEnvironmentVariableName(testCase[0])).toEqual(testCase[1]);
});
});

it('updates extraEnv if variable names differ from default', async () => {
fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir);
fs.ensureCacheDir.mockResolvedValueOnce(pipCacheDir);
fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir);
Expand All @@ -641,6 +654,11 @@ describe('modules/manager/pipenv/artifacts', () => {
);
fs.readLocalFile.mockResolvedValueOnce('New Pipfile.lock');

find.mockReturnValueOnce({
username: 'usernameOne',
password: 'passwordTwo',
});

expect(
await updateArtifacts({
packageFileName: 'Pipfile',
Expand All @@ -666,6 +684,8 @@ describe('modules/manager/pipenv/artifacts', () => {
env: {
PIPENV_CACHE_DIR: pipenvCacheDir,
WORKON_HOME: virtualenvsCacheDir,
USERNAME_FOO: 'usernameOne',
PAZZWORD: 'passwordTwo',
},
},
},
Expand Down
92 changes: 67 additions & 25 deletions lib/modules/manager/pipenv/artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
} from '../../../util/fs';
import { getRepoStatus } from '../../../util/git';
import { find } from '../../../util/host-rules';
import { parseUrl } from '../../../util/url';
import { PypiDatasource } from '../../datasource/pypi';
import type {
UpdateArtifact,
Expand Down Expand Up @@ -113,35 +114,58 @@
return '';
}

function getMatchingHostRule(url: string): HostRule {
return find({ hostType: PypiDatasource.id, url });
function getMatchingHostRule(url: string): HostRule | null {
const parsedUrl = parseUrl(url);
if (!parsedUrl) {
return null;

Check warning on line 120 in lib/modules/manager/pipenv/artifacts.ts

View check run for this annotation

Codecov / codecov/patch

lib/modules/manager/pipenv/artifacts.ts#L120

Added line #L120 was not covered by tests
}
fivetide marked this conversation as resolved.
Show resolved Hide resolved
parsedUrl.username = '';
parsedUrl.password = '';
const urlWithoutCredentials = parsedUrl.toString();

return find({ hostType: PypiDatasource.id, url: urlWithoutCredentials });
}

async function findPipfileSourceUrlWithCredentials(
async function findPipfileSourceUrlsWithCredentials(
pipfileContent: string,
pipfileName: string,
): Promise<string | null> {
): Promise<string[]> {
const pipfile = await extractPackageFile(pipfileContent, pipfileName);
if (!pipfile) {
logger.debug('Error parsing Pipfile');
return null;
return [];
}

const credentialTokens = [
'$USERNAME:',
// eslint-disable-next-line no-template-curly-in-string
'${USERNAME}',
'$PASSWORD@',
// eslint-disable-next-line no-template-curly-in-string
'${PASSWORD}',
];
return pipfile.registryUrls?.filter((url) => !!parseUrl(url)?.username) ?? [];
}

const sourceWithCredentials = pipfile.registryUrls?.find((url) =>
credentialTokens.some((token) => url.includes(token)),
);
export function extractEnvironmentVariableName(
credential: string | null,
): string | null {
if (!credential) {
return null;

Check warning on line 146 in lib/modules/manager/pipenv/artifacts.ts

View check run for this annotation

Codecov / codecov/patch

lib/modules/manager/pipenv/artifacts.ts#L146

Added line #L146 was not covered by tests
}
const match = decodeURI(credential).match('([a-zA-Z0-9_]+)');
rarkins marked this conversation as resolved.
Show resolved Hide resolved
return match?.length ? match[0] : null;
}

// Only one source is currently supported
return sourceWithCredentials ?? null;
function addExtraEnvVariable(
extraEnv: ExtraEnv<unknown>,
environmentVariableName: string,
environmentValue: string,
): void {
logger.debug(
rarkins marked this conversation as resolved.
Show resolved Hide resolved
`Adding ${environmentVariableName} environment variable for pipenv`,
);
if (
extraEnv[environmentVariableName] &&
extraEnv[environmentVariableName] !== environmentValue
) {
logger.warn(

Check warning on line 164 in lib/modules/manager/pipenv/artifacts.ts

View check run for this annotation

Codecov / codecov/patch

lib/modules/manager/pipenv/artifacts.ts#L164

Added line #L164 was not covered by tests
`Possible misconfiguration, ${environmentVariableName} is already set to a different value`,
);
}
extraEnv[environmentVariableName] = environmentValue;
}

export async function updateArtifacts({
Expand Down Expand Up @@ -189,25 +213,43 @@
],
};

const sourceUrl = await findPipfileSourceUrlWithCredentials(
const sourceUrls = await findPipfileSourceUrlsWithCredentials(
newPipfileContent,
pipfileName,
);
if (sourceUrl) {
sourceUrls.every((sourceUrl) => {
const parsedSourceUrl = parseUrl(sourceUrl);
if (!parsedSourceUrl) {
return;

Check warning on line 223 in lib/modules/manager/pipenv/artifacts.ts

View check run for this annotation

Codecov / codecov/patch

lib/modules/manager/pipenv/artifacts.ts#L223

Added line #L223 was not covered by tests
}
logger.debug({ sourceUrl }, 'Pipfile contains credentials');
const hostRule = getMatchingHostRule(sourceUrl);
if (hostRule) {
logger.debug('Found matching hostRule for Pipfile credentials');
if (hostRule.username) {
logger.debug('Adding USERNAME environment variable for pipenv');
extraEnv.USERNAME = hostRule.username;
const environmentVariableName =
extractEnvironmentVariableName(parsedSourceUrl.username) ??
'USERNAME';
const environmentValue = hostRule.username;
addExtraEnvVariable(
extraEnv,
environmentVariableName,
environmentValue,
);
}
if (hostRule.password) {
logger.debug('Adding PASSWORD environment variable for pipenv');
extraEnv.PASSWORD = hostRule.password;
const environmentVariableName =
extractEnvironmentVariableName(parsedSourceUrl.password) ??
'PASSWORD';
const environmentValue = hostRule.password;
addExtraEnvVariable(
extraEnv,
environmentVariableName,
environmentValue,
);
}
}
}
});
execOptions.extraEnv = extraEnv;

logger.trace({ cmd }, 'pipenv lock command');
Expand Down