Selaa lähdekoodia

feat(cli): add build subcommand for standalone image build

sm121 build --recipe <name> --build-node <host>
  - Shares handle_build() code with serve --build-node
  - Respects --force-build flag
  - Useful for checking long builds (instanttensor sdist, etc.) without
    committing to a full serve cycle

Usage:
  cargo run -- build --recipe deepseek-v4-flash-dspark --build-node dgx1.lan
Alexander Korolev 3 kuukautta sitten
vanhempi
sitoutus
097f22082f
4 muutettua tiedostoa jossa 51 lisäystä ja 2 poistoa
  1. 16 0
      src/cli.rs
  2. 1 1
      src/docker/build.rs
  3. 6 1
      src/docker/mod.rs
  4. 28 0
      src/main.rs

+ 16 - 0
src/cli.rs

@@ -152,6 +152,22 @@ pub enum Commands {
         command: Vec<String>,
     },
 
+    /// Build Docker image for a recipe on a remote node
+    Build {
+        #[arg(long, required = true, help = "Recipe name")]
+        recipe: String,
+
+        #[arg(long, required = true, help = "Node to build on (e.g. dgx1.lan)")]
+        build_node: String,
+
+        #[arg(long, default_value = "auto", help = "Cluster name")]
+        cluster: String,
+
+        /// Force rebuild even if checksum matches.
+        #[arg(long, default_value = "false")]
+        force_build: bool,
+    },
+
     /// Power on/off nodes via Home Assistant (smart plugs / Wake-on-LAN).
     Power {
         #[command(subcommand)]

+ 1 - 1
src/docker/build.rs

@@ -1,5 +1,5 @@
-use crate::config::{Config, Cluster, Node, Recipe};
 use super::ServeOpts;
+use crate::config::{Cluster, Config, Node, Recipe};
 use crate::ssh::run_ssh;
 use anyhow::Result;
 use sha2::{Digest, Sha256};

+ 6 - 1
src/docker/mod.rs

@@ -92,7 +92,12 @@ pub async fn serve(
         }
         if !opts.dry_run {
             crate::docker::build::handle_build(
-                config, &cluster, recipe_name, &recipe, &opts, build_node_name,
+                config,
+                &cluster,
+                recipe_name,
+                &recipe,
+                &opts,
+                build_node_name,
             )
             .await?;
         }

+ 28 - 0
src/main.rs

@@ -116,6 +116,34 @@ async fn main() -> Result<()> {
                     .await?;
             }
         }
+        Commands::Build {
+            recipe,
+            build_node,
+            cluster,
+            force_build,
+        } => {
+            let recipe_obj = crate::config::load_recipe(&recipe).await?;
+            let cluster = resolve_cluster_for_op(&config, &recipe, &cluster).await?;
+            let opts = docker::ServeOpts {
+                keep: false,
+                skip_before_serve: true,
+                dry_run,
+                auto_power_on: false,
+                sync_image: false,
+                sync_image_source: None,
+                build_node: Some(build_node.clone()),
+                force_build,
+            };
+            docker::build::handle_build(
+                &config,
+                &cluster,
+                &recipe,
+                &recipe_obj,
+                &opts,
+                &build_node,
+            )
+            .await?;
+        }
         Commands::Logs {
             recipe: recipe_name,
             cluster,