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(pacmak): prerelease identifier support #2146

Merged
merged 25 commits into from
Oct 28, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7de9996
feat(pacmak): prerelease identifier support
RomainMuller Oct 19, 2020
2c25ddf
Merge branch 'master' into rmuller/auto-preversions
RomainMuller Oct 19, 2020
bacaddf
Update packages/jsii-pacmak/lib/targets/version-utils.ts
RomainMuller Oct 22, 2020
daee495
Most of @nija-at feedback
RomainMuller Oct 22, 2020
7b94522
Merge remote-tracking branch 'origin/rmuller/auto-preversions' into r…
RomainMuller Oct 22, 2020
0216e7f
add comment on toBracketNotation
RomainMuller Oct 22, 2020
059274f
fix linter violations
RomainMuller Oct 22, 2020
1941a43
Update all-targets-tested.test.ts
RomainMuller Oct 22, 2020
f3dc659
fixup tests
RomainMuller Oct 22, 2020
033d777
add support for .pr####.# identifiers
RomainMuller Oct 22, 2020
6526ea2
Merge remote-tracking branch 'origin/master' into rmuller/auto-prever…
RomainMuller Oct 27, 2020
874ed3d
add tests
RomainMuller Oct 27, 2020
a5af5de
Merge branch 'master' into rmuller/auto-preversions
RomainMuller Oct 27, 2020
b1ff0c5
document Python pre-version translation
RomainMuller Oct 27, 2020
f623d7e
fix linter offense
RomainMuller Oct 27, 2020
eafcaf0
stop using mock-fs, it apparently has unstable compatibility with jes…
RomainMuller Oct 27, 2020
8b3c988
Merge branch 'master' into rmuller/auto-preversions
RomainMuller Oct 27, 2020
650e931
pr feedback from @nija-at
RomainMuller Oct 28, 2020
2a8614d
Merge branch 'master' into rmuller/auto-preversions
RomainMuller Oct 28, 2020
929b709
fixup test expectations I had forgotten to touch
RomainMuller Oct 28, 2020
b2de61d
Merge branch 'master' into rmuller/auto-preversions
RomainMuller Oct 28, 2020
d5398d6
pull .prN mapping
RomainMuller Oct 28, 2020
e45088c
Update docs/configuration.md
Oct 28, 2020
7bdf1a0
make integration test work again (hopefully)
RomainMuller Oct 28, 2020
eef1e6b
Merge branch 'main' into rmuller/auto-preversions
RomainMuller Oct 28, 2020
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
3 changes: 2 additions & 1 deletion packages/jsii-pacmak/lib/targets/dotnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { shell, Scratch, setExtend, filterAsync } from '../util';
import { DotNetGenerator } from './dotnet/dotnetgenerator';
import { TargetBuilder, BuildOptions } from '../builder';
import { JsiiModule } from '../packaging';
import { toReleaseVersion } from './version-utils';

export const TARGET_FRAMEWORK = 'netcoreapp3.1';

Expand Down Expand Up @@ -274,7 +275,7 @@ export default class Dotnet extends Target {
assm: spec.Assembly,
): { [language: string]: PackageInfo } {
const packageId = assm.targets!.dotnet!.packageId;
const version = assm.version;
const version = toReleaseVersion(assm.version, 'dotnet');
const packageInfo: PackageInfo = {
repository: 'Nuget',
url: `https://www.nuget.org/packages/${packageId}/${version}`,
Expand Down
4 changes: 2 additions & 2 deletions packages/jsii-pacmak/lib/targets/dotnet/filegenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as xmlbuilder from 'xmlbuilder';
import { DotNetNameUtils } from './nameutils';
import * as logging from '../../logging';
import { TARGET_FRAMEWORK } from '../dotnet';
import { toNuGetVersionRange } from '../version-utils';
import { toNuGetVersionRange, toReleaseVersion } from '../version-utils';

// Represents a dependency in the dependency tree.
export class DotNetDependency {
Expand Down Expand Up @@ -182,6 +182,6 @@ export class FileGenerator {
// suffix is guaranteed to start with a leading `-`
return `${assembly.version}${suffix}`;
}
return assembly.version;
return toReleaseVersion(assembly.version, 'dotnet');
}
}
15 changes: 8 additions & 7 deletions packages/jsii-pacmak/lib/targets/java.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { shell, Scratch, slugify, setExtend } from '../util';
import { TargetBuilder, BuildOptions } from '../builder';
import { JsiiModule } from '../packaging';
import { VERSION, VERSION_DESC } from '../version';
import { toMavenVersionRange } from './version-utils';
import { toMavenVersionRange, toReleaseVersion } from './version-utils';
import {
INCOMPLETE_DISCLAIMER_COMPILING,
INCOMPLETE_DISCLAIMER_NONCOMPILING,
Expand Down Expand Up @@ -366,6 +366,7 @@ export default class Java extends Target {
): { [language: string]: PackageInfo } {
const groupId = assm.targets!.java!.maven.groupId;
const artifactId = assm.targets!.java!.maven.artifactId;
const releaseVersion = toReleaseVersion(assm.version, 'java');
const url = `https://repo1.maven.org/maven2/${groupId.replace(
/\./g,
'/',
Expand All @@ -379,27 +380,27 @@ export default class Java extends Target {
language: 'xml',
code: xmlbuilder
.create({
dependency: { groupId, artifactId, version: assm.version },
dependency: { groupId, artifactId, version: releaseVersion },
})
.end({ pretty: true })
.replace(/<\?\s*xml(\s[^>]+)?>\s*/m, ''),
},
'Apache Buildr': `'${groupId}:${artifactId}:jar:${assm.version}'`,
'Apache Buildr': `'${groupId}:${artifactId}:jar:${releaseVersion}'`,
'Apache Ivy': {
language: 'xml',
code: xmlbuilder
.create({
dependency: {
'@groupId': groupId,
'@name': artifactId,
'@rev': assm.version,
'@rev': releaseVersion,
},
})
.end({ pretty: true })
.replace(/<\?\s*xml(\s[^>]+)?>\s*/m, ''),
},
'Groovy Grape': `@Grapes(\n@Grab(group='${groupId}', module='${artifactId}', version='${assm.version}')\n)`,
'Gradle / Grails': `compile '${groupId}:${artifactId}:${assm.version}'`,
'Groovy Grape': `@Grapes(\n@Grab(group='${groupId}', module='${artifactId}', version='${releaseVersion}')\n)`,
'Gradle / Grails': `compile '${groupId}:${artifactId}:${releaseVersion}'`,
},
},
};
Expand Down Expand Up @@ -1088,7 +1089,7 @@ class JavaGenerator extends Generator {
*/
function makeVersion(version: string, suffix?: string): string {
if (!suffix) {
return version;
return toReleaseVersion(version, 'java');
}
if (!suffix.startsWith('-') && !suffix.startsWith('.')) {
throw new Error(
Expand Down
13 changes: 10 additions & 3 deletions packages/jsii-pacmak/lib/targets/js.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
import * as spec from '@jsii/spec';
import { Generator } from '../generator';
import { PackageInfo, Target } from '../target';
import { toReleaseVersion } from './version-utils';

export default class JavaScript extends Target {
public static toPackageInfos(
assm: spec.Assembly,
): { [language: string]: PackageInfo } {
const packageInfo: PackageInfo = {
repository: 'NPM',
url: `https://www.npmjs.com/package/${assm.name}/v/${assm.version}`,
url: `https://www.npmjs.com/package/${assm.name}/v/${toReleaseVersion(
assm.version,
'js',
)}`,
usage: {
'package.json': {
language: 'js',
code: JSON.stringify({ [assm.name]: `^${assm.version}` }, null, 2),
},
npm: {
language: 'console',
code: `$ npm i ${assm.name}@${assm.version}`,
code: `$ npm i ${assm.name}@${toReleaseVersion(assm.version, 'js')}`,
},
yarn: {
language: 'console',
code: `$ yarn add ${assm.name}@${assm.version}`,
code: `$ yarn add ${assm.name}@${toReleaseVersion(
assm.version,
'js',
)}`,
},
},
};
Expand Down
4 changes: 2 additions & 2 deletions packages/jsii-pacmak/lib/targets/python.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
Rosetta,
typeScriptSnippetFromSource,
} from 'jsii-rosetta';
import { toPythonVersionRange } from './version-utils';
import { toPythonVersionRange, toReleaseVersion } from './version-utils';
import {
INCOMPLETE_DISCLAIMER_COMPILING,
INCOMPLETE_DISCLAIMER_NONCOMPILING,
Expand Down Expand Up @@ -2160,7 +2160,7 @@ class PythonGenerator extends Generator {
this.package = new Package(
this,
assm.targets!.python!.distName,
assm.version,
toReleaseVersion(assm.version, 'python'),
assm,
);

Expand Down
104 changes: 98 additions & 6 deletions packages/jsii-pacmak/lib/targets/version-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { Comparator, Range } from 'semver';
import { Comparator, Range, parse } from 'semver';
import { inspect } from 'util';
import type { TargetName } from '.';
import { info } from '../logging';

/**
* Converts a SemVer range expression to a Maven version range expression.
Expand All @@ -12,7 +15,10 @@ export function toMavenVersionRange(
semverRange: string,
suffix?: string,
): string {
return toBracketNotation(semverRange, suffix, { semver: false });
return toBracketNotation(semverRange, suffix, {
semver: false,
target: 'java',
});
}

/**
Expand All @@ -23,7 +29,10 @@ export function toMavenVersionRange(
* @see https://docs.microsoft.com/en-us/nuget/concepts/package-versioning#version-ranges-and-wildcards
*/
export function toNuGetVersionRange(semverRange: string): string {
return toBracketNotation(semverRange, undefined, { semver: false });
return toBracketNotation(semverRange, undefined, {
semver: false,
target: 'dotnet',
});
}

/**
Expand All @@ -38,7 +47,10 @@ export function toPythonVersionRange(semverRange: string): string {
.map((set) =>
set
.map((comp) => {
const versionId = comp.semver.raw?.replace(/-0$/, '') ?? '0.0.0';
const versionId = toReleaseVersion(
comp.semver.raw?.replace(/-0$/, '') ?? '0.0.0',
'python',
);
switch (comp.operator) {
case '':
// With ^0.0.0, somehow we get a left entry with an empty operator and value, we'll fix this up
Expand All @@ -55,10 +67,90 @@ export function toPythonVersionRange(semverRange: string): string {
.join(', ');
}

/**
* Converts an original version number from the NPM convention to the target
* language's convention for expressing the same. For versions that do not
* include a prerelease identifier, this always returns the assembly version
* unmodified.
*
* @param assemblyVersion the assembly version being released
* @param target the target language for which the verison is destined
*
* @returns the version that should be serialized
*/
export function toReleaseVersion(
assemblyVersion: string,
target: TargetName,
): string {
const version = parse(assemblyVersion, { includePrerelease: true });
if (version == null) {
throw new Error(
`Unable to parse the provided assembly version: "${assemblyVersion}"`,
);
}
if (version.prerelease.length === 0) {
return assemblyVersion;
}
switch (target) {
case 'python':
// Python supports a limited set of identifiers... And we have a mapping table...
// https://packaging.python.org/guides/distributing-packages-using-setuptools/#pre-release-versioning
const [label, sequence, ...rest] = version.prerelease;
if (rest.length > 0) {
info(
`Unable to map prerelease identifier (in: ${assemblyVersion}) components to python: ${inspect(
version.prerelease,
)}`,
);
break;
}
if (!Number.isInteger(sequence)) {
info(
`Unable to map prerelease identifier (in: ${assemblyVersion}) to python, as sequence ${inspect(
sequence,
)} is not an integer`,
);
break;
}
switch (label) {
case 'dev':
return `${version.major}.${version.minor}.${version.patch}.dev${sequence}`;
case 'alpha':
return `${version.major}.${version.minor}.${version.patch}.a${sequence}`;
case 'beta':
return `${version.major}.${version.minor}.${version.patch}.b${sequence}`;
case 'rc':
return `${version.major}.${version.minor}.${version.patch}.rc${sequence}`;
default:
info(
`Unable to map prerelease identifier (in: ${assemblyVersion}) to python, as label ${inspect(
label,
)} is not mapped (only "dev", "alpha", "beta" and "rc" are)`,
);
}
break;
case 'dotnet':
case 'java':
case 'js':
// Not touching - the NPM version number should be usable as-is
break;
default:
info(
`Unknown target ${inspect(
target,
)} for ${assemblyVersion}. Returning version as-is.`,
);
}
return assemblyVersion;
}

function toBracketNotation(
semverRange: string,
suffix?: string,
{ semver = true }: { semver?: boolean } = {},
{
semver = true,
target = 'js',
}: { semver?: boolean; target?: TargetName } = {},
): string {
if (semverRange === '*') {
semverRange = '>=0.0.0';
Expand Down Expand Up @@ -137,6 +229,6 @@ function toBracketNotation(
if (trimDashZero) {
str = str.replace(/-0$/, '');
}
return suffix ? `${str}${suffix}` : str;
return suffix ? `${str}${suffix}` : toReleaseVersion(str, target);
}
}
53 changes: 53 additions & 0 deletions packages/jsii-pacmak/test/targets/version-utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { TargetName } from '../../lib/targets';
import {
toMavenVersionRange,
toNuGetVersionRange,
toPythonVersionRange,
toReleaseVersion,
} from '../../lib/targets/version-utils';

const examples: Record<
Expand Down Expand Up @@ -102,3 +104,54 @@ describe(toPythonVersionRange, () => {
expect(toPythonVersionRange(semver)).toEqual(python));
}
});

describe(toReleaseVersion, () => {
type Expectations = { readonly [K in TargetName]: string };
const examples: Record<string, Expectations> = {
'1.2.3': {
dotnet: '1.2.3',
go: '1.2.3',
java: '1.2.3',
js: '1.2.3',
python: '1.2.3',
},
'1.2.3-pre': {
dotnet: '1.2.3-pre',
go: '1.2.3-pre',
java: '1.2.3-pre',
js: '1.2.3-pre',
python: '1.2.3-pre',
},
'1.2.3-alpha.1337': {
dotnet: '1.2.3-alpha.1337',
go: '1.2.3-alpha.1337',
java: '1.2.3-alpha.1337',
js: '1.2.3-alpha.1337',
python: '1.2.3.a1337',
},
'1.2.3-beta.42': {
dotnet: '1.2.3-beta.42',
go: '1.2.3-beta.42',
java: '1.2.3-beta.42',
js: '1.2.3-beta.42',
python: '1.2.3.b42',
},
'1.2.3-rc.9': {
dotnet: '1.2.3-rc.9',
go: '1.2.3-rc.9',
java: '1.2.3-rc.9',
js: '1.2.3-rc.9',
python: '1.2.3.rc9',
},
};

for (const [version, targets] of Object.entries(examples)) {
test(`"${version}" translations`, () => {
for (const [target, targetVersion] of Object.entries(targets)) {
expect(toReleaseVersion(version, target as TargetName)).toBe(
targetVersion,
);
}
});
}
});