From 077262f6b0eb158d3592cecf0e4faf5473199b21 Mon Sep 17 00:00:00 2001 From: Rico Hermans Date: Thu, 31 Oct 2024 14:34:05 +0100 Subject: [PATCH] chore: automatically age out old TypeScript versions from testing (#31960) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In our integ tests, we were testing TypeScript versions from 3.9 up to the latest version, which recently broke because of a change to modernize the init templates. We should up this range to a recent version; the common support lifetime of a TypeScript version is the one on `DefinitelyTyped`, the types registry for TypeScript packages. They only target TS versions less than 2 years old, which at the time of this PR is 4.9 and higher. Encode that policy into code automatically, so that we don't have to manually keep this minimum version up-to-date. This currently ages out the following versions: ``` ✕ typescript 3.9 init app ✕ typescript 4.0 init app ✕ typescript 4.1 init app ✕ typescript 4.2 init app ✕ typescript 4.3 init app ✕ typescript 4.4 init app ✕ typescript 4.5 init app ✕ typescript 4.6 init app ✕ typescript 4.7 init app ✕ typescript 4.8 init app ------------------------------8< cut line ----- ✕ typescript 4.9 init app ✓ typescript 5.0 init app ✓ typescript 5.1 init app ✓ typescript 5.2 init app ✓ typescript 5.3 init app ✓ typescript 5.4 init app ✓ typescript 5.5 init app ✓ typescript 5.6 init app ``` Unfortunately not enough to save the TypeScript template modernization change entirely... but at least it's an improvement. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk-testing/cli-integ/lib/npm.ts | 30 ++++++++++++++++++- .../init-typescript-app.integtest.ts | 12 ++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk-testing/cli-integ/lib/npm.ts b/packages/@aws-cdk-testing/cli-integ/lib/npm.ts index 0ec1052247478..fd7923c2e01ee 100644 --- a/packages/@aws-cdk-testing/cli-integ/lib/npm.ts +++ b/packages/@aws-cdk-testing/cli-integ/lib/npm.ts @@ -10,4 +10,32 @@ export function typescriptVersionsSync(): string[] { const versions: string[] = JSON.parse(stdout); return Array.from(new Set(versions.map(v => v.split('.').slice(0, 2).join('.')))); -} \ No newline at end of file +} + +/** + * Use NPM preinstalled on the machine to query publish times of versions + */ +export function typescriptVersionsYoungerThanDaysSync(days: number, versions: string[]): string[] { + const { stdout } = spawnSync('npm', ['--silent', 'view', 'typescript', 'time', '--json'], { encoding: 'utf-8' }); + const versionTsMap: Record = JSON.parse(stdout); + + const cutoffDate = new Date(Date.now() - (days * 24 * 3600 * 1000)); + const cutoffDateS = cutoffDate.toISOString(); + + const recentVersions = Object.entries(versionTsMap) + .filter(([_, dateS]) => dateS > cutoffDateS) + .map(([v]) => v); + + // Input versions are of the form 3.9, 5.2, etc. + // Actual versions are of the form `3.9.15`, `5.3.0-dev.20511311`. + // Return only 2-digit versions for which there is a non-prerelease version in the set of recentVersions + // So a 2-digit versions that is followed by `.` until the end of the string. + return versions.filter((twoV) => { + const re = new RegExp(`^${reQuote(twoV)}\\.\\d+$`); + return recentVersions.some(fullV => fullV.match(re)); + }); +} + +function reQuote(str: string): string { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} diff --git a/packages/@aws-cdk-testing/cli-integ/tests/init-typescript-app/init-typescript-app.integtest.ts b/packages/@aws-cdk-testing/cli-integ/tests/init-typescript-app/init-typescript-app.integtest.ts index 158764ef3c973..f45cfaeff0ac5 100644 --- a/packages/@aws-cdk-testing/cli-integ/tests/init-typescript-app/init-typescript-app.integtest.ts +++ b/packages/@aws-cdk-testing/cli-integ/tests/init-typescript-app/init-typescript-app.integtest.ts @@ -1,7 +1,7 @@ import { promises as fs } from 'fs'; import * as path from 'path'; import { integTest, withTemporaryDirectory, ShellHelper, withPackages, TemporaryDirectoryContext } from '../../lib'; -import { typescriptVersionsSync } from '../../lib/npm'; +import { typescriptVersionsSync, typescriptVersionsYoungerThanDaysSync } from '../../lib/npm'; ['app', 'sample-app'].forEach(template => { integTest(`typescript init ${template}`, withTemporaryDirectory(withPackages(async (context) => { @@ -19,10 +19,18 @@ import { typescriptVersionsSync } from '../../lib/npm'; }))); }); +// Same as https://github.com/DefinitelyTyped/DefinitelyTyped?tab=readme-ov-file#support-window +const TYPESCRIPT_VERSION_AGE_DAYS = 2 * 365; + +const TYPESCRIPT_VERSIONS = typescriptVersionsYoungerThanDaysSync(TYPESCRIPT_VERSION_AGE_DAYS, typescriptVersionsSync()); + +// eslint-disable-next-line no-console +console.log(TYPESCRIPT_VERSIONS); + /** * Test our generated code with various versions of TypeScript */ -typescriptVersionsSync().forEach(tsVersion => { +TYPESCRIPT_VERSIONS.forEach(tsVersion => { integTest(`typescript ${tsVersion} init app`, withTemporaryDirectory(withPackages(async (context) => { const shell = ShellHelper.fromContext(context); await context.packages.makeCliAvailable();