豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit 71e8eb8

Browse files
committed
refactor: rework browser update script to properly deal with artifact extensions
Reworks the browser update script to properly deal with artifact extensions, not incorrectly treating every file as `zip`, preventing Bazel's extraction detection from working.
1 parent b65765b commit 71e8eb8

File tree

7 files changed

+130
-51
lines changed

7 files changed

+130
-51
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {Browser} from './browser';
10+
import * as path from 'path';
11+
12+
/** Type describing possible artifact types for browser downloads. */
13+
export type ArtifactType = 'driver-bin' | 'browser-bin';
14+
15+
/** Set of known artifact extensions, including chained extensions for gzipped files. */
16+
const KNOWN_EXTENSIONS = new Set(['zip', 'tar.gz', 'tar.bz2', 'dmg']);
17+
18+
/** Class describing an artifact for a browser. */
19+
export class BrowserArtifact {
20+
/** Extension of the artifact, derived from the download URL. */
21+
extension = getArtifactExtension(this.downloadUrl);
22+
23+
constructor(
24+
/** Instance of the browser this artifact exists for. */
25+
public browser: Browser<unknown>,
26+
/** Type of the artifact. */
27+
public type: ArtifactType,
28+
/** URL for downloading the artifact. */
29+
public downloadUrl: string,
30+
) {}
31+
}
32+
33+
/**
34+
* Gets the extension of a given artifact file, excluding the dot/period.
35+
*
36+
* Since artifact download URLs can use chained extensions as for
37+
* example with `.tar.gz`, we will need to keep track of known extensions
38+
* and start looking with the first dot/period we discover.
39+
*/
40+
function getArtifactExtension(filePath: string) {
41+
let tmpPath: string = filePath;
42+
let extension: string = '';
43+
let currentPart: string = '';
44+
45+
// Iterate from the end of the path, finding the largest possible
46+
// extension substring, accounting for cases like `a/b.tmp/file.tar.gz`.
47+
while ((currentPart = path.extname(tmpPath)) !== '') {
48+
extension = currentPart + extension;
49+
tmpPath = path.basename(tmpPath, currentPart);
50+
}
51+
52+
// Strip off the leading period/dot from the extension.
53+
// If there is no extension, this string would remain empty.
54+
extension = extension.substring(1);
55+
56+
if (KNOWN_EXTENSIONS.has(extension)) {
57+
return extension;
58+
}
59+
60+
throw new Error(`Unable to find known extension for file path: ${filePath}`);
61+
}

bazel/browsers/update-script/browser.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {BrowserArtifact, ArtifactType} from './browser-artifact';
910
import {Platform} from './platform';
1011

11-
/** Type describing possible archive types for browser downloads. */
12-
export type ArchiveType = 'driver-bin' | 'browser-bin';
13-
1412
/** Interface describing a browser. */
1513
export interface Browser<T> {
1614
name: string;
1715
revision: T;
1816
supports(platform: Platform): boolean;
19-
getDownloadUrl(platform: Platform, type: ArchiveType): string;
17+
getArtifact(platform: Platform, type: ArtifactType): BrowserArtifact;
2018
}

bazel/browsers/update-script/chromium.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Browser, ArchiveType} from './browser';
9+
import {Browser} from './browser';
10+
import {ArtifactType, BrowserArtifact} from './browser-artifact';
1011
import {Platform} from './platform';
1112

1213
const cloudStorageArchiveUrl =
@@ -59,13 +60,16 @@ export class Chromium implements Browser<number> {
5960
return supportedPlatforms.has(platform);
6061
}
6162

62-
getDownloadUrl(platform: Platform, type: ArchiveType): string {
63-
return Chromium.getDownloadArchiveUrl(this.revision, platform, type);
63+
getArtifact(platform: Platform, type: ArtifactType): BrowserArtifact {
64+
return new BrowserArtifact(this, type, this.getDownloadUrl(platform, type));
6465
}
6566

66-
static getDownloadArchiveUrl(revision: number, platform: Platform, archive: ArchiveType): string {
67-
const archiveMap =
68-
archive === 'driver-bin' ? PlatformDriverArchiveMap : PlatformBrowserArchiveMap;
67+
getDownloadUrl(platform: Platform, type: ArtifactType): string {
68+
return Chromium.getDownloadArtifactUrl(this.revision, platform, type);
69+
}
70+
71+
static getDownloadArtifactUrl(revision: number, platform: Platform, type: ArtifactType): string {
72+
const archiveMap = type === 'driver-bin' ? PlatformDriverArchiveMap : PlatformBrowserArchiveMap;
6973
return cloudStorageArchiveUrl
7074
.replace('{platform}', PlatformSnapshotNameMap[platform])
7175
.replace('{revision}', `${revision}`)

bazel/browsers/update-script/find-revision-chromium.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import {createHash} from 'crypto';
2929
import fetch from 'node-fetch';
3030
import {Spinner} from '../../../ng-dev/utils/spinner';
31-
import {ArchiveType} from './browser';
31+
import {ArtifactType} from './browser-artifact';
3232
import {Chromium} from './chromium';
3333
import {Platform} from './platform';
3434

@@ -145,7 +145,7 @@ async function isRevisionAvailableForPlatform(
145145
platform: Platform,
146146
): Promise<boolean> {
147147
// Look for the `driver` archive as this is smaller and faster to check.
148-
const response = await fetch(Chromium.getDownloadArchiveUrl(revision, platform, 'driver-bin'));
148+
const response = await fetch(Chromium.getDownloadArtifactUrl(revision, platform, 'driver-bin'));
149149
return response.ok && response.status === 200;
150150
}
151151

@@ -179,9 +179,9 @@ async function getHeadChromiumRevision(): Promise<number> {
179179
async function getSha256ChecksumForPlatform(
180180
browser: Chromium,
181181
platform: Platform,
182-
archive: ArchiveType,
182+
artifactType: ArtifactType,
183183
): Promise<string> {
184-
const response = await fetch(browser.getDownloadUrl(platform, archive));
184+
const response = await fetch(browser.getDownloadUrl(platform, artifactType));
185185
const binaryContent = await response.buffer();
186186
return createHash('sha256').update(binaryContent).digest('hex');
187187
}

bazel/browsers/update-script/firefox.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Browser, ArchiveType} from './browser';
9+
import {ArtifactType, BrowserArtifact} from './browser-artifact';
10+
import {Browser} from './browser';
1011
import {Platform} from './platform';
1112

1213
const downloadLinuxUrls = {
@@ -32,7 +33,7 @@ const downloadMacOsArm64Urls = {
3233

3334
/** Class providing necessary information for the firefox browser. */
3435
export class Firefox implements Browser<string> {
35-
name = 'chromium';
36+
name = 'firefox';
3637

3738
constructor(public revision: string, public driverVersion: string) {}
3839

@@ -44,14 +45,15 @@ export class Firefox implements Browser<string> {
4445
);
4546
}
4647

47-
getDownloadUrl(platform: Platform, archiveType: ArchiveType): string {
48+
getArtifact(platform: Platform, archiveType: ArtifactType): BrowserArtifact {
4849
const urlSet = this._getUrlSetForPlatform(platform);
4950
const baseUrl = urlSet[archiveType];
51+
const downloadUrl = baseUrl.replace(/\{version}/g, this.revision);
5052

51-
return baseUrl.replace(/\{version}/g, this.revision);
53+
return new BrowserArtifact(this, archiveType, downloadUrl);
5254
}
5355

54-
private _getUrlSetForPlatform(platform: Platform): Record<ArchiveType, string> {
56+
private _getUrlSetForPlatform(platform: Platform): Record<ArtifactType, string> {
5557
switch (platform) {
5658
case Platform.LINUX_X64:
5759
return downloadLinuxUrls;
@@ -60,7 +62,7 @@ export class Firefox implements Browser<string> {
6062
case Platform.MAC_ARM64:
6163
return downloadMacOsArm64Urls;
6264
default:
63-
throw Error('Unexpected platform without Firefox support.');
65+
throw Error(`Unexpected platform "${platform}" without Firefox support.`);
6466
}
6567
}
6668
}

bazel/browsers/update-script/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,16 @@ async function main() {
3535
(args) => uploadBrowserArtifactsToMirror(new Storage(), new Chromium(args.revision)),
3636
)
3737
.command(
38-
'firefox <version> <driver-version>',
38+
'firefox <browser-version> <driver-version>',
3939
'Push Firefox artifacts',
4040
(args) =>
4141
args
42-
.positional('version', {type: 'string', demandOption: true})
42+
.positional('browserVersion', {type: 'string', demandOption: true})
4343
.positional('driverVersion', {type: 'string', demandOption: true}),
4444
(args) =>
4545
uploadBrowserArtifactsToMirror(
4646
new Storage(),
47-
new Firefox(args.version, args.driverVersion),
47+
new Firefox(args.browserVersion, args.driverVersion),
4848
),
4949
),
5050
)

bazel/browsers/update-script/upload-mirror.ts

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
*/
88

99
import {Bucket, Storage, File} from '@google-cloud/storage';
10-
import {Browser, ArchiveType} from './browser';
10+
import {Browser} from './browser';
1111
import {Platform} from './platform';
1212
import {createTmpDir, downloadFileThroughStreaming} from './utils';
1313

1414
import * as path from 'path';
15+
import {ArtifactType, BrowserArtifact} from './browser-artifact';
1516

1617
/** Name of the Google Cloud Storage bucket for the browser mirror. */
1718
const MIRROR_BUCKET_NAME = 'dev-infra-mirror';
@@ -21,17 +22,25 @@ export function getMirrorDirectoryForBrowserInstance<T>(browser: Browser<T>): st
2122
return `${browser.name}/${browser.revision}`;
2223
}
2324

24-
/** Uploads a browser platform artifact to the browser mirror. */
25+
/** Gets the destination file path for a given browser artifact. */
26+
export function getDestinationFilePath(artifact: BrowserArtifact, platform: Platform): string {
27+
const versionMirrorDir = getMirrorDirectoryForBrowserInstance(artifact.browser);
28+
return `${versionMirrorDir}/${platform}/${artifact.type}.${artifact.extension}`;
29+
}
30+
31+
/**
32+
* Uploads a browser platform artifact to the browser mirror.
33+
*
34+
* @throws {Error} An error if the artifact already exists in the mirror.
35+
*/
2536
export async function uploadArtifactToMirror(
2637
bucket: Bucket,
27-
browser: Browser<unknown>,
38+
artifact: BrowserArtifact,
2839
platform: Platform,
29-
type: ArchiveType,
3040
sourceFile: string,
3141
): Promise<File> {
32-
const versionMirrorDir = getMirrorDirectoryForBrowserInstance(browser);
3342
const [file] = await bucket.upload(sourceFile, {
34-
destination: `${versionMirrorDir}/${platform}/${type}.zip`,
43+
destination: getDestinationFilePath(artifact, platform),
3544
public: true,
3645
});
3746

@@ -45,62 +54,67 @@ export async function uploadArtifactToMirror(
4554
*/
4655
export async function uploadBrowserArtifactsToMirror(storage: Storage, browser: Browser<unknown>) {
4756
const bucket = storage.bucket(MIRROR_BUCKET_NAME);
48-
const versionMirrorDir = getMirrorDirectoryForBrowserInstance(browser);
49-
50-
// Note that the `File#exists` method returns the following: `[boolean]`.
51-
// https://googleapis.dev/nodejs/storage/latest/global.html#FileExistsResponse.
52-
if ((await bucket.file(versionMirrorDir).exists())[0]) {
53-
throw Error('Revision is already in the mirror. Remove the artifacts if you want to retry.');
54-
}
55-
5657
const tmpDir = await createTmpDir({template: `${browser.name}-${browser.revision}-XXXXXX`});
5758
const downloadTasks: Promise<{
5859
platform: Platform;
5960
filePath: string;
60-
type: ArchiveType;
61+
artifact: BrowserArtifact;
6162
}>[] = [];
6263

6364
for (const platform of Object.values(Platform)) {
64-
const driverArchiveUrl = browser.getDownloadUrl(platform, 'driver-bin');
65-
const browserArchiveUrl = browser.getDownloadUrl(platform, 'browser-bin');
66-
const driverTmpPath = path.join(tmpDir, 'driver.bin');
67-
const browserTmpPath = path.join(tmpDir, 'browser.bin');
65+
if (!browser.supports(platform)) {
66+
continue;
67+
}
68+
69+
const driverArtifact = browser.getArtifact(platform, 'driver-bin');
70+
const browserArtifact = browser.getArtifact(platform, 'browser-bin');
71+
const driverTmpPath = path.join(tmpDir, `${platform}-driver.${driverArtifact.extension}`);
72+
const browserTmpPath = path.join(tmpDir, `${platform}-browser.${browserArtifact.extension}`);
73+
74+
// We use the driver artifact (which is usually much smaller) to run a quick
75+
// sanity check upstream to ensure that the artifact does not yet exist upstream.
76+
const testDestinationFile = getDestinationFilePath(driverArtifact, platform);
77+
// Note that we cannot check directly for the directory to exist since GCP does
78+
// not support this. Hence we need to run this check for the actual files instead.
79+
if ((await bucket.file(testDestinationFile).exists())[0]) {
80+
throw Error('Revision is already in the mirror. Remove the artifacts if you want to retry.');
81+
}
6882

6983
downloadTasks.push(
70-
downloadFileThroughStreaming(browserArchiveUrl, browserTmpPath)
84+
downloadFileThroughStreaming(browserArtifact.downloadUrl, browserTmpPath)
7185
.then(() => console.info(`✅ Downloaded: ${browser.name} - ${platform} browser.`))
7286
.then(() => ({
7387
platform,
7488
filePath: browserTmpPath,
75-
type: 'browser-bin',
89+
artifact: browserArtifact,
7690
})),
7791
);
7892

7993
downloadTasks.push(
80-
downloadFileThroughStreaming(driverArchiveUrl, driverTmpPath)
94+
downloadFileThroughStreaming(driverArtifact.downloadUrl, driverTmpPath)
8195
.then(() => console.info(`✅ Downloaded: ${browser.name} - ${platform} driver.`))
8296
.then(() => ({
8397
platform,
8498
filePath: driverTmpPath,
85-
type: 'driver-bin',
99+
artifact: driverArtifact,
86100
})),
87101
);
88102
}
89103

90104
const tasks = await Promise.all(downloadTasks);
91-
const uploadTasks: Promise<{platform: Platform; type: ArchiveType; file: File}>[] = [];
105+
const uploadTasks: Promise<{platform: Platform; artifact: BrowserArtifact; file: File}>[] = [];
92106

93107
console.info();
94108
console.info('Fetched all browser artifacts. Now uploading to mirror.');
95109
console.info();
96110

97-
for (const {platform, filePath, type} of tasks) {
111+
for (const {platform, filePath, artifact} of tasks) {
98112
uploadTasks.push(
99-
uploadArtifactToMirror(bucket, browser, platform, type, filePath).then((file) => {
100-
console.log(`✅ Uploaded: ${platform} ${type}`);
113+
uploadArtifactToMirror(bucket, artifact, platform, filePath).then((file) => {
114+
console.log(`✅ Uploaded: ${platform} ${artifact.type}`);
101115
console.log(` -> ${file.publicUrl()}`);
102116

103-
return {platform, file, type};
117+
return {platform, file, artifact};
104118
}),
105119
);
106120
}

0 commit comments

Comments
 (0)