diff --git a/src/cli.rs b/src/cli.rs index 85b3b91..9281877 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -88,6 +88,9 @@ pub enum Verb { /// Apply manifests and rollout restart after pushing (implies --push). #[arg(long)] deploy: bool, + /// Disable buildkitd layer cache. + #[arg(long)] + no_cache: bool, }, /// Functional service health checks. @@ -243,6 +246,7 @@ pub enum BuildTarget { Tuwunel, Calendars, Projects, + Sol, } impl std::fmt::Display for BuildTarget { @@ -265,6 +269,7 @@ impl std::fmt::Display for BuildTarget { BuildTarget::Tuwunel => "tuwunel", BuildTarget::Calendars => "calendars", BuildTarget::Projects => "projects", + BuildTarget::Sol => "sol", }; write!(f, "{s}") } @@ -465,10 +470,11 @@ mod tests { fn test_build_proxy() { let cli = parse(&["sunbeam", "build", "proxy"]); match cli.verb { - Some(Verb::Build { what, push, deploy }) => { + Some(Verb::Build { what, push, deploy, no_cache }) => { assert!(matches!(what, BuildTarget::Proxy)); assert!(!push); assert!(!deploy); + assert!(!no_cache); } _ => panic!("expected Build"), } @@ -479,10 +485,11 @@ mod tests { fn test_build_deploy_flag() { let cli = parse(&["sunbeam", "build", "proxy", "--deploy"]); match cli.verb { - Some(Verb::Build { deploy, push, .. }) => { + Some(Verb::Build { deploy, push, no_cache, .. }) => { assert!(deploy); // clap does not imply --push; that logic is in dispatch() assert!(!push); + assert!(!no_cache); } _ => panic!("expected Build"), } @@ -838,9 +845,9 @@ pub async fn dispatch() -> Result<()> { crate::services::cmd_restart(target.as_deref()).await } - Some(Verb::Build { what, push, deploy }) => { + Some(Verb::Build { what, push, deploy, no_cache }) => { let push = push || deploy; - crate::images::cmd_build(&what, push, deploy).await + crate::images::cmd_build(&what, push, deploy, no_cache).await } Some(Verb::Check { target }) => { diff --git a/src/images.rs b/src/images.rs index 4cb62fd..ea290e3 100644 --- a/src/images.rs +++ b/src/images.rs @@ -837,7 +837,7 @@ async fn clear_image_pull_error_pods() -> Result<()> { // Per-service build functions // --------------------------------------------------------------------------- -async fn build_proxy(push: bool, deploy: bool) -> Result<()> { +async fn build_proxy(push: bool, deploy: bool, no_cache: bool) -> Result<()> { let env = get_build_env().await?; let proxy_dir = crate::config::get_repo_root().join("proxy"); if !proxy_dir.is_dir() { @@ -855,7 +855,7 @@ async fn build_proxy(push: bool, deploy: bool) -> Result<()> { None, None, push, - false, + no_cache, &[], ) .await?; @@ -866,7 +866,7 @@ async fn build_proxy(push: bool, deploy: bool) -> Result<()> { Ok(()) } -async fn build_tuwunel(push: bool, deploy: bool) -> Result<()> { +async fn build_tuwunel(push: bool, deploy: bool, no_cache: bool) -> Result<()> { let env = get_build_env().await?; let tuwunel_dir = crate::config::get_repo_root().join("tuwunel"); if !tuwunel_dir.is_dir() { @@ -884,7 +884,7 @@ async fn build_tuwunel(push: bool, deploy: bool) -> Result<()> { None, None, push, - false, + no_cache, &[], ) .await?; @@ -895,7 +895,7 @@ async fn build_tuwunel(push: bool, deploy: bool) -> Result<()> { Ok(()) } -async fn build_integration(push: bool, deploy: bool) -> Result<()> { +async fn build_integration(push: bool, deploy: bool, no_cache: bool) -> Result<()> { let env = get_build_env().await?; let sunbeam_dir = crate::config::get_repo_root(); let integration_service_dir = sunbeam_dir.join("integration-service"); @@ -940,7 +940,7 @@ async fn build_integration(push: bool, deploy: bool) -> Result<()> { None, None, push, - false, + no_cache, &[], ) .await; @@ -957,7 +957,7 @@ async fn build_integration(push: bool, deploy: bool) -> Result<()> { Ok(()) } -async fn build_kratos_admin(push: bool, deploy: bool) -> Result<()> { +async fn build_kratos_admin(push: bool, deploy: bool, no_cache: bool) -> Result<()> { let env = get_build_env().await?; let kratos_admin_dir = crate::config::get_repo_root().join("kratos-admin"); if !kratos_admin_dir.is_dir() { @@ -978,7 +978,7 @@ async fn build_kratos_admin(push: bool, deploy: bool) -> Result<()> { None, None, push, - false, + no_cache, &[], ) .await?; @@ -989,7 +989,7 @@ async fn build_kratos_admin(push: bool, deploy: bool) -> Result<()> { Ok(()) } -async fn build_meet(push: bool, deploy: bool) -> Result<()> { +async fn build_meet(push: bool, deploy: bool, no_cache: bool) -> Result<()> { let env = get_build_env().await?; let meet_dir = crate::config::get_repo_root().join("meet"); if !meet_dir.is_dir() { @@ -1009,7 +1009,7 @@ async fn build_meet(push: bool, deploy: bool) -> Result<()> { Some("backend-production"), None, push, - false, + no_cache, &[], ) .await?; @@ -1035,7 +1035,7 @@ async fn build_meet(push: bool, deploy: bool) -> Result<()> { Some("frontend-production"), Some(&build_args), push, - false, + no_cache, &[], ) .await?; @@ -1053,7 +1053,7 @@ async fn build_meet(push: bool, deploy: bool) -> Result<()> { Ok(()) } -async fn build_people(push: bool, deploy: bool) -> Result<()> { +async fn build_people(push: bool, deploy: bool, no_cache: bool) -> Result<()> { let env = get_build_env().await?; let people_dir = crate::config::get_repo_root().join("people"); if !people_dir.is_dir() { @@ -1109,7 +1109,7 @@ async fn build_people(push: bool, deploy: bool) -> Result<()> { Some("frontend-production"), Some(&build_args), push, - false, + no_cache, &[], ) .await?; @@ -1160,7 +1160,7 @@ const MESSAGES_COMPONENTS: &[(&str, &str, &str, Option<&str>)] = &[ ), ]; -async fn build_messages(what: &str, push: bool, deploy: bool) -> Result<()> { +async fn build_messages(what: &str, push: bool, deploy: bool, no_cache: bool) -> Result<()> { let env = get_build_env().await?; let messages_dir = crate::config::get_repo_root().join("messages"); if !messages_dir.is_dir() { @@ -1214,7 +1214,7 @@ async fn build_messages(what: &str, push: bool, deploy: bool) -> Result<()> { *target, None, push, - false, + no_cache, &cleanup_paths, ) .await?; @@ -1257,6 +1257,7 @@ async fn build_la_suite_frontend( namespace: &str, push: bool, deploy: bool, + no_cache: bool, ) -> Result<()> { let env = get_build_env().await?; @@ -1307,7 +1308,7 @@ async fn build_la_suite_frontend( Some("frontend-production"), Some(&build_args), push, - false, + no_cache, &[], ) .await?; @@ -1439,7 +1440,7 @@ async fn patch_dockerfile_uv( Ok((patched_df, cleanup)) } -async fn build_projects(push: bool, deploy: bool) -> Result<()> { +async fn build_projects(push: bool, deploy: bool, no_cache: bool) -> Result<()> { let env = get_build_env().await?; let projects_dir = crate::config::get_repo_root().join("projects"); if !projects_dir.is_dir() { @@ -1457,7 +1458,7 @@ async fn build_projects(push: bool, deploy: bool) -> Result<()> { None, None, push, - false, + no_cache, &[], ) .await?; @@ -1468,7 +1469,36 @@ async fn build_projects(push: bool, deploy: bool) -> Result<()> { Ok(()) } -async fn build_calendars(push: bool, deploy: bool) -> Result<()> { +async fn build_sol(push: bool, deploy: bool, no_cache: bool) -> Result<()> { + let env = get_build_env().await?; + let sol_dir = crate::config::get_repo_root().join("sol"); + if !sol_dir.is_dir() { + return Err(SunbeamError::build(format!("Sol source not found at {}", sol_dir.display()))); + } + + let image = format!("{}/studio/sol:latest", env.registry); + step(&format!("Building sol -> {image} ...")); + + build_image( + &env, + &image, + &sol_dir.join("Dockerfile"), + &sol_dir, + None, + None, + push, + no_cache, + &[], + ) + .await?; + + if deploy { + deploy_rollout(&env, &["sol"], "matrix", 120, None).await?; + } + Ok(()) +} + +async fn build_calendars(push: bool, deploy: bool, no_cache: bool) -> Result<()> { let env = get_build_env().await?; let cal_dir = crate::config::get_repo_root().join("calendars"); if !cal_dir.is_dir() { @@ -1518,7 +1548,7 @@ async fn build_calendars(push: bool, deploy: bool) -> Result<()> { Some("backend-production"), None, push, - false, + no_cache, &cleanup, ) .await?; @@ -1535,7 +1565,7 @@ async fn build_calendars(push: bool, deploy: bool) -> Result<()> { None, None, push, - false, + no_cache, &[], ) .await?; @@ -1573,7 +1603,7 @@ async fn build_calendars(push: bool, deploy: bool) -> Result<()> { Some("frontend-production"), Some(&build_args), push, - false, + no_cache, &[], ) .await?; @@ -1601,12 +1631,12 @@ async fn build_calendars(push: bool, deploy: bool) -> Result<()> { // --------------------------------------------------------------------------- /// Build an image. Pass push=true to push, deploy=true to also apply + rollout. -pub async fn cmd_build(what: &BuildTarget, push: bool, deploy: bool) -> Result<()> { +pub async fn cmd_build(what: &BuildTarget, push: bool, deploy: bool, no_cache: bool) -> Result<()> { match what { - BuildTarget::Proxy => build_proxy(push, deploy).await, - BuildTarget::Integration => build_integration(push, deploy).await, - BuildTarget::KratosAdmin => build_kratos_admin(push, deploy).await, - BuildTarget::Meet => build_meet(push, deploy).await, + BuildTarget::Proxy => build_proxy(push, deploy, no_cache).await, + BuildTarget::Integration => build_integration(push, deploy, no_cache).await, + BuildTarget::KratosAdmin => build_kratos_admin(push, deploy, no_cache).await, + BuildTarget::Meet => build_meet(push, deploy, no_cache).await, BuildTarget::DocsFrontend => { let repo_dir = crate::config::get_repo_root().join("docs"); build_la_suite_frontend( @@ -1620,22 +1650,24 @@ pub async fn cmd_build(what: &BuildTarget, push: bool, deploy: bool) -> Result<( "lasuite", push, deploy, + no_cache, ) .await } - BuildTarget::PeopleFrontend | BuildTarget::People => build_people(push, deploy).await, - BuildTarget::Messages => build_messages("messages", push, deploy).await, - BuildTarget::MessagesBackend => build_messages("messages-backend", push, deploy).await, - BuildTarget::MessagesFrontend => build_messages("messages-frontend", push, deploy).await, - BuildTarget::MessagesMtaIn => build_messages("messages-mta-in", push, deploy).await, - BuildTarget::MessagesMtaOut => build_messages("messages-mta-out", push, deploy).await, - BuildTarget::MessagesMpa => build_messages("messages-mpa", push, deploy).await, + BuildTarget::PeopleFrontend | BuildTarget::People => build_people(push, deploy, no_cache).await, + BuildTarget::Messages => build_messages("messages", push, deploy, no_cache).await, + BuildTarget::MessagesBackend => build_messages("messages-backend", push, deploy, no_cache).await, + BuildTarget::MessagesFrontend => build_messages("messages-frontend", push, deploy, no_cache).await, + BuildTarget::MessagesMtaIn => build_messages("messages-mta-in", push, deploy, no_cache).await, + BuildTarget::MessagesMtaOut => build_messages("messages-mta-out", push, deploy, no_cache).await, + BuildTarget::MessagesMpa => build_messages("messages-mpa", push, deploy, no_cache).await, BuildTarget::MessagesSocksProxy => { - build_messages("messages-socks-proxy", push, deploy).await + build_messages("messages-socks-proxy", push, deploy, no_cache).await } - BuildTarget::Tuwunel => build_tuwunel(push, deploy).await, - BuildTarget::Calendars => build_calendars(push, deploy).await, - BuildTarget::Projects => build_projects(push, deploy).await, + BuildTarget::Tuwunel => build_tuwunel(push, deploy, no_cache).await, + BuildTarget::Calendars => build_calendars(push, deploy, no_cache).await, + BuildTarget::Projects => build_projects(push, deploy, no_cache).await, + BuildTarget::Sol => build_sol(push, deploy, no_cache).await, } } @@ -1730,6 +1762,7 @@ mod tests { BuildTarget::Tuwunel, BuildTarget::Calendars, BuildTarget::Projects, + BuildTarget::Sol, ]; for t in &targets { let s = t.to_string();