1
+ import * as fs from 'fs' ;
2
+ import * as os from 'os' ;
3
+ import * as path from 'path' ;
4
+ import * as semver from 'semver' ;
5
+ import { convertXmlToVersionList , updateXml } from './utils/cloud_storage_xml' ;
6
+ import { initOptions , requestBody , JsonObject , requestBinary } from './utils/http_utils' ;
7
+ import { changeFilePermissions , generateConfigFile , getBinaryPathFromConfig , removeFiles , renameFileWithVersion , unzipFile , zipFileList } from './utils/file_utils' ;
8
+ import { isExpired } from './utils/file_utils' ;
9
+ import { OUT_DIR , ProviderConfig , ProviderInterface } from './provider' ;
10
+
11
+ export class Chromium implements ProviderInterface {
12
+ cacheFileName = 'chromium-all.json' ;
13
+ cacheVersionFileName = 'chromium-version.json' ;
14
+ cacheStorageFileName = 'chromium-storage.json' ;
15
+ compressedBinaryFileName = 'chromium.zip' ;
16
+ configFileName = 'chromium.config.json' ;
17
+ ignoreSSL = false ;
18
+ osType = os . type ( ) ;
19
+ osArch = os . arch ( ) ;
20
+ outDir = OUT_DIR ;
21
+ proxy : string = null ;
22
+
23
+ constructor ( providerConfig ?: ProviderConfig ) {
24
+ if ( ! providerConfig ) {
25
+ return ;
26
+ }
27
+ if ( providerConfig . cacheFileName ) {
28
+ this . cacheFileName = providerConfig . cacheFileName ;
29
+ }
30
+ if ( providerConfig . configFileName ) {
31
+ this . configFileName = providerConfig . configFileName ;
32
+ }
33
+ this . ignoreSSL = providerConfig . ignoreSSL ;
34
+ if ( providerConfig . osArch ) {
35
+ this . osArch = providerConfig . osArch ;
36
+ }
37
+ if ( providerConfig . osType ) {
38
+ this . osType = providerConfig . osType ;
39
+ }
40
+ if ( providerConfig . outDir ) {
41
+ this . outDir = providerConfig . outDir ;
42
+ }
43
+ if ( providerConfig . proxy ) {
44
+ this . proxy = providerConfig . proxy ;
45
+ }
46
+ }
47
+
48
+ private makeDirectory ( fileName : string ) {
49
+ const dir = path . dirname ( fileName ) ;
50
+ try {
51
+ fs . mkdirSync ( dir ) ;
52
+ } catch ( err ) {
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Step 1: Download the json file that contains all the releases by OS. Each
58
+ * OS will have a list of release versions. The requested body will also be
59
+ * written to the out directory.
60
+ *
61
+ * The requested url is https://omahaproxy.appspot.com/all.json. Some other
62
+ * urls include a timestamped csv https://omahaproxy.appspot.com/history.
63
+ * @return Promise of the all-json file.
64
+ */
65
+ async downloadAllJson ( ) : Promise < JsonObject > {
66
+ const fileName = path . resolve ( this . outDir , this . cacheFileName ) ;
67
+ if ( ! isExpired ( fileName ) ) {
68
+ return JSON . parse ( fs . readFileSync ( fileName ) . toString ( ) ) ;
69
+ } else {
70
+ this . makeDirectory ( fileName ) ;
71
+ const httpOptions = { fileName, ignoreSSL : this . ignoreSSL ,
72
+ proxy : this . proxy } ;
73
+
74
+ if ( isExpired ( fileName ) ) {
75
+ const allJsonUrl = 'https://omahaproxy.appspot.com/all.json' ;
76
+ let contents = await requestBody ( allJsonUrl , httpOptions ) ;
77
+ contents = `{ "all": ${ contents } }` ;
78
+ const jsonObj = JSON . parse ( contents ) ;
79
+ fs . writeFileSync ( fileName , JSON . stringify ( jsonObj , null , 2 ) ) ;
80
+ return jsonObj ;
81
+ } else {
82
+ const contents = fs . readFileSync ( fileName ) . toString ( ) ;
83
+ return JSON . parse ( contents ) ;
84
+ }
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Step 2: From the all-json object, make a request that matches the major
90
+ * version requested. The requested body will also be written to file in the
91
+ * out directory.
92
+ *
93
+ * An example of a requsted url is
94
+ * https://omahaproxy.appspot.com/deps.json?version=72.0.3626.81
95
+ * @param allJson The all-json object.
96
+ * @param majorVersion The major version, this must be a whole number.
97
+ */
98
+ async downloadVersionJson ( allJson : JsonObject , majorVersion : string
99
+ ) : Promise < JsonObject > {
100
+ const fileName = path . resolve ( this . outDir ,
101
+ this . cacheVersionFileName . replace ( '.json' , `-${ majorVersion } .json` ) ) ;
102
+ if ( ! isExpired ( fileName ) ) {
103
+ return JSON . parse ( fs . readFileSync ( fileName ) . toString ( ) ) ;
104
+ } else {
105
+ this . makeDirectory ( fileName ) ;
106
+
107
+ // Look up a version that makes sense.
108
+ const all = allJson [ 'all' ] ;
109
+ let os = '' ;
110
+ if ( this . osType === 'Windows_NT' ) {
111
+ os = 'win' ;
112
+ if ( this . osArch === 'x64' ) {
113
+ os = 'win64' ;
114
+ }
115
+ } else if ( this . osType === 'Linux' ) {
116
+ os = 'linux' ;
117
+ } else {
118
+ os = 'mac' ;
119
+ }
120
+
121
+ let workingFullVersion = '' ;
122
+ let workingSemanticVersion = '0.0.0' ;
123
+ for ( let item of all ) {
124
+ if ( item [ 'os' ] === os ) {
125
+ const versions = item [ 'versions' ] ;
126
+ for ( let version of versions ) {
127
+ const fullVersion = version [ 'current_version' ] ;
128
+ const major = fullVersion . split ( '.' ) [ 0 ] ;
129
+ const minor = fullVersion . split ( '.' ) [ 1 ] ;
130
+ const patch = fullVersion . split ( '.' ) [ 2 ] ;
131
+ const semanticVersion = `${ major } .${ minor } .${ patch } ` ;
132
+ if ( majorVersion === major ) {
133
+ if ( semver . gt ( semanticVersion , workingSemanticVersion ) ) {
134
+ workingFullVersion = fullVersion ;
135
+ }
136
+ }
137
+ }
138
+ }
139
+ }
140
+
141
+ // Make a request and write it out to file.
142
+ const httpOptions = { fileName, ignoreSSL : this . ignoreSSL ,
143
+ proxy : this . proxy } ;
144
+
145
+ const depsUrl = 'https://omahaproxy.appspot.com/deps.json?version=' +
146
+ workingFullVersion ;
147
+ const contents = await requestBody ( depsUrl , httpOptions ) ;
148
+ const jsonObj = JSON . parse ( contents ) ;
149
+ fs . writeFileSync ( fileName , JSON . stringify ( jsonObj , null , 2 ) ) ;
150
+ return jsonObj ;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Step 3: From the downloaded-version-json object, get the revision number.
156
+ * This is the "chromium_base_position" and make a request to the storage
157
+ * bucket. If the returned value is {"kind": "storage#objects"}, then
158
+ * decrement the revision number.
159
+ *
160
+ * An example is the chromium_base_position revision number (612437).
161
+ * https://www.googleapis.com/storage/v1/b/chromium-browser-snapshots/o?delimiter=/&prefix=Linux_x64/612437/
162
+ * returns {"kind": "storage#objects"}.
163
+ *
164
+ * We keep decrementing the number until we reach 612434 where there is a list
165
+ * of items.
166
+ * @param downloadJson The download-version-json object.
167
+ * @param majorVersion The major version, this must be a whole number.
168
+ */
169
+ async downloadStorageObject ( downloadJson : JsonObject , majorVersion : string
170
+ ) : Promise < JsonObject > {
171
+ const fileName = path . resolve ( this . outDir ,
172
+ this . cacheStorageFileName . replace ( '.json' , `-${ majorVersion } .json` ) ) ;
173
+ if ( ! isExpired ( fileName ) ) {
174
+ return JSON . parse ( fs . readFileSync ( fileName ) . toString ( ) ) ;
175
+ } else {
176
+ this . makeDirectory ( fileName ) ;
177
+ let revisionUrl = 'https://www.googleapis.com/storage/v1/b/' +
178
+ 'chromium-browser-snapshots/o?delimiter=/&prefix=' ;
179
+ let os = '' ;
180
+ if ( this . osType === 'Windows_NT' ) {
181
+ os = 'Win' ;
182
+ if ( this . osArch === 'x64' ) {
183
+ os = 'Win_x64' ;
184
+ }
185
+ } else if ( this . osType === 'Linux' ) {
186
+ os = 'Linux' ;
187
+ if ( this . osArch === 'x64' ) {
188
+ os = 'Linux_x64' ;
189
+ }
190
+ } else {
191
+ os = 'Mac' ;
192
+ }
193
+ revisionUrl += os + '/' ;
194
+ let chromiumBasePosition : number = downloadJson [ 'chromium_base_position' ] ;
195
+
196
+ const httpOptions = { fileName, ignoreSSL : this . ignoreSSL ,
197
+ proxy : this . proxy } ;
198
+ while ( chromiumBasePosition > 0 ) {
199
+ const revisionBasePositionUrl =
200
+ `${ revisionUrl } ${ chromiumBasePosition } /` ;
201
+ const body = await requestBody ( revisionBasePositionUrl , httpOptions ) ;
202
+ const jsonBody = JSON . parse ( body ) ;
203
+ if ( jsonBody [ 'items' ] ) {
204
+ fs . writeFileSync ( fileName , JSON . stringify ( jsonBody , null , 2 ) ) ;
205
+ return jsonBody ;
206
+ } else {
207
+ chromiumBasePosition -- ;
208
+ }
209
+ }
210
+ return null ;
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Step 4: Get the download url for the chromium zip. Unzipping the zip file
216
+ * directory. The folders and binaries uncompressed are different for each OS.
217
+ * The following is examples of each OS:
218
+ *
219
+ * downloads/
220
+ * |- chrome-linux/chrome
221
+ * |- chrome-mac/Chromium.app
222
+ * |- chrome-win/chrome.exe
223
+ *
224
+ * @param storageObject The download-storage-json object
225
+ * @param majorVersion The major version, this must be a whole number.
226
+ */
227
+ async downloadUrl ( storageObject : JsonObject , majorVersion : string
228
+ ) : Promise < void > {
229
+ const fileName = path . resolve ( this . outDir ,
230
+ this . compressedBinaryFileName . replace ( '.zip' , `-${ majorVersion } .zip` ) ) ;
231
+ if ( isExpired ( fileName ) ) {
232
+ const httpOptions = { fileName, ignoreSSL : this . ignoreSSL ,
233
+ proxy : this . proxy } ;
234
+ for ( let item of storageObject [ 'items' ] as JsonObject [ ] ) {
235
+ const name : string = item [ 'name' ] ;
236
+ if ( name . indexOf ( 'chrome' ) >= 0 ) {
237
+ const downloadUrl = item [ 'mediaLink' ] ;
238
+ await requestBinary ( downloadUrl , httpOptions ) ;
239
+ break ;
240
+ }
241
+ }
242
+ }
243
+ unzipFile ( fileName , this . outDir ) ;
244
+ }
245
+
246
+ async updateBinary ( majorVersion ?: string ) : Promise < void > {
247
+ const allJson = await this . downloadAllJson ( ) ;
248
+ const downloadVersionJson = await this . downloadVersionJson (
249
+ allJson , majorVersion ) ;
250
+ const storageObject = await this . downloadStorageObject (
251
+ downloadVersionJson , majorVersion ) ;
252
+ await this . downloadUrl ( storageObject , majorVersion ) ;
253
+ }
254
+
255
+ getBinaryPath ( version ?: string ) : string | null {
256
+ try {
257
+ const configFilePath = path . resolve ( this . outDir , this . configFileName ) ;
258
+ return getBinaryPathFromConfig ( configFilePath , version ) ;
259
+ } catch ( _ ) {
260
+ return null ;
261
+ }
262
+ }
263
+
264
+ getStatus ( ) : string | null {
265
+ return '' ;
266
+ }
267
+
268
+ cleanFiles ( ) : string {
269
+ return removeFiles ( this . outDir , [ / c h r o m i u m .* / g] ) ;
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Helps translate the os type and arch to the download name associated
275
+ * with composing the download link.
276
+ * @param ostype The operating stystem type.
277
+ * @param osarch The chip architecture.
278
+ * @returns The download name associated with composing the download link.
279
+ */
280
+ export function osHelper ( ostype : string , osarch : string ) : string {
281
+ if ( ostype === 'Darwin' ) {
282
+ return 'Mac' ;
283
+ } else if ( ostype === 'Windows_NT' ) {
284
+ if ( osarch === 'x64' ) {
285
+ return 'Win_x64' ;
286
+ } else if ( osarch === 'x32' ) {
287
+ return 'Win' ;
288
+ }
289
+ } else if ( ostype === 'Linux' ) {
290
+ if ( osarch === 'x64' ) {
291
+ return 'Linux_64' ;
292
+ }
293
+ }
294
+ return null ;
295
+ }
0 commit comments