diff --git a/deno.jsonc b/deno.jsonc index 2dde3af..ed74976 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -3,6 +3,7 @@ // The task to automatically generate `./src/celestial.ts` "bind": "deno run -A ./bindings/_tools/generate/mod.ts && deno fmt", "test": "deno test -A --trace-ops", + "bench": "deno bench -A", "www": "cd docs && pyro dev" }, "compilerOptions": { diff --git a/deno.lock b/deno.lock index deb7062..dd37413 100644 --- a/deno.lock +++ b/deno.lock @@ -1,13 +1,47 @@ { - "version": "2", + "version": "3", + "redirects": { + "https://deno.land/x/zipjs/index.js": "https://deno.land/x/zipjs@v2.7.29/index.js" + }, "remote": { + "https://deno.land/std@0.162.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", + "https://deno.land/std@0.162.0/bytes/bytes_list.ts": "aba5e2369e77d426b10af1de0dcc4531acecec27f9b9056f4f7bfbf8ac147ab4", + "https://deno.land/std@0.162.0/bytes/equals.ts": "3c3558c3ae85526f84510aa2b48ab2ad7bdd899e2e0f5b7a8ffc85acb3a6043a", + "https://deno.land/std@0.162.0/bytes/mod.ts": "b2e342fd3669176a27a4e15061e9d588b89c1aaf5008ab71766e23669565d179", + "https://deno.land/std@0.162.0/fmt/colors.ts": "9e36a716611dcd2e4865adea9c4bec916b5c60caad4cdcdc630d4974e6bb8bd4", + "https://deno.land/std@0.162.0/io/buffer.ts": "fae02290f52301c4e0188670e730cd902f9307fb732d79c4aa14ebdc82497289", + "https://deno.land/std@0.162.0/streams/conversion.ts": "555c6c249f3acf85655f2d0af52d1cb3168e40b1c1fa26beefea501b333abe28", "https://deno.land/std@0.201.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", "https://deno.land/std@0.201.0/assert/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", "https://deno.land/std@0.201.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", "https://deno.land/std@0.201.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.201.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", + "https://deno.land/std@0.201.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", "https://deno.land/std@0.201.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", + "https://deno.land/std@0.201.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", + "https://deno.land/std@0.201.0/assert/assert_false.ts": "a9962749f4bf5844e3fa494257f1de73d69e4fe0e82c34d0099287552163a2dc", + "https://deno.land/std@0.201.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63", + "https://deno.land/std@0.201.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c", + "https://deno.land/std@0.201.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c", + "https://deno.land/std@0.201.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", + "https://deno.land/std@0.201.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4", + "https://deno.land/std@0.201.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848", + "https://deno.land/std@0.201.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", + "https://deno.land/std@0.201.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", + "https://deno.land/std@0.201.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", + "https://deno.land/std@0.201.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", + "https://deno.land/std@0.201.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", + "https://deno.land/std@0.201.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54", + "https://deno.land/std@0.201.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", + "https://deno.land/std@0.201.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265", + "https://deno.land/std@0.201.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", + "https://deno.land/std@0.201.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", "https://deno.land/std@0.201.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", "https://deno.land/std@0.201.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", + "https://deno.land/std@0.201.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", + "https://deno.land/std@0.201.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085", + "https://deno.land/std@0.201.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", + "https://deno.land/std@0.201.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", "https://deno.land/std@0.201.0/async/deadline.ts": "58f72a3cc0fcb731b2cc055ba046f4b5be3349ff6bf98f2e793c3b969354aab2", "https://deno.land/std@0.201.0/async/delay.ts": "a6142eb44cdd856b645086af2b811b1fcce08ec06bb7d50969e6a872ee9b8659", "https://deno.land/std@0.201.0/async/retry.ts": "296fb9c323e1325a69bee14ba947e7da7409a8dd9dd646d70cb51ea0d301f24e", @@ -51,6 +85,41 @@ "https://deno.land/std@0.201.0/path/to_file_url.ts": "00e6322373dd51ad109956b775e4e72e5f9fa68ce2c6b04e4af2a6eed3825d31", "https://deno.land/std@0.201.0/path/to_namespaced_path.ts": "1b1db3055c343ab389901adfbda34e82b7386bcd1c744d54f9c1496ee0fd0c3d", "https://deno.land/std@0.201.0/path/win32.ts": "8b3f80ef7a462511d5e8020ff490edcaa0a0d118f1b1e9da50e2916bdd73f9dd", - "https://deno.land/std@0.201.0/testing/snapshot.ts": "fd91f03c258c316bc9faf815846d85e80e0c82622b28ee44b57ec66dd91d3408" + "https://deno.land/std@0.201.0/testing/asserts.ts": "b4e4b1359393aeff09e853e27901a982c685cb630df30426ed75496961931946", + "https://deno.land/std@0.201.0/testing/snapshot.ts": "fd91f03c258c316bc9faf815846d85e80e0c82622b28ee44b57ec66dd91d3408", + "https://deno.land/std@0.203.0/fs/exists.ts": "cb59a853d84871d87acab0e7936a4dac11282957f8e195102c5a7acb42546bb8", + "https://deno.land/x/progress@v1.3.9/deps.ts": "83050e627263931d853ba28b7c15c80bf4be912bea7e0d3d13da2bc0aaf7889d", + "https://deno.land/x/progress@v1.3.9/mod.ts": "ca14ba3c56fc5991c4beee622faeb882e59db8f28d4f5e529ac17b7b07d55741", + "https://deno.land/x/progress@v1.3.9/multi.ts": "1de7edf67047ba0050edf63229970f2cbf9cd069342fcd29444a3800d4cfdcbc", + "https://deno.land/x/progress@v1.3.9/time.ts": "f5b302425cef076958c9352030f48a79bd7b54316b4057f99192884b5e140ea0", + "https://deno.land/x/zipjs@v2.7.29/index.js": "7c71926e0c9618e48a22d9dce701131704fd3148a1d2eefd5dba1d786c846a5f", + "https://deno.land/x/zipjs@v2.7.29/lib/core/codec-pool.js": "e5ab8ee3ec800ed751ef1c63a1bd8e50f162aa256a5f625d173d7a32e76e828c", + "https://deno.land/x/zipjs@v2.7.29/lib/core/codec-worker.js": "744b7e149df6f2d105afbcb9cce573df2fbf7bf1c2e14c3689220c2dedeabe65", + "https://deno.land/x/zipjs@v2.7.29/lib/core/configuration.js": "baa316a63df2f8239f9d52cd4863eaedaddd34ad887b7513588da75d19e84932", + "https://deno.land/x/zipjs@v2.7.29/lib/core/constants.js": "14fe1468b87cd0fe20c6f1fec916485f875d8592beba94c9241af4cbd12dd88f", + "https://deno.land/x/zipjs@v2.7.29/lib/core/io.js": "4c4e86ba187540be533003271f222183455897cd144cb542539e9480882c2dda", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/aes-crypto-stream.js": "63988c9f3ce1e043c80e6eb140ebb07bf2ab543ee9a85349651ab74b96aab2cf", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/codec-stream.js": "685f1120b94b6295dcd61b195d6202cd24a5344e4588dc52f42e8ac0f9dfe294", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/codecs/crc32.js": "dfdde666f72b4a5ffc8cf5b1451e0db578ce4bd90de20df2cff5bfd47758cb23", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/codecs/deflate.js": "08c1b24d1845528f6db296570d690ecbe23c6c01c6cb26b561e601e770281c3a", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/codecs/inflate.js": "55d00eed332cf2c4f61e2ee23133e3257768d0608572ee3f9641a2921c3a6f67", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/codecs/sjcl.js": "462289c5312f01bba8a757a7a0f3d8f349f471183cb4c49fb73d58bba18a5428", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/common-crypto.js": "4d462619848d94427fcd486fd94e5c0741af60e476df6720da8224b086eba47e", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/crc32-stream.js": "10e26bd18df0e1e89d61a62827a1a1c19f4e541636dd0eccbd85af3afabce289", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/stream-adapter.js": "9e7f3fe1601cc447943cd37b5adb6d74c6e9c404d002e707e8eace7bc048929c", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/zip-crypto-stream.js": "19305af1e8296e7fa6763f3391d0b8149a1e09c659e1d1ff32a484448b18243c", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/zip-entry-stream.js": "01d4dc0843e8c43d32454cbb15e4d1f9b7122ab288d7650129d010df54bc0b8e", + "https://deno.land/x/zipjs@v2.7.29/lib/core/util/cp437-decode.js": "d665ded184037ffe5d255be8f379f90416053e3d0d84fac95b28f4aeaab3d336", + "https://deno.land/x/zipjs@v2.7.29/lib/core/util/decode-text.js": "c04a098fa7c16470c48b6abd4eb4ac48af53547de65e7c8f39b78ae62330ad57", + "https://deno.land/x/zipjs@v2.7.29/lib/core/util/default-mime-type.js": "177ae00e1956d3d00cdefc40eb158cb591d3d24ede452c056d30f98d73d9cd73", + "https://deno.land/x/zipjs@v2.7.29/lib/core/util/encode-text.js": "c51a8947c15b7fe31b0036b69fd68817f54b30ce29502b5c9609d8b15e3b20d9", + "https://deno.land/x/zipjs@v2.7.29/lib/core/util/mime-type.js": "6c6dfa4daf98ef59cd65118073b74f327ceab2ef28140e38934b0d15eb2b5c29", + "https://deno.land/x/zipjs@v2.7.29/lib/core/util/stream-codec-shim.js": "1323016ec3c743942dc887215832badc7f2c1e8dbb37b71c94bf54276d2b281a", + "https://deno.land/x/zipjs@v2.7.29/lib/core/zip-entry.js": "d30a535cd1e75ef98094cd04120f178c103cdc4055d23ff747ffc6a154da8d2d", + "https://deno.land/x/zipjs@v2.7.29/lib/core/zip-fs-core.js": "737a92a0e27083eefa2b51811dd0b9b6ffc216b3949509fbc8833bfd2a78c071", + "https://deno.land/x/zipjs@v2.7.29/lib/core/zip-reader.js": "c918875362d7e46fc690cea0b3f81e50a0ec0b6e20f6ae35e5982de73ae45449", + "https://deno.land/x/zipjs@v2.7.29/lib/core/zip-writer.js": "b78c099828ec3134983c259adc4d6118fbfda7f033a7e95de8176a470e9a5a54", + "https://deno.land/x/zipjs@v2.7.29/lib/z-worker-inline.js": "a38bbe15eb011daee291b8243c3101b197aa91dcd786f1c04251bd1dda5c33e7", + "https://deno.land/x/zipjs@v2.7.29/lib/zip-fs.js": "a733360302f5fbec9cc01543cb9fcfe7bae3f35a50d0006626ce42fe8183b63f" } } diff --git a/src/cache.ts b/src/cache.ts index 43439ad..1b169e9 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -1,5 +1,11 @@ import { ensureDirSync } from "https://deno.land/std@0.201.0/fs/ensure_dir.ts"; import { resolve } from "https://deno.land/std@0.201.0/path/mod.ts"; +import { ensureDir } from "https://deno.land/std@0.201.0/fs/ensure_dir.ts"; +import { dirname } from "https://deno.land/std@0.201.0/path/dirname.ts"; +import { join } from "https://deno.land/std@0.201.0/path/join.ts"; +import { ZipReader } from "https://deno.land/x/zipjs@v2.7.29/index.js"; +import ProgressBar from "https://deno.land/x/progress@v1.3.9/mod.ts"; +import { exists } from "https://deno.land/std@0.203.0/fs/exists.ts"; export const SUPPORTED_VERSIONS = { chrome: "118.0.5943.0", @@ -41,28 +47,73 @@ function getCache(): Record { } } +/** + * 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), diff --git a/tests/_get_binary_test.ts b/tests/_get_binary_test.ts new file mode 100644 index 0000000..7fa5415 --- /dev/null +++ b/tests/_get_binary_test.ts @@ -0,0 +1,30 @@ +import { assertMatch } from "https://deno.land/std@0.201.0/assert/assert_match.ts"; +import { cleanCache, getBinary, launch } from "../mod.ts"; +import { assert } from "https://deno.land/std@0.201.0/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(); +}); diff --git a/tests/benchs/_get_binary_bench.ts b/tests/benchs/_get_binary_bench.ts new file mode 100644 index 0000000..1ce2897 --- /dev/null +++ b/tests/benchs/_get_binary_bench.ts @@ -0,0 +1,27 @@ +import { cleanCache, getBinary } from "../../mod.ts"; +import { assert } from "https://deno.land/std@0.201.0/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(); + }, +});