get-firefox
Version:
Download the latest Firefox from the command line
194 lines (189 loc) • 6.2 kB
JavaScript
/* eslint-disable require-atomic-updates */
import fs from "node:fs/promises";
import {
getDefaultSystem,
getContainer,
downloadFirefox,
check,
extract,
PLATFORMS,
} from '../index.js';
import meow from "meow";
import streamToPromise from "stream-to-promise";
import chalk from "chalk";
import Listr from "listr";
import isCI from "is-ci";
const cli = meow(`
Usage
$ get-firefox
Options
-p, --platform <platform> Platform of the Firefox version
-a, --architecture <arch> Platform architecture
-t, --target Target file name or folder when extracting; defaults to the remote file name
-c, --check Verify file checksums
-e, --extract Extract archive after download; does not work for dmg and exe files
-l, --list List all platforms and architectures
-b, --branch <branch> Release branch to fetch
-h, --help This output
-v, --version Version of the tool
--verbose Don't animate output and show detailed progress log. Enabled by default in CI environments
Examples
$ get-firefox --platform android --target fenix.apk --architecture arm
`,
{
importMeta: import.meta,
flags: {
architecture: {
type: "string",
shortFlag: "a",
},
branch: {
type: "string",
shortFlag: "b",
default: "nightly",
},
check: {
type: "boolean",
shortFlag: "c",
},
extract: {
type: "boolean",
shortFlag: "e",
},
help: {
type: "boolean",
shortFlag: "h",
},
list: {
type: "boolean",
shortFlag: "l",
},
platform: {
type: "string",
shortFlag: "p",
default: getDefaultSystem(),
},
target: {
type: "string",
shortFlag: "t",
},
version: {
type: "boolean",
shortFlag: "v",
},
verbose: {
type: "boolean",
default: isCI,
},
},
});
if(cli.flags.list) {
const defaultPlatform = getDefaultSystem();
let branch,
title,
multipleArches = false;
for(const [
p,
plat,
] of Object.entries(PLATFORMS)) {
title = p;
if(p == defaultPlatform) {
title += chalk.gray(" (default)");
}
process.stdout.write(`${chalk.underline.bold(title)}\n`);
for(const b in plat.branches) {
branch = plat.branches[b];
title = chalk.bold(b);
if(b == plat.defaultBranch) {
title = chalk.yellow(title);
title += chalk.gray(" (default)");
}
process.stdout.write(` ${title}\n`);
multipleArches = Object.keys(branch.arches).length;
for(const a in branch.arches) {
title = a;
if(a == branch.defaultArch && multipleArches) {
title += chalk.gray(" (default)");
}
process.stdout.write(` - ${title}\n`);
}
}
process.stdout.write("\n");
}
}
else {
new Listr([
{
title: "Search latest Firefox",
task: async (context) => {
context.container = getContainer(cli.flags.branch, cli.flags.platform, cli.flags.architecture);
const promises = [
context.container.getFileName(),
context.container.getFileURL(),
],
[
fileName,
fileURL,
] = await Promise.all(promises);
context.url = fileURL;
context.name = fileName;
},
},
{
title: "Get checksum",
enabled: () => cli.flags.check,
task: async (context, task) => {
try {
context.checksums = await context.container.getChecksums();
}
catch(error) {
if(error.message === "No checksums available") {
task.output = error.message;
context.noChecksums = true;
}
else {
throw error;
}
}
},
},
{
title: "Download",
task: async (context, task) => {
task.output = `Downloading from ${chalk.blue(context.url)}`;
context.stream = await downloadFirefox(context.container);
context.buffer = await streamToPromise(context.stream);
},
},
{
title: "Verify checksum",
enabled: () => cli.flags.check,
skip: (context) => context.noChecksums || !context.checksums,
task: async (context) => {
context.stream = check(context.buffer, context.name, context.checksums);
context.buffer = await streamToPromise(context.stream);
},
},
{
title: "Extract",
enabled: () => cli.flags.extract,
task: (context, task) => {
task.output = `Extracting the archive to ${chalk.blue(cli.flags.target || "./")}`;
return extract(context.buffer, cli.flags.target);
},
},
{
title: "Write file to disk",
enabled: () => !cli.flags.extract,
task: (context, task) => {
const saveAs = cli.flags.target || context.name;
task.output = `Saving as ${chalk.blue(saveAs)}`;
return fs.writeFile(saveAs, context.buffer);
},
},
], {
renderer: cli.flags.verbose ? 'verbose' : 'default',
}).run();
}
/* eslint-enable require-atomic-updates */