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