66import * as fs from 'fs' ;
77import * as os from 'os' ;
88import { 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
1115interface 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