wash_lib/start/github/
mod.rs1use anyhow::{anyhow, bail, Result};
4use async_compression::tokio::bufread::GzipDecoder;
5#[cfg(target_family = "unix")]
6use std::os::unix::prelude::PermissionsExt;
7use std::path::{Path, PathBuf};
8use std::{ffi::OsStr, io::Cursor};
9use tokio::fs::{create_dir_all, metadata, File};
10use tokio_stream::StreamExt;
11use tokio_tar::Archive;
12use wasmcloud_core::tls::NativeRootsExt;
13
14pub const DOWNLOAD_CLIENT_USER_AGENT: &str =
15 concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
16
17pub const GITHUB_WASMCLOUD_ORG: &str = "wasmCloud";
18pub const GITHUB_WASMCLOUD_WASMCLOUD_REPO: &str = "wasmCloud";
19pub const GITHUB_WASMCLOUD_WADM_REPO: &str = "wadm";
20
21mod api;
22pub use api::*;
23
24pub async fn download_binary_from_github<P>(url: &str, dir: P, bin_name: &str) -> Result<PathBuf>
43where
44 P: AsRef<Path>,
45{
46 let bin_path = dir.as_ref().join(bin_name);
47 let body = match get_download_client()?.get(url).send().await {
49 Ok(resp) => resp.bytes().await?,
50 Err(e) => bail!("Failed to request release tarball: {:?}", e),
51 };
52 let cursor = Cursor::new(body);
53 let mut bin_tarball = Archive::new(Box::new(GzipDecoder::new(cursor)));
54
55 let mut entries = bin_tarball.entries()?;
57 while let Some(res) = entries.next().await {
58 let mut entry = res.map_err(|e| {
59 anyhow!(
60 "Failed to retrieve file from archive, ensure {bin_name} exists. Original error: {e}",
61 )
62 })?;
63 if let Ok(tar_path) = entry.path() {
64 match tar_path.file_name() {
65 Some(name) if name == OsStr::new(bin_name) => {
66 create_dir_all(&dir).await?;
68 let mut bin_file = File::create(&bin_path).await?;
69 #[cfg(target_family = "unix")]
71 {
72 let mut permissions = bin_file.metadata().await?.permissions();
73 permissions.set_mode(0o755);
75 bin_file.set_permissions(permissions).await?;
76 }
77
78 tokio::io::copy(&mut entry, &mut bin_file).await?;
79 return Ok(bin_path);
80 }
81 _ => (),
83 }
84 }
85 }
86
87 bail!("{bin_name} binary could not be installed, please see logs")
88}
89
90#[allow(unused)]
92pub(crate) async fn is_bin_installed<P>(dir: P, bin_name: &str) -> bool
93where
94 P: AsRef<Path>,
95{
96 metadata(dir.as_ref().join(bin_name))
97 .await
98 .is_ok_and(|m| m.is_file())
99}
100
101pub fn get_download_client() -> Result<reqwest::Client> {
103 get_download_client_with_user_agent(DOWNLOAD_CLIENT_USER_AGENT)
104}
105
106pub(crate) fn get_download_client_with_user_agent(user_agent: &str) -> Result<reqwest::Client> {
108 let proxy_username = std::env::var("WASH_PROXY_USERNAME").unwrap_or_default();
109 let proxy_password = std::env::var("WASH_PROXY_PASSWORD").unwrap_or_default();
110
111 let mut builder = reqwest::ClientBuilder::default()
112 .user_agent(user_agent)
113 .with_native_certificates();
114
115 if let Ok(http_proxy) = std::env::var("HTTP_PROXY").or_else(|_| std::env::var("http_proxy")) {
116 let mut proxy = reqwest::Proxy::http(http_proxy)?.no_proxy(reqwest::NoProxy::from_env());
117 if !proxy_username.is_empty() && !proxy_password.is_empty() {
118 proxy = proxy.basic_auth(&proxy_username, &proxy_password);
119 }
120 builder = builder.proxy(proxy);
121 }
122
123 if let Ok(https_proxy) = std::env::var("HTTPS_PROXY").or_else(|_| std::env::var("https_proxy"))
124 {
125 let mut proxy = reqwest::Proxy::https(https_proxy)?.no_proxy(reqwest::NoProxy::from_env());
126 if !proxy_username.is_empty() && !proxy_password.is_empty() {
127 proxy = proxy.basic_auth(&proxy_username, &proxy_password);
128 }
129 builder = builder.proxy(proxy);
130 }
131
132 Ok(builder.build()?)
133}