fork of https://github.com/rustwasm/wasm-pack for the needs of NextGraph.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
wasm-pack/src/test/webdriver/chromedriver.rs

163 lines
5.6 KiB

use super::get_and_notify;
use crate::install::InstallMode;
use crate::stamps;
use crate::target;
use anyhow::{bail, Context, Result};
use binary_install::Cache;
use chrono::DateTime;
use std::collections::HashMap;
use std::path::PathBuf;
// Keep it up to date with each `wasm-pack` release.
// https://chromedriver.storage.googleapis.com/LATEST_RELEASE
const DEFAULT_CHROMEDRIVER_VERSION: &str = "114.0.5735.90";
const CHROMEDRIVER_LAST_UPDATED_STAMP: &str = "chromedriver_last_updated";
const CHROMEDRIVER_VERSION_STAMP: &str = "chromedriver_version";
/// Get the path to an existing `chromedriver`, or install it if no existing
/// binary is found or if there is a new binary version.
pub fn get_or_install_chromedriver(cache: &Cache, mode: InstallMode) -> Result<PathBuf> {
if let Ok(path) = which::which("chromedriver") {
return Ok(path);
}
install_chromedriver(cache, mode.install_permitted())
}
/// Download and install a pre-built `chromedriver` binary.
pub fn install_chromedriver(cache: &Cache, installation_allowed: bool) -> Result<PathBuf> {
let target = if target::LINUX && target::x86_64 {
"linux64"
} else if target::MACOS && target::x86_64 {
"mac-x64"
} else if target::MACOS && target::aarch64 {
"mac-arm64"
} else if target::WINDOWS {
"win32"
} else {
bail!("chromedriver binaries are unavailable for this target")
};
let url = get_chromedriver_url(target);
match get_and_notify(cache, installation_allowed, "chromedriver", &url)? {
Some(path) => Ok(path),
None => bail!(
"No cached `chromedriver` binary found, and could not find a global \
`chromedriver` on the `$PATH`. Not installing `chromedriver` because of noinstall \
mode."
),
}
}
/// Get `chromedriver` download URL.
///
/// _Algorithm_:
/// 1. Try to open `*.stamps` file and deserialize its content to JSON object.
/// 2. Try to compare current time with the saved one.
/// 3. If the saved time is older than 1 day or something failed
/// => fetch a new version and save version & time.
/// 4. If everything failed, use the default version.
/// 5. Return URL.
///
/// _Notes:_
///
/// It returns the latest one without checking the installed `Chrome` version
/// because it's not easy to find out `Chrome` version on `Windows` -
/// https://bugs.chromium.org/p/chromium/issues/detail?id=158372
///
/// The official algorithm for `chromedriver` version selection:
/// https://chromedriver.chromium.org/downloads/version-selection
fn get_chromedriver_url(target: &str) -> String {
let fetch_and_save_version =
|| fetch_chromedriver_version().and_then(save_chromedriver_version);
let chromedriver_version = match stamps::read_stamps_file_to_json() {
Ok(json) => {
if should_load_chromedriver_version_from_stamp(&json) {
stamps::get_stamp_value(CHROMEDRIVER_VERSION_STAMP, &json)
} else {
fetch_and_save_version()
}
}
Err(_) => fetch_and_save_version(),
}
.unwrap_or_else(|error| {
log::warn!(
"Cannot load or fetch chromedriver's latest version data, \
the default version {} will be used. Error: {}",
DEFAULT_CHROMEDRIVER_VERSION,
error
);
DEFAULT_CHROMEDRIVER_VERSION.to_owned()
});
assemble_chromedriver_url(&chromedriver_version, target)
}
// ------ `get_chromedriver_url` helpers ------
fn save_chromedriver_version(version: String) -> Result<String> {
stamps::save_stamp_value(CHROMEDRIVER_VERSION_STAMP, &version)?;
let current_time = chrono::offset::Local::now().to_rfc3339();
stamps::save_stamp_value(CHROMEDRIVER_LAST_UPDATED_STAMP, current_time)?;
Ok(version)
}
fn should_load_chromedriver_version_from_stamp(json: &serde_json::Value) -> bool {
let last_updated = stamps::get_stamp_value(CHROMEDRIVER_LAST_UPDATED_STAMP, json)
.ok()
.and_then(|last_updated| DateTime::parse_from_rfc3339(&last_updated).ok());
match last_updated {
None => false,
Some(last_updated) => {
let current_time = chrono::offset::Local::now();
current_time.signed_duration_since(last_updated).num_hours() < 24
}
}
}
/// Channel information from the chromedriver version endpoint.
#[derive(Deserialize)]
struct ChannelInfo {
version: String,
}
/// The response from the chromedriver version endpoint.
#[derive(Deserialize)]
struct GoodLatestVersions {
channels: HashMap<String, ChannelInfo>,
}
/// Retrieve the latest version of chromedriver from the json endpoints.
/// See: <https://github.com/GoogleChromeLabs/chrome-for-testing#json-api-endpoints>
fn fetch_chromedriver_version() -> Result<String> {
let info: GoodLatestVersions = ureq::get(
"https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions.json",
)
.call()
.context("fetching of chromedriver's LATEST_RELEASE failed")?
.into_json()
.context("converting chromedriver version response to GoodLatestVersions failed")?;
let version = info
.channels
.get("Stable")
.ok_or_else(|| anyhow::anyhow!("no Stable channel found in chromedriver version response"))?
.version
.clone();
println!("chromedriver version: {}", version);
Ok(version)
}
fn assemble_chromedriver_url(chromedriver_version: &str, target: &str) -> String {
format!(
"https://storage.googleapis.com/chrome-for-testing-public/{version}/{target}/chromedriver-{target}.zip",
version = chromedriver_version,
target = target,
)
}