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