Skip to content

Commit 630c34c

Browse files
Fix 'npm run gulp updatePackageDependencies'
This commit improves the updatePackageDependencies gulp task so that hopefully it will work for all three teams needing to update package.json to point at new runtime dependancies in hopefully a safe way. Fixes/improvements: * Make the task work again since gulp no longer likes the command line argument hack that we used to have * Add support for computting checksums * Add verification that the primary and fallback URLs have the same hash and both work
1 parent 6657f99 commit 630c34c

File tree

5 files changed

+125
-39
lines changed

5 files changed

+125
-39
lines changed

.vscode/launch.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"version": "0.2.0",
3-
"configurations": [
3+
"configurations": [
44
{
55
"name": "Launch Extension",
66
"type": "extensionHost",
@@ -100,6 +100,21 @@
100100
"outFiles": [
101101
"${workspaceRoot}/out/test/**/*.js"
102102
]
103+
},
104+
{
105+
"type": "node",
106+
"request": "launch",
107+
"name": "Launch gulp task",
108+
"preLaunchTask": "build",
109+
"program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js",
110+
"args": [
111+
"updatePackageDependencies"
112+
],
113+
"env": {
114+
"NEW_DEPS_URLS": "https://download.visualstudio.microsoft.com/download/pr/9c36608c-cb04-451f-8df7-547eeb9b5d55/8626f25cb9fb40cd6fa2508009fccd1d/coreclr-debug-linux-x64.zip,https://download.visualstudio.microsoft.com/download/pr/9c36608c-cb04-451f-8df7-547eeb9b5d55/b5b307980f5da0bc161018acfad043c9/coreclr-debug-osx-x64.zip,https://download.visualstudio.microsoft.com/download/pr/9c36608c-cb04-451f-8df7-547eeb9b5d55/4b9c72a90d4558d8e72e653c1ea79936/coreclr-debug-win7-x64.zip",
115+
"NEW_DEPS_VERSION": "1.18.0"
116+
},
117+
"cwd": "${workspaceFolder}"
103118
}
104119
]
105120
}

gulpfile.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ require('./tasks/offlinePackagingTasks');
1616
require('./tasks/backcompatTasks');
1717
require('./tasks/coverageTasks');
1818

19-
gulp.task('generateOptionsSchema', () => {
19+
gulp.task('generateOptionsSchema', () : void => {
2020
optionsSchemaGenerator.GenerateOptionsSchema();
2121
});
2222

23-
gulp.task('updatePackageDependencies', () => {
24-
packageDependencyUpdater.updatePackageDependencies();
23+
// Disable warning about wanting an async function
24+
// tslint:disable-next-line
25+
gulp.task('updatePackageDependencies', () : Promise<void> => {
26+
return packageDependencyUpdater.updatePackageDependencies();
2527
});
2628

2729
gulp.task('tslint', () => {

package-lock.json

Lines changed: 1 addition & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/packageManager/isValidDownload.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,10 @@ export interface DownloadValidator {
1212
}
1313

1414
export function isValidDownload(buffer: Buffer, integrity: string, eventStream: EventStream): boolean {
15-
let hash = crypto.createHash('sha256');
1615
if (integrity && integrity.length > 0) {
1716
eventStream.post(new DownloadValidation());
18-
hash.update(buffer);
19-
let value = hash.digest('hex');
20-
if (value.toUpperCase() == integrity.toUpperCase()) {
17+
let value = getBufferIntegrityHash(buffer);
18+
if (value == integrity.toUpperCase()) {
2119
eventStream.post(new IntegrityCheckSuccess());
2220
return true;
2321
}
@@ -28,4 +26,11 @@ export function isValidDownload(buffer: Buffer, integrity: string, eventStream:
2826

2927
// no integrity has been specified
3028
return true;
29+
}
30+
31+
export function getBufferIntegrityHash(buffer: Buffer) : string {
32+
let hash = crypto.createHash('sha256');
33+
hash.update(buffer);
34+
let value = hash.digest('hex').toUpperCase();
35+
return value;
3136
}

src/tools/UpdatePackageDependencies.ts

Lines changed: 94 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,47 @@
66
import * as fs from 'fs';
77
import * as os from 'os';
88
import { Package } from '../packageManager/Package';
9-
9+
import { DownloadFile } from '../packageManager/FileDownloader';
10+
import { EventStream } from '../EventStream';
11+
import * as Event from "../omnisharp/loggingEvents";
12+
import NetworkSettings, { NetworkSettingsProvider } from '../NetworkSettings';
13+
import { getBufferIntegrityHash } from '../packageManager/isValidDownload';
1014

1115
interface PackageJSONFile
1216
{
1317
runtimeDependencies : Package[];
1418
}
1519

16-
export function updatePackageDependencies() {
20+
export async function updatePackageDependencies() : Promise<void> {
1721

18-
const urlsIndex = process.argv.indexOf("--urls");
19-
const newPrimaryUrls = urlsIndex >= 0 ? process.argv[urlsIndex+1] : undefined;
20-
21-
const fallbackUrlsIndex = process.argv.indexOf("--fallbackUrls");
22-
const newFallbackUrls = fallbackUrlsIndex >= 0 ? process.argv[fallbackUrlsIndex+1] : undefined;
22+
const newPrimaryUrls = process.env["NEW_DEPS_URLS"];
23+
const newVersion = process.env["NEW_DEPS_VERSION"];
2324

24-
if (newPrimaryUrls === undefined || newPrimaryUrls === "-?" || newPrimaryUrls === "-h") {
25-
console.log("This command will update the URLs for package dependencies in package.json");
25+
if (!newPrimaryUrls || !newVersion) {
26+
console.log();
27+
console.log("'npm run gulp updatePackageDependencies' will update package.json with new URLs of dependencies.");
2628
console.log();
27-
console.log("Syntax: updatePackageDependencies --urls \"<url1>,<url2>,...\" [--fallbackUrls \"<fallback-url-1>,<fallback-url-2>...\"]");
29+
console.log("To use:");
30+
const setEnvVarPrefix = os.platform() === 'win32' ? "set " : "export ";
31+
const setEnvVarQuote = os.platform() === 'win32' ? "" : "\'";
32+
console.log(` ${setEnvVarPrefix}NEW_DEPS_URLS=${setEnvVarQuote}https://example1/foo-osx.zip,https://example1/foo-win.zip,https://example1/foo-linux.zip${setEnvVarQuote}`);
33+
console.log(` ${setEnvVarPrefix}NEW_DEPS_VERSION=${setEnvVarQuote}1.2.3${setEnvVarQuote}`);
34+
console.log(" npm run gulp updatePackageDependencies");
2835
console.log();
2936
return;
3037
}
3138

32-
if (newPrimaryUrls.length === 0) {
33-
throw new Error("Invalid first argument to updatePackageDependencies. URL string argument expected.");
39+
const newPrimaryUrlArray = newPrimaryUrls.split(',');
40+
for (let urlToUpdate of newPrimaryUrlArray) {
41+
if (!urlToUpdate.startsWith("https://")) {
42+
throw new Error("Unexpected 'NEW_DEPS_URLS' value. All URLs should start with 'https://'.");
43+
}
3444
}
3545

46+
if (! /^[0-9]+\.[0-9]+\.[0-9]+$/.test(newVersion)) {
47+
throw new Error("Unexpected 'NEW_DEPS_VERSION' value. Expected format similar to: 1.2.3.");
48+
}
49+
3650
let packageJSON: PackageJSONFile = JSON.parse(fs.readFileSync('package.json').toString());
3751

3852
// map from lowercase filename to Package
@@ -54,23 +68,79 @@ export function updatePackageDependencies() {
5468
if (dependency === undefined) {
5569
throw new Error(`Unable to update item for url '${url}'. No 'runtimeDependency' found with filename '${fileName}'.`);
5670
}
57-
58-
console.log(`Updating ${url}`);
5971
return dependency;
6072
};
6173

62-
newPrimaryUrls.split(',').forEach(urlToUpdate =>{
63-
console.log(`Trying to update ${urlToUpdate}`);
64-
let dependency = findDependencyToUpdate(urlToUpdate);
65-
dependency.url = urlToUpdate;
74+
const dottedVersionRegExp = /[0-9]+\.[0-9]+\.[0-9]+/g;
75+
const dashedVersionRegExp = /[0-9]+-[0-9]+-[0-9]+/g;
76+
77+
const getMatchCount = (regexp: RegExp, searchString: string) : number => {
78+
regexp.lastIndex = 0;
79+
let retVal = 0;
80+
while (regexp.test(searchString)) {
81+
retVal++;
82+
}
83+
regexp.lastIndex = 0;
84+
return retVal;
85+
};
86+
87+
// First quickly make sure we could match up the URL to an existing item.
88+
for (let urlToUpdate of newPrimaryUrlArray) {
89+
const dependency = findDependencyToUpdate(urlToUpdate);
90+
if (dependency.fallbackUrl) {
91+
const dottedMatches : number = getMatchCount(dottedVersionRegExp, dependency.fallbackUrl);
92+
const dashedMatches : number = getMatchCount(dashedVersionRegExp, dependency.fallbackUrl);
93+
const matchCount : number = dottedMatches + dashedMatches;
94+
95+
if (matchCount == 0) {
96+
throw new Error(`Version number not found in fallback URL '${dependency.fallbackUrl}'.`);
97+
}
98+
if (matchCount > 1) {
99+
throw new Error(`Ambiguous version pattern found in fallback URL '${dependency.fallbackUrl}'. Multiple version strings found.`);
100+
}
101+
}
102+
}
103+
104+
// Next take another pass to try and update to the URL
105+
const eventStream = new EventStream();
106+
eventStream.subscribe((event: Event.BaseEvent) => {
107+
switch (event.constructor.name) {
108+
case Event.DownloadFailure.name:
109+
console.log("Failed to download: " + (<Event.DownloadFailure>event).message);
110+
break;
111+
}
66112
});
113+
const networkSettingsProvider : NetworkSettingsProvider = () => new NetworkSettings(/*proxy:*/ null, /*stringSSL:*/ true);
67114

68-
if (newFallbackUrls !== undefined) {
69-
newFallbackUrls.split(',').forEach(urlToUpdate =>{
70-
console.log(`Trying to update ${urlToUpdate}`);
71-
let dependency = findDependencyToUpdate(urlToUpdate);
72-
dependency.fallbackUrl = urlToUpdate;
73-
});
115+
const downloadAndGetHash = async (url:string) : Promise<string> => {
116+
console.log(`Downlodaing from '${url}'`);
117+
const buffer : Buffer = await DownloadFile(url, eventStream, networkSettingsProvider, url, null);
118+
return getBufferIntegrityHash(buffer);
119+
};
120+
121+
for (let urlToUpdate of newPrimaryUrlArray) {
122+
let dependency = findDependencyToUpdate(urlToUpdate);
123+
dependency.url = urlToUpdate;
124+
dependency.integrity = await downloadAndGetHash(dependency.url);
125+
126+
if (dependency.fallbackUrl) {
127+
128+
// NOTE: We already verified in the first loop that one of these patterns will work, grab the one that does
129+
let regex: RegExp = dottedVersionRegExp;
130+
let newValue: string = newVersion;
131+
if (!dottedVersionRegExp.test(dependency.fallbackUrl)) {
132+
regex = dashedVersionRegExp;
133+
newValue = newVersion.replace(/\./g, "-");
134+
}
135+
dottedVersionRegExp.lastIndex = 0;
136+
137+
dependency.fallbackUrl = dependency.fallbackUrl.replace(regex, newValue);
138+
const fallbackUrlIntegrity = await downloadAndGetHash(dependency.fallbackUrl);
139+
140+
if (dependency.integrity !== fallbackUrlIntegrity) {
141+
throw new Error(`File downloaded from primary URL '${dependency.url}' doesn't match '${dependency.fallbackUrl}'.`);
142+
}
143+
}
74144
}
75145

76146
let content = JSON.stringify(packageJSON, null, 2);

0 commit comments

Comments
 (0)