oxyde_cloud_deploy/
deploy.rs1use cargo_leptos::config::Opts;
2use oxyde_cloud_client::{Client, ReqwestJsonError, UploadFileError};
3use oxyde_cloud_common::config::CloudConfig;
4use std::env::VarError;
5use std::fs::read_dir;
6use std::path::{Path, PathBuf};
7use thiserror::Error;
8use walkdir::WalkDir;
9
10#[derive(Debug, Error)]
11pub enum Error {
12 #[error("Build error: {0}")]
13 Build(#[from] anyhow::Error),
14
15 #[error("IO error: {0}")]
16 Io(#[from] std::io::Error),
17
18 #[error("Check Name error: {0}")]
19 CheckName(#[from] ReqwestJsonError),
20
21 #[error("Upload error: {0}")]
22 Upload(#[from] UploadFileError),
23
24 #[error("Deployment done error: {0}")]
25 Done(#[from] reqwest::Error),
26
27 #[error("Error reading variable `OXYDE_CLOUD_API_KEY`: {0}")]
28 ApiKeyEnv(#[from] VarError),
29
30 #[error("Config loading error: {0}")]
31 Config(#[from] oxyde_cloud_common::config::Error),
32}
33
34pub async fn deploy_with_config_file(
35 config: &PathBuf,
36 cargo_leptos_opts: Opts,
37) -> Result<(), Error> {
38 let config = CloudConfig::load(config).await?;
39 deploy(&config, cargo_leptos_opts).await?;
40 Ok(())
41}
42
43pub async fn deploy(config: &CloudConfig, cargo_leptos_opts: Opts) -> Result<(), Error> {
44 crate::build::build(cargo_leptos_opts.clone()).await.map_err(Error::Build)?;
45
46 let target_dir = "target";
47 let target_bin_dir = "target/x86_64-unknown-linux-musl";
48
49 let server_bin_dir = if cargo_leptos_opts.release {
50 "release"
51 } else {
52 "debug"
53 };
54 let frontend_dir = "site";
55
56 let api_key = std::env::var("OXYDE_CLOUD_API_KEY")?;
57 let client = Client::new(api_key.clone());
58
59 let frontend_path = Path::new(target_dir).join(frontend_dir);
60 let server_path = Path::new(target_bin_dir).join(server_bin_dir);
61
62 let mut files = recursive_files_from_dir(frontend_path);
63 files.append(&mut server_files(server_path)?);
64
65 log::debug!(target:"cargo_leptos", "Found files: {:#?}", files);
66
67 log::info!(target:"cargo_leptos", "Deploying app {}", config.app.slug);
68
69 if let Err(err) = deploy_inner(config, client, &mut files).await {
70 log::error!(target:"cargo_leptos", "Deploy failed: {:?}", err);
71 return Err(err);
72 }
73
74 log::info!(target:"cargo_leptos", "Deployed app to {}", config.deployed_url());
75
76 Ok(())
77}
78
79async fn deploy_inner(
80 config: &CloudConfig,
81 client: Client,
82 files: &mut Vec<PathBuf>,
83) -> Result<(), Error> {
84 for file in files {
85 log::debug!(target:"cargo_leptos", "Uploading {}...", file.display());
86 client.clone().upload_file(&config.app.slug, file).await?;
87 }
88
89 log::debug!(target:"cargo_leptos", "Deploying app...");
90 client.upload_done(config).await?;
91
92 Ok(())
93}
94
95fn recursive_files_from_dir(dir: impl AsRef<Path>) -> Vec<PathBuf> {
96 WalkDir::new(dir)
97 .into_iter()
98 .filter_map(|e| e.ok().map(|e| e.into_path()))
99 .filter_map(|e| if e.is_file() { Some(e) } else { None })
100 .collect()
101}
102
103fn server_files(dir: impl AsRef<Path>) -> std::io::Result<Vec<PathBuf>> {
104 let starts_with_a_dot = |path: &Path| {
105 path.file_name().expect("cant read filename").to_str().expect("cant convert filename").starts_with(".")
106 };
107
108 Ok(read_dir(dir)?
109 .filter_map(|d| {
110 d.ok().and_then(|e| {
111 let path = e.path();
112 if path.is_file() && path.extension().is_none() && !starts_with_a_dot(&path) {
113 Some(path)
114 } else {
115 None
116 }
117 })
118 })
119 .collect())
120}