diff --git a/.vscode/launch.json b/.vscode/launch.json index 3a6a18ecef..4928118d18 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -104,7 +104,7 @@ { "type": "node", "request": "launch", - "name": "Launch gulp task", + "name": "Update package dependencies", "preLaunchTask": "build", "program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js", "args": [ diff --git a/package-lock.json b/package-lock.json index 431d31f962..bd8aa284ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2606,6 +2606,24 @@ "locate-path": "^2.0.0" } }, + "find-versions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.0.0.tgz", + "integrity": "sha512-IUvtItVFNmTtKoB0PRfbkR0zR9XMG5rWNO3qI1S8L0zdv+v2gqzM0pAunloxqbqAfT8w7bg8n/5gHzTXte8H5A==", + "dev": true, + "requires": { + "array-uniq": "^2.0.0", + "semver-regex": "^2.0.0" + }, + "dependencies": { + "array-uniq": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-2.0.0.tgz", + "integrity": "sha512-O3QZEr+3wDj7otzF7PjNGs6CA3qmYMLvt5xGkjY/V0VxS+ovvqVo/5wKM/OVOAyuX4DTh9H31zE/yKtO66hTkg==", + "dev": true + } + } + }, "findup-sync": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", @@ -9827,6 +9845,12 @@ "sver-compat": "^1.5.0" } }, + "semver-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", + "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "dev": true + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", diff --git a/package.json b/package.json index 38ad511dc6..e2e5f0fa6b 100644 --- a/package.json +++ b/package.json @@ -118,6 +118,7 @@ "copyfiles": "2.0.0", "cross-env": "5.1.4", "del": "3.0.0", + "find-versions": "^3.0.0", "get-port": "3.2.0", "glob-promise": "3.4.0", "gulp": "4.0.0", diff --git a/src/tools/UpdatePackageDependencies.ts b/src/tools/UpdatePackageDependencies.ts index de034612f8..768ce49460 100644 --- a/src/tools/UpdatePackageDependencies.ts +++ b/src/tools/UpdatePackageDependencies.ts @@ -11,17 +11,23 @@ import { EventStream } from '../EventStream'; import * as Event from "../omnisharp/loggingEvents"; import NetworkSettings, { NetworkSettingsProvider } from '../NetworkSettings'; import { getBufferIntegrityHash } from '../packageManager/isValidDownload'; +const findVersions = require('find-versions'); -interface PackageJSONFile -{ - runtimeDependencies : Package[]; +interface PackageJSONFile { + runtimeDependencies: Package[]; + defaults: { + [key: string]: string; + }; } -export async function updatePackageDependencies() : Promise { +const dottedVersionRegExp = /[0-9]+\.[0-9]+\.[0-9]+/g; +const dashedVersionRegExp = /[0-9]+-[0-9]+-[0-9]+/g; + +export async function updatePackageDependencies(): Promise { const newPrimaryUrls = process.env["NEW_DEPS_URLS"]; const newVersion = process.env["NEW_DEPS_VERSION"]; - + if (!newPrimaryUrls || !newVersion) { console.log(); console.log("'npm run gulp updatePackageDependencies' will update package.json with new URLs of dependencies."); @@ -46,9 +52,9 @@ export async function updatePackageDependencies() : Promise { if (! /^[0-9]+\.[0-9]+\.[0-9]+$/.test(newVersion)) { throw new Error("Unexpected 'NEW_DEPS_VERSION' value. Expected format similar to: 1.2.3."); } - + let packageJSON: PackageJSONFile = JSON.parse(fs.readFileSync('package.json').toString()); - + // map from lowercase filename to Package const mapFileNameToDependency: { [key: string]: Package } = {}; @@ -61,8 +67,8 @@ export async function updatePackageDependencies() : Promise { } mapFileNameToDependency[fileName] = dependency; }); - - let findDependencyToUpdate = (url : string): Package => { + + let findDependencyToUpdate = (url: string): Package => { let fileName = getLowercaseFileNameFromUrl(url); let dependency = mapFileNameToDependency[fileName]; if (dependency === undefined) { @@ -71,34 +77,13 @@ export async function updatePackageDependencies() : Promise { return dependency; }; - const dottedVersionRegExp = /[0-9]+\.[0-9]+\.[0-9]+/g; - const dashedVersionRegExp = /[0-9]+-[0-9]+-[0-9]+/g; - - const getMatchCount = (regexp: RegExp, searchString: string) : number => { - regexp.lastIndex = 0; - let retVal = 0; - while (regexp.test(searchString)) { - retVal++; - } - regexp.lastIndex = 0; - return retVal; - }; - // First quickly make sure we could match up the URL to an existing item. for (let urlToUpdate of newPrimaryUrlArray) { const dependency = findDependencyToUpdate(urlToUpdate); - if (dependency.fallbackUrl) { - const dottedMatches : number = getMatchCount(dottedVersionRegExp, dependency.fallbackUrl); - const dashedMatches : number = getMatchCount(dashedVersionRegExp, dependency.fallbackUrl); - const matchCount : number = dottedMatches + dashedMatches; - - if (matchCount == 0) { - throw new Error(`Version number not found in fallback URL '${dependency.fallbackUrl}'.`); - } - if (matchCount > 1) { - throw new Error(`Ambiguous version pattern found in fallback URL '${dependency.fallbackUrl}'. Multiple version strings found.`); - } - } + //Fallback url should contain a version + verifyVersionSubstringCount(dependency.fallbackUrl, true); + verifyVersionSubstringCount(dependency.installPath); + verifyVersionSubstringCount(dependency.installTestPath); } // Next take another pass to try and update to the URL @@ -110,11 +95,11 @@ export async function updatePackageDependencies() : Promise { break; } }); - const networkSettingsProvider : NetworkSettingsProvider = () => new NetworkSettings(/*proxy:*/ null, /*stringSSL:*/ true); + const networkSettingsProvider: NetworkSettingsProvider = () => new NetworkSettings(/*proxy:*/ null, /*stringSSL:*/ true); - const downloadAndGetHash = async (url:string) : Promise => { - console.log(`Downlodaing from '${url}'`); - const buffer : Buffer = await DownloadFile(url, eventStream, networkSettingsProvider, url, null); + const downloadAndGetHash = async (url: string): Promise => { + console.log(`Downloading from '${url}'`); + const buffer: Buffer = await DownloadFile(url, eventStream, networkSettingsProvider, url, null); return getBufferIntegrityHash(buffer); }; @@ -122,21 +107,18 @@ export async function updatePackageDependencies() : Promise { let dependency = findDependencyToUpdate(urlToUpdate); dependency.url = urlToUpdate; dependency.integrity = await downloadAndGetHash(dependency.url); - - if (dependency.fallbackUrl) { - - // NOTE: We already verified in the first loop that one of these patterns will work, grab the one that does - let regex: RegExp = dottedVersionRegExp; - let newValue: string = newVersion; - if (!dottedVersionRegExp.test(dependency.fallbackUrl)) { - regex = dashedVersionRegExp; - newValue = newVersion.replace(/\./g, "-"); + dependency.fallbackUrl = replaceVersion(dependency.fallbackUrl, newVersion); + dependency.installPath = replaceVersion(dependency.installPath, newVersion); + dependency.installTestPath = replaceVersion(dependency.installTestPath, newVersion); + Object.keys(packageJSON.defaults).forEach(key => { + //Update the version present in the defaults + if (key.toLowerCase() == dependency.id.toLowerCase()) { + packageJSON.defaults[key] = newVersion; } - dottedVersionRegExp.lastIndex = 0; + }); - dependency.fallbackUrl = dependency.fallbackUrl.replace(regex, newValue); + if (dependency.fallbackUrl) { const fallbackUrlIntegrity = await downloadAndGetHash(dependency.fallbackUrl); - if (dependency.integrity !== fallbackUrlIntegrity) { throw new Error(`File downloaded from primary URL '${dependency.url}' doesn't match '${dependency.fallbackUrl}'.`); } @@ -155,7 +137,55 @@ export async function updatePackageDependencies() : Promise { fs.writeFileSync('package.json', content); } -function getLowercaseFileNameFromUrl(url : string) : string { + +function replaceVersion(fileName: string, newVersion: string): string { + if (!fileName) { + return fileName; //if the value is null or undefined return the same one + } + + let regex: RegExp = dottedVersionRegExp; + let newValue: string = newVersion; + if (!dottedVersionRegExp.test(fileName)) { + regex = dashedVersionRegExp; + newValue = newVersion.replace(/\./g, "-"); + } + dottedVersionRegExp.lastIndex = 0; + + if (!regex.test(fileName)) { + return fileName; //If the string doesn't contain any version return the same string + } + + return fileName.replace(regex, newValue); +} + +function verifyVersionSubstringCount(value: string, shouldContainVersion = false): void { + const getMatchCount = (regexp: RegExp, searchString: string): number => { + regexp.lastIndex = 0; + let retVal = 0; + while (regexp.test(searchString)) { + retVal++; + } + regexp.lastIndex = 0; + return retVal; + }; + + if (!value) { + return; + } + + const dottedMatches: number = getMatchCount(dottedVersionRegExp, value); + const dashedMatches: number = getMatchCount(dashedVersionRegExp, value); + const matchCount: number = dottedMatches + dashedMatches; + + if (shouldContainVersion && matchCount == 0) { + throw new Error(`Version number not found in '${value}'.`); + } + if (matchCount > 1) { + throw new Error(`Ambiguous version pattern found in '${value}'. Multiple version strings found.`); + } +} + +function getLowercaseFileNameFromUrl(url: string): string { if (!url.startsWith("https://")) { throw new Error(`Unexpected URL '${url}'. URL expected to start with 'https://'.`); @@ -166,5 +196,18 @@ function getLowercaseFileNameFromUrl(url : string) : string { } let index = url.lastIndexOf("/"); - return url.substr(index + 1).toLowerCase(); + let fileName = url.substr(index + 1).toLowerCase(); + let versions = findVersions(fileName); + if (!versions || versions.length == 0) { + return fileName; + } + + if (versions.length > 1) { + //we expect only one version string to be present in the last part of the url + throw new Error(`Ambiguous version pattern found URL '${url}'. Multiple version strings found.`); + } + + let versionIndex = fileName.indexOf(versions[0]); + //remove the dash before the version number + return fileName.substr(0, versionIndex - 1).toLowerCase(); } \ No newline at end of file