Skip to content

Commit

Permalink
refactor(versioning/poetry): Enable strict null checks (#14028)
Browse files Browse the repository at this point in the history
  • Loading branch information
zharinov committed Feb 7, 2022
1 parent eba4e8f commit 1f05297
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 70 deletions.
19 changes: 11 additions & 8 deletions lib/versioning/poetry/index.spec.ts
Expand Up @@ -22,14 +22,15 @@ describe('versioning/poetry/index', () => {
});

test.each`
version | major | minor | patch
${'1'} | ${1} | ${0} | ${0}
${'1.9'} | ${1} | ${9} | ${0}
${'1.9.0'} | ${1} | ${9} | ${0}
${'1.9.4'} | ${1} | ${9} | ${4}
${'1.9.4b0'} | ${1} | ${9} | ${4}
${'1.9.4-beta0'} | ${1} | ${9} | ${4}
${'17.04.01'} | ${17} | ${4} | ${1}
version | major | minor | patch
${'1'} | ${1} | ${0} | ${0}
${'1.9'} | ${1} | ${9} | ${0}
${'1.9.0'} | ${1} | ${9} | ${0}
${'1.9.4'} | ${1} | ${9} | ${4}
${'1.9.4b0'} | ${1} | ${9} | ${4}
${'1.9.4-beta0'} | ${1} | ${9} | ${4}
${'17.04.01'} | ${17} | ${4} | ${1}
${'!@#'} | ${null} | ${null} | ${null}
`(
'getMajor, getMinor, getPatch for "$version"',
({ version, major, minor, patch }) => {
Expand Down Expand Up @@ -156,6 +157,7 @@ describe('versioning/poetry/index', () => {
${['0.4.0', '0.5.0', '4.2.0', '5.0.0']} | ${'^4.0.0, > 4.1.0, <= 4.3.5'} | ${'4.2.0'}
${['0.4.0', '0.5.0', '4.2.0', '5.0.0']} | ${'^6.2.0, 3.*'} | ${null}
${['0.8.0a2', '0.8.0a7']} | ${'^0.8.0-alpha.0'} | ${'0.8.0-alpha.2'}
${['1.0.0', '2.0.0']} | ${'^3.0.0'} | ${null}
`(
'minSatisfyingVersion($versions, "$range") === $expected',
({ versions, range, expected }) => {
Expand All @@ -168,6 +170,7 @@ describe('versioning/poetry/index', () => {
${['4.2.1', '0.4.0', '0.5.0', '4.0.0', '4.2.0', '5.0.0']} | ${'4.*.0, < 4.2.5'} | ${'4.2.1'}
${['0.4.0', '0.5.0', '4.0.0', '4.2.0', '5.0.0', '5.0.3']} | ${'5.0, > 5.0.0'} | ${'5.0.3'}
${['0.8.0a2', '0.8.0a7']} | ${'^0.8.0-alpha.0'} | ${'0.8.0-alpha.7'}
${['1.0.0', '2.0.0']} | ${'^3.0.0'} | ${null}
`(
'getSatisfyingVersion($versions, "$range") === $expected',
({ versions, range, expected }) => {
Expand Down
128 changes: 86 additions & 42 deletions lib/versioning/poetry/index.ts
Expand Up @@ -17,33 +17,42 @@ export const supportsRanges = true;
export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];

function equals(a: string, b: string): boolean {
return npm.equals(poetry2semver(a), poetry2semver(b));
const semverA = poetry2semver(a);
const semverB = poetry2semver(b);
return !!(semverA && semverB && npm.equals(semverA, semverB));
}

function getMajor(version: string): number {
return npm.getMajor(poetry2semver(version));
function getMajor(version: string): number | null {
const semverVersion = poetry2semver(version);
return semverVersion ? npm.getMajor(semverVersion) : null;
}

function getMinor(version: string): number {
return npm.getMinor(poetry2semver(version));
function getMinor(version: string): number | null {
const semverVersion = poetry2semver(version);
return semverVersion ? npm.getMinor(semverVersion) : null;
}

function getPatch(version: string): number {
return npm.getPatch(poetry2semver(version));
function getPatch(version: string): number | null {
const semverVersion = poetry2semver(version);
return semverVersion ? npm.getPatch(semverVersion) : null;
}

function isVersion(input: string): boolean {
return VERSION_PATTERN.test(input);
}

function isGreaterThan(a: string, b: string): boolean {
return npm.isGreaterThan(poetry2semver(a), poetry2semver(b));
const semverA = poetry2semver(a);
const semverB = poetry2semver(b);
return !!(semverA && semverB && npm.isGreaterThan(semverA, semverB));
}

function isLessThanRange(version: string, range: string): boolean {
return (
const semverVersion = poetry2semver(version);
return !!(
isVersion(version) &&
npm.isLessThanRange(poetry2semver(version), poetry2npm(range))
semverVersion &&
npm.isLessThanRange?.(semverVersion, poetry2npm(range))
);
}

Expand All @@ -52,31 +61,49 @@ export function isValid(input: string): boolean {
}

function isStable(version: string): boolean {
return npm.isStable(poetry2semver(version));
const semverVersion = poetry2semver(version);
return !!(semverVersion && npm.isStable(semverVersion));
}

function matches(version: string, range: string): boolean {
return (
isVersion(version) && npm.matches(poetry2semver(version), poetry2npm(range))
);
}

function getSatisfyingVersion(versions: string[], range: string): string {
return semver2poetry(
npm.getSatisfyingVersion(
versions.map((version) => poetry2semver(version)),
poetry2npm(range)
)
const semverVersion = poetry2semver(version);
return !!(
isVersion(version) &&
semverVersion &&
npm.matches(semverVersion, poetry2npm(range))
);
}

function minSatisfyingVersion(versions: string[], range: string): string {
return semver2poetry(
npm.minSatisfyingVersion(
versions.map((version) => poetry2semver(version)),
poetry2npm(range)
)
);
function getSatisfyingVersion(
versions: string[],
range: string
): string | null {
const semverVersions: string[] = [];
versions.forEach((version) => {
const semverVersion = poetry2semver(version);
if (semverVersion) {
semverVersions.push(semverVersion);
}
});
const npmRange = poetry2npm(range);
const satisfyingVersion = npm.getSatisfyingVersion(semverVersions, npmRange);
return satisfyingVersion ? semver2poetry(satisfyingVersion) : null;
}

function minSatisfyingVersion(
versions: string[],
range: string
): string | null {
const semverVersions: string[] = [];
versions.forEach((version) => {
const semverVersion = poetry2semver(version);
if (semverVersion) {
semverVersions.push(semverVersion);
}
});
const npmRange = poetry2npm(range);
const satisfyingVersion = npm.minSatisfyingVersion(semverVersions, npmRange);
return satisfyingVersion ? semver2poetry(satisfyingVersion) : null;
}

function isSingleVersion(constraint: string): boolean {
Expand All @@ -91,15 +118,19 @@ function handleShort(
operator: string,
currentValue: string,
newVersion: string
): string {
): string | null {
const toVersionMajor = getMajor(newVersion);
const toVersionMinor = getMinor(newVersion);
const split = currentValue.split('.');
if (split.length === 1) {
if (toVersionMajor !== null && split.length === 1) {
// [^,~]4
return `${operator}${toVersionMajor}`;
}
if (split.length === 2) {
if (
toVersionMajor !== null &&
toVersionMinor !== null &&
split.length === 2
) {
// [^,~]4.1
return `${operator}${toVersionMajor}.${toVersionMinor}`;
}
Expand All @@ -117,6 +148,7 @@ function getNewValue({
try {
const massagedNewVersion = poetry2semver(newVersion);
if (
massagedNewVersion &&
isVersion(massagedNewVersion) &&
npm.matches(massagedNewVersion, npmCurrentValue)
) {
Expand Down Expand Up @@ -157,25 +189,37 @@ function getNewValue({
return currentValue;
}
try {
const newSemver = npm.getNewValue({
currentValue: poetry2npm(currentValue),
rangeStrategy,
currentVersion: poetry2semver(currentVersion),
newVersion: poetry2semver(newVersion),
});
const newPoetry = npm2poetry(newSemver);
return newPoetry;
const currentSemverVersion =
currentVersion && poetry2semver(currentVersion);

const newSemverVersion = poetry2semver(newVersion);

if (currentSemverVersion && newSemverVersion) {
const newSemver = npm.getNewValue({
currentValue: poetry2npm(currentValue),
rangeStrategy,
currentVersion: currentSemverVersion,
newVersion: newSemverVersion,
});
const newPoetry = newSemver && npm2poetry(newSemver);
if (newPoetry) {
return newPoetry;
}
}
} catch (err) /* istanbul ignore next */ {
logger.debug(
{ currentValue, rangeStrategy, currentVersion, newVersion, err },
'Could not generate new value using npm.getNewValue()'
);
return currentValue;
}

// istanbul ignore next
return currentValue;
}

function sortVersions(a: string, b: string): number {
return npm.sortVersions(poetry2semver(a), poetry2semver(b));
// istanbul ignore next
return npm.sortVersions(poetry2semver(a) ?? '', poetry2semver(b) ?? '');
}

export const api: VersioningApi = {
Expand Down
36 changes: 19 additions & 17 deletions lib/versioning/poetry/transform.ts
@@ -1,13 +1,15 @@
import semver from 'semver';
import { RANGE_COMPARATOR_PATTERN, VERSION_PATTERN } from './patterns';

function parseLetterTag(
letter?: string,
number?: string
): { letter?: string; number?: string } | null {
interface LetterTag {
letter: string;
number: string;
}

function parseLetterTag(letter?: string, number?: string): LetterTag | null {
if (letter !== undefined) {
// apply the same normalizations as poetry
const spellings = {
const spellings: Record<string, string> = {
alpha: 'a',
beta: 'b',
c: 'rc',
Expand Down Expand Up @@ -41,22 +43,22 @@ export function poetry2semver(
poetry_version: string,
padRelease = true
): string | null {
const match = VERSION_PATTERN.exec(poetry_version);
if (!match) {
const matchGroups = VERSION_PATTERN.exec(poetry_version)?.groups;
if (!matchGroups) {
return null;
}
// trim leading zeros from valid numbers
const releaseParts = match.groups.release
const releaseParts = matchGroups.release
.split('.')
.map((segment) => parseInt(segment, 10));
while (padRelease && releaseParts.length < 3) {
releaseParts.push(0);
}
const pre = parseLetterTag(match.groups.pre_l, match.groups.pre_n);
const post = match.groups.post_n1
? parseLetterTag(undefined, match.groups.post_n1)
: parseLetterTag(match.groups.post_l, match.groups.post_n);
const dev = parseLetterTag(match.groups.dev_l, match.groups.dev_n);
const pre = parseLetterTag(matchGroups.pre_l, matchGroups.pre_n);
const post = matchGroups.post_n1
? parseLetterTag(undefined, matchGroups.post_n1)
: parseLetterTag(matchGroups.post_l, matchGroups.post_n);
const dev = parseLetterTag(matchGroups.dev_l, matchGroups.dev_n);

const parts = [releaseParts.map((num) => num.toString()).join('.')];
if (pre !== null) {
Expand All @@ -81,13 +83,13 @@ export function semver2poetry(version?: string): string | null {
if (!s) {
return null;
}
const spellings = {
const spellings: Record<string, string> = {
a: 'alpha',
b: 'beta',
c: 'rc',
dev: 'alpha',
};
s.prerelease = s.prerelease.map((letter) => spellings[letter] || letter);
s.prerelease = s.prerelease.map((letter) => spellings[letter] ?? letter);
return s.format();
}

Expand All @@ -108,7 +110,7 @@ export function poetry2npm(input: string): string {
.split(RANGE_COMPARATOR_PATTERN);
// do not pad versions with zeros in a range
const transformed = chunks
.map((chunk) => poetry2semver(chunk, false) || chunk)
.map((chunk) => poetry2semver(chunk, false) ?? chunk)
.join('')
.replace(/===/, '=');
return transformed;
Expand All @@ -126,7 +128,7 @@ export function npm2poetry(range: string): string {
// (i.e. anything that is not a range operator, potentially surrounded by whitespace)
const transformedRange = range
.split(RANGE_COMPARATOR_PATTERN)
.map((chunk) => semver2poetry(chunk) || chunk)
.map((chunk) => semver2poetry(chunk) ?? chunk)
.join('');

// Note: this doesn't remove the ^
Expand Down
3 changes: 0 additions & 3 deletions tsconfig.strict.json
Expand Up @@ -385,9 +385,6 @@
"lib/versioning/common.ts",
"lib/versioning/helm/index.ts",
"lib/versioning/index.ts",
"lib/versioning/poetry/index.ts",
"lib/versioning/poetry/patterns.ts",
"lib/versioning/poetry/transform.ts",
"lib/versioning/types.ts",
"lib/workers/branch/artifacts.ts",
"lib/workers/branch/auto-replace.ts",
Expand Down

0 comments on commit 1f05297

Please sign in to comment.