use anyhow::{anyhow, bail, Result};
use async_compression::tokio::bufread::GzipDecoder;
#[cfg(target_family = "unix")]
use std::os::unix::prelude::PermissionsExt;
use std::path::{Path, PathBuf};
use std::{ffi::OsStr, io::Cursor};
use tokio::fs::{create_dir_all, metadata, File};
use tokio_stream::StreamExt;
use tokio_tar::Archive;
use wasmcloud_core::tls::NativeRootsExt;
pub const DOWNLOAD_CLIENT_USER_AGENT: &str =
concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
pub const GITHUB_WASMCLOUD_ORG: &str = "wasmCloud";
pub const GITHUB_WASMCLOUD_WASMCLOUD_REPO: &str = "wasmCloud";
pub const GITHUB_WASMCLOUD_WADM_REPO: &str = "wadm";
mod api;
pub use api::*;
pub async fn download_binary_from_github<P>(url: &str, dir: P, bin_name: &str) -> Result<PathBuf>
where
P: AsRef<Path>,
{
let bin_path = dir.as_ref().join(bin_name);
let body = match get_download_client()?.get(url).send().await {
Ok(resp) => resp.bytes().await?,
Err(e) => bail!("Failed to request release tarball: {:?}", e),
};
let cursor = Cursor::new(body);
let mut bin_tarball = Archive::new(Box::new(GzipDecoder::new(cursor)));
let mut entries = bin_tarball.entries()?;
while let Some(res) = entries.next().await {
let mut entry = res.map_err(|e| {
anyhow!(
"Failed to retrieve file from archive, ensure {bin_name} exists. Original error: {e}",
)
})?;
if let Ok(tar_path) = entry.path() {
match tar_path.file_name() {
Some(name) if name == OsStr::new(bin_name) => {
create_dir_all(&dir).await?;
let mut bin_file = File::create(&bin_path).await?;
#[cfg(target_family = "unix")]
{
let mut permissions = bin_file.metadata().await?.permissions();
permissions.set_mode(0o755);
bin_file.set_permissions(permissions).await?;
}
tokio::io::copy(&mut entry, &mut bin_file).await?;
return Ok(bin_path);
}
_ => (),
}
}
}
bail!("{bin_name} binary could not be installed, please see logs")
}
#[allow(unused)]
pub(crate) async fn is_bin_installed<P>(dir: P, bin_name: &str) -> bool
where
P: AsRef<Path>,
{
metadata(dir.as_ref().join(bin_name))
.await
.is_ok_and(|m| m.is_file())
}
pub fn get_download_client() -> Result<reqwest::Client> {
get_download_client_with_user_agent(DOWNLOAD_CLIENT_USER_AGENT)
}
pub(crate) fn get_download_client_with_user_agent(user_agent: &str) -> Result<reqwest::Client> {
let proxy_username = std::env::var("WASH_PROXY_USERNAME").unwrap_or_default();
let proxy_password = std::env::var("WASH_PROXY_PASSWORD").unwrap_or_default();
let mut builder = reqwest::ClientBuilder::default()
.user_agent(user_agent)
.with_native_certificates();
if let Ok(http_proxy) = std::env::var("HTTP_PROXY").or_else(|_| std::env::var("http_proxy")) {
let mut proxy = reqwest::Proxy::http(http_proxy)?.no_proxy(reqwest::NoProxy::from_env());
if !proxy_username.is_empty() && !proxy_password.is_empty() {
proxy = proxy.basic_auth(&proxy_username, &proxy_password);
}
builder = builder.proxy(proxy);
}
if let Ok(https_proxy) = std::env::var("HTTPS_PROXY").or_else(|_| std::env::var("https_proxy"))
{
let mut proxy = reqwest::Proxy::https(https_proxy)?.no_proxy(reqwest::NoProxy::from_env());
if !proxy_username.is_empty() && !proxy_password.is_empty() {
proxy = proxy.basic_auth(&proxy_username, &proxy_password);
}
builder = builder.proxy(proxy);
}
Ok(builder.build()?)
}