From 4f72b4fdae9baef3d762866b3ae12dfa9967eea0 Mon Sep 17 00:00:00 2001 From: kjuulh Date: Sat, 25 Nov 2023 11:58:22 +0100 Subject: [PATCH] feat: with htmx Signed-off-by: kjuulh --- Cargo.lock | 10 ++ crates/dagger-rust/src/htmx.rs | 261 +++++++++++++++++++++++++++++++ crates/dagger-rust/src/leptos.rs | 245 +++++++++++++++++++++++++++++ crates/dagger-rust/src/lib.rs | 258 +----------------------------- examples/htmx/Cargo.toml | 17 ++ examples/htmx/src/main.rs | 16 ++ 6 files changed, 551 insertions(+), 256 deletions(-) create mode 100644 crates/dagger-rust/src/htmx.rs create mode 100644 crates/dagger-rust/src/leptos.rs create mode 100644 examples/htmx/Cargo.toml create mode 100644 examples/htmx/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 064fe21..16706f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -810,6 +810,16 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +[[package]] +name = "htmx" +version = "0.2.0" +dependencies = [ + "dagger-rust", + "dagger-sdk", + "eyre", + "tokio", +] + [[package]] name = "http" version = "0.2.9" diff --git a/crates/dagger-rust/src/htmx.rs b/crates/dagger-rust/src/htmx.rs new file mode 100644 index 0000000..87d3a7d --- /dev/null +++ b/crates/dagger-rust/src/htmx.rs @@ -0,0 +1,261 @@ +use std::path::PathBuf; + +use crate::{ + build::{BuildProfile, BuildTarget, RustVersion, SlimImage}, + source::RustSource, +}; + +pub struct HtmxBuild { + client: dagger_sdk::Query, + registry: Option, +} + +impl HtmxBuild { + pub fn new(client: dagger_sdk::Query) -> Self { + Self { + client, + registry: None, + } + } + + pub async fn build( + &self, + source_path: Option>, + rust_version: impl AsRef, + profile: impl AsRef, + crates: &[&str], + extra_deps: &[&str], + ) -> eyre::Result { + let source_path = source_path.map(|s| s.into()).unwrap_or(PathBuf::from(".")); + let rust_version = rust_version.as_ref(); + let profile = profile.as_ref(); + + let rust_source = RustSource::new(self.client.clone()); + + let (src, dep_src) = rust_source + .get_rust_src(Some(&source_path), crates.to_vec()) + .await?; + + let mut deps = vec!["apt", "install", "-y"]; + deps.extend(extra_deps); + + let rust_build_image = self + .client + .container() + .from(rust_version.to_string()) + .with_exec(vec!["rustup", "target", "add", "wasm32-unknown-unknown"]) + .with_exec(vec!["apt", "update"]) + .with_exec(deps) + .with_exec(vec!["wget", "https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz"]) + .with_exec("tar -xvf cargo-binstall-x86_64-unknown-linux-musl.tgz".split_whitespace().collect()) + .with_exec("mv cargo-binstall /usr/local/cargo/bin".split_whitespace().collect()) + .with_exec(vec!["cargo", "binstall", "sqlx-cli", "-y"]); + + let target_cache = self + .client + .cache_volume(format!("rust_htmx_{}", profile.to_string())); + + let build_options = vec!["cargo", "sqlx", "prepare"]; + + let rust_prebuild = rust_build_image + .with_workdir("/mnt/src") + .with_directory("/mnt/src", dep_src) + .with_exec(build_options) + .with_mounted_cache("/mnt/src/target/", target_cache); + + let incremental_dir = rust_source + .get_rust_target_src(&source_path, rust_prebuild.clone(), crates.to_vec()) + .await?; + + let rust_with_src = rust_build_image + .with_workdir("/mnt/src") + .with_directory( + "/usr/local/cargo", + rust_prebuild.directory("/usr/local/cargo"), + ) + .with_directory("/mnt/src/target", incremental_dir) + .with_directory("/mnt/src/", src); + + Ok(rust_with_src) + } + + pub async fn build_release( + &self, + source_path: Option>, + rust_version: impl AsRef, + crates: &[&str], + extra_deps: &[&str], + images: impl IntoIterator, + bin_name: &str, + ) -> eyre::Result> { + let images = images.into_iter().collect::>(); + let source_path = source_path.map(|s| s.into()); + + let postgres_password = "somepassword123"; + let postgres = self + .client + .container() + .from("postgres:16.1") + .with_env_variable("POSTGRES_PASSWORD", postgres_password); + + let postgres_service = postgres.with_exposed_port(5432); + + let mut containers = Vec::new(); + for container_image in images { + let container = + match &container_image { + SlimImage::Debian { image, deps, .. } => { + let target = BuildTarget::from_target(&container_image); + + let build_container = self + .build( + source_path.clone(), + &rust_version, + BuildProfile::Release, + crates, + extra_deps, + ) + .await?; + + let binary_build = build_container + .with_service_binding("postgres", postgres_service.as_service()) + .with_env_variable( + "DATABASE_URL", + "root:somepassword123@postgres:5432/postgres", + ) + .with_exec(vec!["cargo", "sqlx", "migrate", "run"]) + .with_exec(vec!["cargo", "sqlx", "prepare"]) + .with_exec(vec!["cargo", "build", "--release", "--bin", bin_name]); + + self.build_debian_image( + binary_build, + image, + BuildTarget::from_target(&container_image), + deps.iter() + .map(|d| d.as_str()) + .collect::>() + .as_slice(), + bin_name, + ) + .await? + } + SlimImage::Alpine { image, deps, .. } => { + let target = BuildTarget::from_target(&container_image); + + let build_container = self + .build( + source_path.clone(), + &rust_version, + BuildProfile::Release, + crates, + extra_deps, + ) + .await?; + + let bin = build_container + .with_exec(vec![ + "cargo", + "build", + "--target", + &target.to_string(), + "--release", + "-p", + bin_name, + ]) + .file(format!("target/{}/release/{}", target.to_string(), bin_name)); + + self.build_alpine_image( + bin, + image, + BuildTarget::from_target(&container_image), + deps.iter() + .map(|d| d.as_str()) + .collect::>() + .as_slice(), + bin_name, + ) + .await? + } + }; + + containers.push(container); + } + + Ok(containers) + } + + async fn build_debian_image( + &self, + builder_image: dagger_sdk::Container, + image: &str, + target: BuildTarget, + production_deps: &[&str], + bin_name: &str, + ) -> eyre::Result { + let base_debian = self + .client + .container_opts(dagger_sdk::QueryContainerOpts { + id: None, + platform: Some(target.into_platform()), + }) + .from(image); + + let mut packages = vec!["apt", "install", "-y"]; + packages.extend_from_slice(production_deps); + let base_debian = base_debian + .with_exec(vec!["apt", "update"]) + .with_exec(packages); + + let final_image = base_debian + .with_workdir("/mnt/app") + .with_file( + format!("/mnt/app/{bin_name}"), + builder_image.file(format!("/mnt/src/target/release/{bin_name}")), + ) + .with_directory( + "/mnt/app/target/site", + builder_image.directory(format!("/mnt/src/target/site")), + ) + .with_file( + "/mnt/app/Cargo.toml", + builder_image.file(format!("/mnt/src/crates/{bin_name}/Cargo.toml")), + ) + .with_env_variable("RUST_LOG", "debug") + .with_env_variable("APP_ENVIRONMENT", "production") + .with_env_variable("LEPTOS_OUTPUT_NAME", bin_name) + .with_env_variable("LEPTOS_SITE_ADDR", "0.0.0.0:8080") + .with_env_variable("LEPTOS_SITE_ROOT", "site") + .with_env_variable("LEPTOS_SITE_PKG_DIR", "pkg") + .with_exposed_port(8080) + .with_entrypoint(vec![format!("/mnt/app/{bin_name}")]); + + final_image.sync().await?; + + Ok(final_image) + } + + async fn build_alpine_image( + &self, + bin: dagger_sdk::File, + image: &str, + target: BuildTarget, + production_deps: &[&str], + bin_name: &str, + ) -> eyre::Result { + let base_debian = self + .client + .container_opts(dagger_sdk::QueryContainerOpts { + id: None, + platform: Some(target.into_platform()), + }) + .from(image); + + let mut packages = vec!["apk", "add"]; + packages.extend_from_slice(production_deps); + let base_debian = base_debian.with_exec(packages); + + let final_image = base_debian.with_file(format!("/usr/local/bin/{}", bin_name), bin); + + Ok(final_image) + } +} diff --git a/crates/dagger-rust/src/leptos.rs b/crates/dagger-rust/src/leptos.rs new file mode 100644 index 0000000..11b4f47 --- /dev/null +++ b/crates/dagger-rust/src/leptos.rs @@ -0,0 +1,245 @@ +use std::path::PathBuf; + +use crate::{ + build::{BuildProfile, BuildTarget, RustVersion, SlimImage}, + source::RustSource, +}; + +pub struct LeptosBuild { + client: dagger_sdk::Query, + registry: Option, +} + +impl LeptosBuild { + pub fn new(client: dagger_sdk::Query) -> Self { + Self { + client, + registry: None, + } + } + + pub async fn build( + &self, + source_path: Option>, + rust_version: impl AsRef, + profile: impl AsRef, + crates: &[&str], + extra_deps: &[&str], + ) -> eyre::Result { + let source_path = source_path.map(|s| s.into()).unwrap_or(PathBuf::from(".")); + let rust_version = rust_version.as_ref(); + let profile = profile.as_ref(); + + let rust_source = RustSource::new(self.client.clone()); + + let (src, dep_src) = rust_source + .get_rust_src(Some(&source_path), crates.to_vec()) + .await?; + + let mut deps = vec!["apt", "install", "-y"]; + deps.extend(extra_deps); + + let rust_build_image = self + .client + .container() + .from(rust_version.to_string()) + .with_exec(vec!["rustup", "target", "add", "wasm32-unknown-unknown"]) + .with_exec(vec!["apt", "update"]) + .with_exec(deps) + .with_exec(vec!["wget", "https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz"]) + .with_exec("tar -xvf cargo-binstall-x86_64-unknown-linux-musl.tgz".split_whitespace().collect()) + .with_exec("mv cargo-binstall /usr/local/cargo/bin".split_whitespace().collect()) + .with_exec(vec!["cargo", "binstall", "cargo-leptos", "-y"]); + + let target_cache = self + .client + .cache_volume(format!("rust_leptos_{}", profile.to_string())); + + let mut build_options = vec!["cargo", "leptos", "build", "--release", "-vv"]; + + let rust_prebuild = rust_build_image + .with_workdir("/mnt/src") + .with_directory("/mnt/src", dep_src) + .with_exec(build_options) + .with_mounted_cache("/mnt/src/target/", target_cache); + + let incremental_dir = rust_source + .get_rust_target_src(&source_path, rust_prebuild.clone(), crates.to_vec()) + .await?; + + let rust_with_src = rust_build_image + .with_workdir("/mnt/src") + .with_directory( + "/usr/local/cargo", + rust_prebuild.directory("/usr/local/cargo"), + ) + .with_directory("/mnt/src/target", incremental_dir) + .with_directory("/mnt/src/", src); + + Ok(rust_with_src) + } + + pub async fn build_release( + &self, + source_path: Option>, + rust_version: impl AsRef, + crates: &[&str], + extra_deps: &[&str], + images: impl IntoIterator, + bin_name: &str, + ) -> eyre::Result> { + let images = images.into_iter().collect::>(); + let source_path = source_path.map(|s| s.into()); + + let mut containers = Vec::new(); + for container_image in images { + let container = match &container_image { + SlimImage::Debian { image, deps, .. } => { + let target = BuildTarget::from_target(&container_image); + + let build_container = self + .build( + source_path.clone(), + &rust_version, + BuildProfile::Release, + crates, + extra_deps, + ) + .await?; + + let binary_build = + build_container + .with_exec(vec!["cargo", "leptos", "build", "--release", "-vv"]); + + self.build_debian_image( + binary_build, + image, + BuildTarget::from_target(&container_image), + deps.iter() + .map(|d| d.as_str()) + .collect::>() + .as_slice(), + bin_name, + ) + .await? + } + SlimImage::Alpine { image, deps, .. } => { + let target = BuildTarget::from_target(&container_image); + + let build_container = self + .build( + source_path.clone(), + &rust_version, + BuildProfile::Release, + crates, + extra_deps, + ) + .await?; + + let bin = build_container + .with_exec(vec![ + "cargo", + "build", + "--target", + &target.to_string(), + "--release", + "-p", + bin_name, + ]) + .file(format!("target/{}/release/{}", target.to_string(), bin_name)); + + self.build_alpine_image( + bin, + image, + BuildTarget::from_target(&container_image), + deps.iter() + .map(|d| d.as_str()) + .collect::>() + .as_slice(), + bin_name, + ) + .await? + } + }; + + containers.push(container); + } + + Ok(containers) + } + + async fn build_debian_image( + &self, + builder_image: dagger_sdk::Container, + image: &str, + target: BuildTarget, + production_deps: &[&str], + bin_name: &str, + ) -> eyre::Result { + let base_debian = self + .client + .container_opts(dagger_sdk::QueryContainerOpts { + id: None, + platform: Some(target.into_platform()), + }) + .from(image); + + let mut packages = vec!["apt", "install", "-y"]; + packages.extend_from_slice(production_deps); + let base_debian = base_debian + .with_exec(vec!["apt", "update"]) + .with_exec(packages); + + let final_image = base_debian + .with_workdir("/mnt/app") + .with_file( + format!("/mnt/app/{bin_name}"), + builder_image.file(format!("/mnt/src/target/release/{bin_name}")), + ) + .with_directory( + "/mnt/app/target/site", + builder_image.directory(format!("/mnt/src/target/site")), + ) + .with_file( + "/mnt/app/Cargo.toml", + builder_image.file(format!("/mnt/src/crates/{bin_name}/Cargo.toml")), + ) + .with_env_variable("RUST_LOG", "debug") + .with_env_variable("APP_ENVIRONMENT", "production") + .with_env_variable("LEPTOS_OUTPUT_NAME", bin_name) + .with_env_variable("LEPTOS_SITE_ADDR", "0.0.0.0:8080") + .with_env_variable("LEPTOS_SITE_ROOT", "site") + .with_env_variable("LEPTOS_SITE_PKG_DIR", "pkg") + .with_exposed_port(8080) + .with_entrypoint(vec![format!("/mnt/app/{bin_name}")]); + + final_image.sync().await?; + + Ok(final_image) + } + + async fn build_alpine_image( + &self, + bin: dagger_sdk::File, + image: &str, + target: BuildTarget, + production_deps: &[&str], + bin_name: &str, + ) -> eyre::Result { + let base_debian = self + .client + .container_opts(dagger_sdk::QueryContainerOpts { + id: None, + platform: Some(target.into_platform()), + }) + .from(image); + + let mut packages = vec!["apk", "add"]; + packages.extend_from_slice(production_deps); + let base_debian = base_debian.with_exec(packages); + + let final_image = base_debian.with_file(format!("/usr/local/bin/{}", bin_name), bin); + + Ok(final_image) + } +} diff --git a/crates/dagger-rust/src/lib.rs b/crates/dagger-rust/src/lib.rs index f145278..7362196 100644 --- a/crates/dagger-rust/src/lib.rs +++ b/crates/dagger-rust/src/lib.rs @@ -1,260 +1,6 @@ pub mod build; +pub mod htmx; +pub mod leptos; pub mod publish; pub mod source; pub mod test; -pub mod leptos { - - use std::path::PathBuf; - - use crate::{ - build::{BuildProfile, BuildTarget, RustVersion, SlimImage}, - source::RustSource, - }; - - pub struct LeptosBuild { - client: dagger_sdk::Query, - registry: Option, - } - - impl LeptosBuild { - pub fn new(client: dagger_sdk::Query) -> Self { - Self { - client, - registry: None, - } - } - - pub async fn build( - &self, - source_path: Option>, - rust_version: impl AsRef, - profile: impl AsRef, - crates: &[&str], - extra_deps: &[&str], - ) -> eyre::Result { - let source_path = source_path.map(|s| s.into()).unwrap_or(PathBuf::from(".")); - let rust_version = rust_version.as_ref(); - let profile = profile.as_ref(); - - let rust_source = RustSource::new(self.client.clone()); - - let (src, dep_src) = rust_source - .get_rust_src(Some(&source_path), crates.to_vec()) - .await?; - - let mut deps = vec!["apt", "install", "-y"]; - deps.extend(extra_deps); - - let rust_build_image = self - .client - .container() - .from(rust_version.to_string()) - .with_exec(vec!["rustup", "target", "add", "wasm32-unknown-unknown"]) - .with_exec(vec!["apt", "update"]) - .with_exec(deps) - .with_exec(vec!["wget", "https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz"]) - .with_exec("tar -xvf cargo-binstall-x86_64-unknown-linux-musl.tgz".split_whitespace().collect()) - .with_exec("mv cargo-binstall /usr/local/cargo/bin".split_whitespace().collect()) - .with_exec(vec!["cargo", "binstall", "cargo-leptos", "-y"]); - - let target_cache = self - .client - .cache_volume(format!("rust_leptos_{}", profile.to_string())); - - let mut build_options = vec!["cargo", "leptos", "build", "--release", "-vv"]; - - let rust_prebuild = rust_build_image - .with_workdir("/mnt/src") - .with_directory("/mnt/src", dep_src) - .with_exec(build_options) - .with_mounted_cache("/mnt/src/target/", target_cache); - - let incremental_dir = rust_source - .get_rust_target_src(&source_path, rust_prebuild.clone(), crates.to_vec()) - .await?; - - let rust_with_src = rust_build_image - .with_workdir("/mnt/src") - .with_directory( - "/usr/local/cargo", - rust_prebuild.directory("/usr/local/cargo"), - ) - .with_directory("/mnt/src/target", incremental_dir) - .with_directory("/mnt/src/", src); - - Ok(rust_with_src) - } - - pub async fn build_release( - &self, - source_path: Option>, - rust_version: impl AsRef, - crates: &[&str], - extra_deps: &[&str], - images: impl IntoIterator, - bin_name: &str, - ) -> eyre::Result> { - let images = images.into_iter().collect::>(); - let source_path = source_path.map(|s| s.into()); - - let mut containers = Vec::new(); - for container_image in images { - let container = match &container_image { - SlimImage::Debian { image, deps, .. } => { - let target = BuildTarget::from_target(&container_image); - - let build_container = self - .build( - source_path.clone(), - &rust_version, - BuildProfile::Release, - crates, - extra_deps, - ) - .await?; - - let binary_build = build_container.with_exec(vec![ - "cargo", - "leptos", - "build", - "--release", - "-vv", - ]); - - self.build_debian_image( - binary_build, - image, - BuildTarget::from_target(&container_image), - deps.iter() - .map(|d| d.as_str()) - .collect::>() - .as_slice(), - bin_name, - ) - .await? - } - SlimImage::Alpine { image, deps, .. } => { - let target = BuildTarget::from_target(&container_image); - - let build_container = self - .build( - source_path.clone(), - &rust_version, - BuildProfile::Release, - crates, - extra_deps, - ) - .await?; - - let bin = build_container - .with_exec(vec![ - "cargo", - "build", - "--target", - &target.to_string(), - "--release", - "-p", - bin_name, - ]) - .file(format!( - "target/{}/release/{}", - target.to_string(), - bin_name - )); - - self.build_alpine_image( - bin, - image, - BuildTarget::from_target(&container_image), - deps.iter() - .map(|d| d.as_str()) - .collect::>() - .as_slice(), - bin_name, - ) - .await? - } - }; - - containers.push(container); - } - - Ok(containers) - } - - async fn build_debian_image( - &self, - builder_image: dagger_sdk::Container, - image: &str, - target: BuildTarget, - production_deps: &[&str], - bin_name: &str, - ) -> eyre::Result { - let base_debian = self - .client - .container_opts(dagger_sdk::QueryContainerOpts { - id: None, - platform: Some(target.into_platform()), - }) - .from(image); - - let mut packages = vec!["apt", "install", "-y"]; - packages.extend_from_slice(production_deps); - let base_debian = base_debian - .with_exec(vec!["apt", "update"]) - .with_exec(packages); - - let final_image = base_debian - .with_workdir("/mnt/app") - .with_file( - format!("/mnt/app/{bin_name}"), - builder_image.file(format!("/mnt/src/target/release/{bin_name}")), - ) - .with_directory( - "/mnt/app/target/site", - builder_image.directory(format!("/mnt/src/target/site")), - ) - .with_file( - "/mnt/app/Cargo.toml", - builder_image.file(format!("/mnt/src/crates/{bin_name}/Cargo.toml")), - ) - .with_env_variable("RUST_LOG", "debug") - .with_env_variable("APP_ENVIRONMENT", "production") - .with_env_variable("LEPTOS_OUTPUT_NAME", bin_name) - .with_env_variable("LEPTOS_SITE_ADDR", "0.0.0.0:8080") - .with_env_variable("LEPTOS_SITE_ROOT", "site") - .with_env_variable("LEPTOS_SITE_PKG_DIR", "pkg") - .with_exposed_port(8080) - .with_entrypoint(vec![format!("/mnt/app/{bin_name}")]); - - final_image.sync().await?; - - Ok(final_image) - } - - async fn build_alpine_image( - &self, - bin: dagger_sdk::File, - image: &str, - target: BuildTarget, - production_deps: &[&str], - bin_name: &str, - ) -> eyre::Result { - let base_debian = self - .client - .container_opts(dagger_sdk::QueryContainerOpts { - id: None, - platform: Some(target.into_platform()), - }) - .from(image); - - let mut packages = vec!["apk", "add"]; - packages.extend_from_slice(production_deps); - let base_debian = base_debian.with_exec(packages); - - let final_image = base_debian.with_file(format!("/usr/local/bin/{}", bin_name), bin); - - Ok(final_image) - } - } -} diff --git a/examples/htmx/Cargo.toml b/examples/htmx/Cargo.toml new file mode 100644 index 0000000..d351624 --- /dev/null +++ b/examples/htmx/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "htmx" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +readme.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dagger-rust.workspace = true + +eyre.workspace = true +dagger-sdk.workspace = true +tokio.workspace = true diff --git a/examples/htmx/src/main.rs b/examples/htmx/src/main.rs new file mode 100644 index 0000000..2227aa9 --- /dev/null +++ b/examples/htmx/src/main.rs @@ -0,0 +1,16 @@ +use std::path::PathBuf; + +#[tokio::main] +pub async fn main() -> eyre::Result<()> { + let client = dagger_sdk::connect().await?; + + let crates = ["some-crate"]; + let dag = dagger_rust::source::RustSource::new(client.clone()); + let (_src, _rust_src) = dag.get_rust_src(None::, crates).await?; + + let _full_src = dag + .get_rust_target_src(&PathBuf::from("."), client.container(), crates) + .await?; + + Ok(()) +}