Skip to content

Commit d247a7f

Browse files
authored
fix: support .tar.xz archives for Firefox 135+ (#13391)
1 parent 75f76cc commit d247a7f

File tree

4 files changed

+77
-3
lines changed

4 files changed

+77
-3
lines changed

docs/guides/system-requirements.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@
1818
- Firefox browser system requirements:
1919

2020
- https://www.mozilla.org/en-US/firefox/system-requirements/
21+
- The `xz` utility is required to unpack Firefox versions 135+ for Linux.

packages/browsers/src/browser-data/firefox.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,15 @@ import {getJSON} from '../httpUtil.js';
1111

1212
import {BrowserPlatform, type ProfileOptions} from './types.js';
1313

14+
function getFormat(buildId: string): string {
15+
const majorVersion = Number(buildId.split('.').shift()!);
16+
return majorVersion >= 135 ? 'xz' : 'bz2';
17+
}
18+
1419
function archiveNightly(platform: BrowserPlatform, buildId: string): string {
1520
switch (platform) {
1621
case BrowserPlatform.LINUX:
17-
return `firefox-${buildId}.en-US.${platform}-x86_64.tar.bz2`;
22+
return `firefox-${buildId}.en-US.${platform}-x86_64.tar.${getFormat(buildId)}`;
1823
case BrowserPlatform.MAC_ARM:
1924
case BrowserPlatform.MAC:
2025
return `firefox-${buildId}.en-US.mac.dmg`;
@@ -27,7 +32,7 @@ function archiveNightly(platform: BrowserPlatform, buildId: string): string {
2732
function archive(platform: BrowserPlatform, buildId: string): string {
2833
switch (platform) {
2934
case BrowserPlatform.LINUX:
30-
return `firefox-${buildId}.tar.bz2`;
35+
return `firefox-${buildId}.tar.${getFormat(buildId)}`;
3136
case BrowserPlatform.MAC_ARM:
3237
case BrowserPlatform.MAC:
3338
return `Firefox ${buildId}.dmg`;

packages/browsers/src/fileUtil.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import {spawnSync} from 'child_process';
7+
import {spawnSync, spawn} from 'child_process';
88
import {createReadStream} from 'fs';
99
import {mkdir, readdir} from 'fs/promises';
1010
import * as path from 'path';
11+
import {Stream} from 'stream';
1112

1213
import extractZip from 'extract-zip';
1314
import tar from 'tar-fs';
@@ -39,6 +40,8 @@ export async function unpackArchive(
3940
`Failed to extract ${archivePath} to ${folderPath}: ${result.output}`,
4041
);
4142
}
43+
} else if (archivePath.endsWith('.tar.xz')) {
44+
await extractTarXz(archivePath, folderPath);
4245
} else {
4346
throw new Error(`Unsupported archive format: ${archivePath}`);
4447
}
@@ -57,6 +60,63 @@ function extractTar(tarPath: string, folderPath: string): Promise<void> {
5760
});
5861
}
5962

63+
/**
64+
* @internal
65+
*/
66+
function createXzStream() {
67+
const child = spawn('xz', ['-d']);
68+
const stream = new Stream.Transform({
69+
transform(chunk, encoding, callback) {
70+
if (!child.stdin.write(chunk, encoding)) {
71+
child.stdin.once('drain', callback);
72+
} else {
73+
callback();
74+
}
75+
},
76+
77+
flush(callback) {
78+
if (child.stdout.destroyed) {
79+
callback();
80+
} else {
81+
child.stdin.end();
82+
child.stdout.on('close', callback);
83+
}
84+
},
85+
});
86+
87+
child.stdin.on('error', e => {
88+
if ('code' in e && e.code === 'EPIPE') {
89+
// finished before reading the file finished (i.e. head)
90+
stream.emit('end');
91+
} else {
92+
stream.destroy(e);
93+
}
94+
});
95+
96+
child.stdout
97+
.on('data', data => {
98+
return stream.push(data);
99+
})
100+
.on('error', e => {
101+
return stream.destroy(e);
102+
});
103+
104+
return stream;
105+
}
106+
107+
/**
108+
* @internal
109+
*/
110+
function extractTarXz(tarPath: string, folderPath: string): Promise<void> {
111+
return new Promise((fulfill, reject) => {
112+
const tarStream = tar.extract(folderPath);
113+
tarStream.on('error', reject);
114+
tarStream.on('finish', fulfill);
115+
const readStream = createReadStream(tarPath);
116+
readStream.pipe(createXzStream()).pipe(tarStream);
117+
});
118+
}
119+
60120
/**
61121
* @internal
62122
*/

packages/browsers/test/src/firefox/firefox-data.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ describe('Firefox', () => {
2323
resolveDownloadUrl(BrowserPlatform.LINUX, '111.0a1'),
2424
'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-111.0a1.en-US.linux-x86_64.tar.bz2',
2525
);
26+
assert.strictEqual(
27+
resolveDownloadUrl(BrowserPlatform.LINUX, '135.0a1'),
28+
'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-135.0a1.en-US.linux-x86_64.tar.xz',
29+
);
30+
assert.strictEqual(
31+
resolveDownloadUrl(BrowserPlatform.LINUX, '136.0a1'),
32+
'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-136.0a1.en-US.linux-x86_64.tar.xz',
33+
);
2634
assert.strictEqual(
2735
resolveDownloadUrl(BrowserPlatform.MAC, '111.0a1'),
2836
'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-111.0a1.en-US.mac.dmg',

0 commit comments

Comments
 (0)