-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18 from lowlighter/feat-progress-and-native-zip
feat: improve install progress and native unzip
- Loading branch information
Showing
5 changed files
with
233 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,11 @@ | ||
import { ensureDirSync } from "https://deno.land/[email protected]/fs/ensure_dir.ts"; | ||
import { resolve } from "https://deno.land/[email protected]/path/mod.ts"; | ||
import { ensureDir } from "https://deno.land/[email protected]/fs/ensure_dir.ts"; | ||
import { dirname } from "https://deno.land/[email protected]/path/dirname.ts"; | ||
import { join } from "https://deno.land/[email protected]/path/join.ts"; | ||
import { ZipReader } from "https://deno.land/x/[email protected]/index.js"; | ||
import ProgressBar from "https://deno.land/x/[email protected]/mod.ts"; | ||
import { exists } from "https://deno.land/[email protected]/fs/exists.ts"; | ||
|
||
export const SUPPORTED_VERSIONS = { | ||
chrome: "118.0.5943.0", | ||
|
@@ -41,28 +47,73 @@ function getCache(): Record<string, string> { | |
} | ||
} | ||
|
||
/** | ||
* Clean cache | ||
*/ | ||
export async function cleanCache() { | ||
try { | ||
if (await exists(BASE_PATH)) { | ||
await Deno.remove(BASE_PATH, { recursive: true }); | ||
} | ||
} catch (error) { | ||
console.warn(`Failed to clean cache: ${error}`); | ||
} | ||
} | ||
|
||
async function isQuietInstall() { | ||
// Hide-progress in CI environment | ||
const ci = await Deno.permissions.query({ | ||
name: "env", | ||
variable: "CI", | ||
}); | ||
if ((ci.state === "granted") && (`${Deno.env.get("CI") ?? ""}`.length)) { | ||
return true; | ||
} | ||
// Hide-progress if asked by user | ||
const quiet = await Deno.permissions.query({ | ||
name: "env", | ||
variable: "ASTRAL_QUIET_INSTALL", | ||
}); | ||
if (quiet.state === "granted") { | ||
const value = `${Deno.env.get("ASTRAL_QUIET_INSTALL") ?? ""}`; | ||
return value.length || | ||
!/^(0|[Nn]o?|NO|[Oo]ff|OFF|[Ff]alse|FALSE)$/.test(value); | ||
} | ||
} | ||
|
||
async function decompressArchive(source: string, destination: string) { | ||
const unzipCommand = new Deno.Command( | ||
Deno.build.os === "windows" ? "PowerShell" : "unzip", | ||
{ | ||
args: Deno.build.os === "windows" | ||
? [ | ||
"Expand-Archive", | ||
"-Path", | ||
`"${source}"`, | ||
"-DestinationPath", | ||
`"${destination}"`, | ||
"-Force", | ||
] | ||
: [ | ||
"-o", | ||
source, | ||
"-d", | ||
destination, | ||
], | ||
}, | ||
); | ||
await unzipCommand.output(); | ||
const quiet = await isQuietInstall(); | ||
const archive = await Deno.open(source); | ||
const zip = new ZipReader(archive); | ||
const entries = await zip.getEntries(); | ||
const bar = !quiet | ||
? new ProgressBar({ | ||
title: `Inflating ${destination}`, | ||
total: entries.length, | ||
clear: true, | ||
display: ":title :bar :percent", | ||
}) | ||
: null; | ||
let progress = 0; | ||
for (const entry of entries) { | ||
if ((!entry.directory) && (entry.getData)) { | ||
const path = join(destination, entry.filename); | ||
await ensureDir(dirname(path)); | ||
const file = await Deno.open(path, { | ||
create: true, | ||
truncate: true, | ||
write: true, | ||
mode: 0o755, | ||
}); | ||
await entry.getData(file, { checkSignature: true, useWebWorkers: false }); | ||
} | ||
progress++; | ||
bar?.render(progress); | ||
} | ||
await zip.close(); | ||
if (!quiet) { | ||
console.log(`Browser saved to ${destination}`); | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -75,6 +126,7 @@ export async function getBinary( | |
const VERSION = SUPPORTED_VERSIONS[browser]; | ||
|
||
const config = getCache(); | ||
const quiet = await isQuietInstall(); | ||
|
||
// If the config doesn't have the revision, download it and return that | ||
if (!config[VERSION]) { | ||
|
@@ -98,17 +150,43 @@ export async function getBinary( | |
); | ||
})[0]; | ||
|
||
console.log( | ||
"Downloading browser... (this may take a while depending on your internet connection)", | ||
); | ||
const req = await fetch(download.url); | ||
if (!req.body) { | ||
throw new Error( | ||
"Download failed, please check your internet connection and try again", | ||
); | ||
} | ||
await Deno.writeFile(resolve(BASE_PATH, `raw_${VERSION}.zip`), req.body); | ||
console.log(`Download complete (${browser} version ${VERSION})`); | ||
if (quiet) { | ||
await Deno.writeFile(resolve(BASE_PATH, `raw_${VERSION}.zip`), req.body); | ||
} else { | ||
const reader = req.body.getReader(); | ||
const archive = await Deno.open( | ||
resolve(BASE_PATH, `raw_${VERSION}.zip`), | ||
{ | ||
write: true, | ||
truncate: true, | ||
create: true, | ||
}, | ||
); | ||
const bar = new ProgressBar({ | ||
title: `Downloading ${browser} ${VERSION}`, | ||
total: Number(req.headers.get("Content-Length") ?? 0), | ||
clear: true, | ||
display: ":title :bar :percent", | ||
}); | ||
let downloaded = 0; | ||
do { | ||
const { done, value } = await reader.read(); | ||
if (done) { | ||
break; | ||
} | ||
await Deno.write(archive.rid, value); | ||
downloaded += value.length; | ||
bar.render(downloaded); | ||
} while (true); | ||
Deno.close(archive.rid); | ||
console.log(`Download complete (${browser} version ${VERSION})`); | ||
} | ||
await decompressArchive( | ||
resolve(BASE_PATH, `raw_${VERSION}.zip`), | ||
resolve(BASE_PATH, VERSION), | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { assertMatch } from "https://deno.land/[email protected]/assert/assert_match.ts"; | ||
import { cleanCache, getBinary, launch } from "../mod.ts"; | ||
import { assert } from "https://deno.land/[email protected]/assert/assert.ts"; | ||
|
||
Deno.test("Test download", async () => { | ||
// Download browser | ||
await cleanCache(); | ||
const path = await getBinary("chrome"); | ||
|
||
// Ensure browser is executable | ||
// Note: it seems that on Windows the --version flag does not exists and spawn a | ||
// browser instance instead. The next test ensure that everything is working | ||
// properly anyways | ||
if (Deno.build.os !== "windows") { | ||
const command = new Deno.Command(path, { | ||
args: [ | ||
"--version", | ||
], | ||
}); | ||
const { success, stdout } = await command.output(); | ||
assert(success); | ||
assertMatch(new TextDecoder().decode(stdout), /Google Chrome/i); | ||
} | ||
|
||
// Ensure browser is capable of loading pages | ||
const browser = await launch(); | ||
const page = await browser.newPage("http://example.com"); | ||
await page.waitForSelector("h1"); | ||
await browser.close(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { cleanCache, getBinary } from "../../mod.ts"; | ||
import { assert } from "https://deno.land/[email protected]/assert/assert.ts"; | ||
|
||
Deno.bench({ | ||
name: "Download progress", | ||
group: "Download browser", | ||
async fn(t) { | ||
// Download browser | ||
await cleanCache(); | ||
t.start(); | ||
assert(await getBinary("chrome")); | ||
t.end(); | ||
}, | ||
}); | ||
|
||
Deno.bench({ | ||
name: "Download quiet", | ||
group: "Download browser", | ||
async fn(t) { | ||
// Download browser | ||
await cleanCache(); | ||
t.start(); | ||
Deno.env.set("ASTRAL_QUIET_INSTALL", "true"); | ||
assert(await getBinary("chrome")); | ||
t.end(); | ||
}, | ||
}); |