parent
439e523192
commit
2facd17653
@ -0,0 +1,77 @@ |
|||||||
|
use super::{get_and_notify, Collector}; |
||||||
|
use binary_install::Cache; |
||||||
|
use failure; |
||||||
|
use install::InstallMode; |
||||||
|
use std::path::PathBuf; |
||||||
|
use target; |
||||||
|
|
||||||
|
/// 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, failure::Error> { |
||||||
|
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, failure::Error> { |
||||||
|
let target = if target::LINUX && target::x86_64 { |
||||||
|
"linux64" |
||||||
|
} else if target::MACOS && target::x86_64 { |
||||||
|
"mac64" |
||||||
|
} 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.
|
||||||
|
///
|
||||||
|
/// 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) -> Result<String, failure::Error> { |
||||||
|
let chromedriver_version = fetch_chromedriver_version()?; |
||||||
|
Ok(assemble_chromedriver_url(&chromedriver_version, target)) |
||||||
|
} |
||||||
|
|
||||||
|
// ------ `get_chromedriver_url` steps ------
|
||||||
|
|
||||||
|
fn fetch_chromedriver_version() -> Result<String, failure::Error> { |
||||||
|
let mut handle = curl::easy::Easy2::new(Collector(Vec::new())); |
||||||
|
handle.url("https://chromedriver.storage.googleapis.com/LATEST_RELEASE")?; |
||||||
|
handle.perform()?; |
||||||
|
|
||||||
|
let contents = handle.get_ref(); |
||||||
|
Ok(String::from_utf8_lossy(&contents.0).into_owned()) |
||||||
|
} |
||||||
|
|
||||||
|
fn assemble_chromedriver_url(chromedriver_version: &str, target: &str) -> String { |
||||||
|
format!( |
||||||
|
"https://chromedriver.storage.googleapis.com/{version}/chromedriver_{target}.zip", |
||||||
|
version = chromedriver_version, |
||||||
|
target = target, |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
use super::{get_and_notify, Collector}; |
||||||
|
use binary_install::Cache; |
||||||
|
use failure; |
||||||
|
use install::InstallMode; |
||||||
|
use std::path::PathBuf; |
||||||
|
use target; |
||||||
|
|
||||||
|
/// Get the path to an existing `geckodriver`, or install it if no existing
|
||||||
|
/// binary is found or if there is a new binary version.
|
||||||
|
pub fn get_or_install_geckodriver( |
||||||
|
cache: &Cache, |
||||||
|
mode: InstallMode, |
||||||
|
) -> Result<PathBuf, failure::Error> { |
||||||
|
if let Ok(path) = which::which("geckodriver") { |
||||||
|
return Ok(path); |
||||||
|
} |
||||||
|
install_geckodriver(cache, mode.install_permitted()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Download and install a pre-built `geckodriver` binary.
|
||||||
|
pub fn install_geckodriver( |
||||||
|
cache: &Cache, |
||||||
|
installation_allowed: bool, |
||||||
|
) -> Result<PathBuf, failure::Error> { |
||||||
|
let (target, ext) = if target::LINUX && target::x86 { |
||||||
|
("linux32", "tar.gz") |
||||||
|
} else if target::LINUX && target::x86_64 { |
||||||
|
("linux64", "tar.gz") |
||||||
|
} else if target::MACOS { |
||||||
|
("macos", "tar.gz") |
||||||
|
} else if target::WINDOWS && target::x86 { |
||||||
|
("win32", "zip") |
||||||
|
} else if target::WINDOWS && target::x86_64 { |
||||||
|
("win64", "zip") |
||||||
|
} else { |
||||||
|
bail!("geckodriver binaries are unavailable for this target") |
||||||
|
}; |
||||||
|
|
||||||
|
let url = get_geckodriver_url(target, ext)?; |
||||||
|
|
||||||
|
match get_and_notify(cache, installation_allowed, "geckodriver", &url)? { |
||||||
|
Some(path) => Ok(path), |
||||||
|
None => bail!( |
||||||
|
"No cached `geckodriver` binary found, and could not find a global `geckodriver` \ |
||||||
|
on the `$PATH`. Not installing `geckodriver` because of noinstall mode." |
||||||
|
), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Get `geckodriver` download URL.
|
||||||
|
///
|
||||||
|
/// It returns the latest one without checking the installed `Firefox` version
|
||||||
|
/// - it should be relatively safe because each `geckodriver` supports many `Firefox` versions:
|
||||||
|
/// https://firefox-source-docs.mozilla.org/testing/geckodriver/Support.html#supported-platforms
|
||||||
|
fn get_geckodriver_url(target: &str, ext: &str) -> Result<String, failure::Error> { |
||||||
|
// JSON example: `{"id":15227534,"tag_name":"v0.24.0","update_url":"/mozzila...`
|
||||||
|
let latest_tag_json = fetch_latest_geckodriver_tag_json()?; |
||||||
|
let latest_tag = get_tag_name_from_json(&latest_tag_json)?; |
||||||
|
Ok(assemble_geckodriver_url(&latest_tag, target, ext)) |
||||||
|
} |
||||||
|
|
||||||
|
// ------ `get_geckodriver_url` steps ------
|
||||||
|
|
||||||
|
fn fetch_latest_geckodriver_tag_json() -> Result<String, failure::Error> { |
||||||
|
let mut headers = curl::easy::List::new(); |
||||||
|
headers.append("Accept: application/json")?; |
||||||
|
|
||||||
|
let mut handle = curl::easy::Easy2::new(Collector(Vec::new())); |
||||||
|
handle.url("https://github.com/mozilla/geckodriver/releases/latest")?; |
||||||
|
handle.http_headers(headers)?; |
||||||
|
// We will be redirected from the `latest` placeholder to the specific tag name.
|
||||||
|
handle.follow_location(true)?; |
||||||
|
handle.perform()?; |
||||||
|
|
||||||
|
let contents = handle.get_ref(); |
||||||
|
Ok(String::from_utf8_lossy(&contents.0).into_owned()) |
||||||
|
} |
||||||
|
|
||||||
|
fn get_tag_name_from_json(json: &str) -> Result<String, failure::Error> { |
||||||
|
let json: serde_json::Value = serde_json::from_str(json)?; |
||||||
|
json.get("tag_name") |
||||||
|
.and_then(|tag_name| tag_name.as_str().map(ToOwned::to_owned)) |
||||||
|
.ok_or_else(|| failure::err_msg("cannot get `tag_name` from JSON response")) |
||||||
|
} |
||||||
|
|
||||||
|
fn assemble_geckodriver_url(tag: &str, target: &str, ext: &str) -> String { |
||||||
|
format!( |
||||||
|
"https://github.com/mozilla/geckodriver/releases/download/{tag}/geckodriver-{tag}-{target}.{ext}", |
||||||
|
tag=tag, |
||||||
|
target=target, |
||||||
|
ext=ext, |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
use std::path::PathBuf; |
||||||
|
|
||||||
|
/// Get the path to an existing `safaridriver`.
|
||||||
|
///
|
||||||
|
/// We can't install `safaridriver` if an existing one is not found because
|
||||||
|
/// Apple does not provide pre-built binaries. However, `safaridriver` *should*
|
||||||
|
/// be present by default.
|
||||||
|
pub fn get_safaridriver() -> Result<PathBuf, failure::Error> { |
||||||
|
match which::which("safaridriver") { |
||||||
|
Ok(p) => Ok(p), |
||||||
|
Err(_) => bail!("could not find `safaridriver` on the `$PATH`"), |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue