From 58b1daaec1cb144a3f2442be3bd684eb06619797 Mon Sep 17 00:00:00 2001
From: theMackabu <theMackabu@gmail.com>
Date: Sun, 22 Oct 2023 11:01:36 -0700
Subject: [PATCH] message output from server now with colors

---
 Maidfile.toml                        |   6 +-
 crates/maid/client/src/server/cli.rs |   4 +-
 crates/maid/server/src/docker/run.rs | 205 ++++++++++++++-------------
 scripts/build.toml                   |   8 +-
 4 files changed, 114 insertions(+), 109 deletions(-)

diff --git a/Maidfile.toml b/Maidfile.toml
index ee6b143..db36e24 100644
--- a/Maidfile.toml
+++ b/Maidfile.toml
@@ -22,7 +22,7 @@ VERSION='1.1.0'
 info = "Build binaries"
 depends = ["clean"]
 script = [
-   "cargo zigbuild --release", 
+   "cargo zigbuild --release --color always", 
    "mv target/release/maid bin/maid",
    "mv target/release/maid_server bin/maid_server",
    "mv target/release/exit_test bin/exit_test"
@@ -37,10 +37,10 @@ target = [
 ]
 
 [tasks.build.remote]
-silent = true
+silent = false
 exclusive = false
 shell = "/bin/bash"
-image = "rust:1.73"
+image = "messense/cargo-zigbuild:latest"
 push = ["crates", "Cargo.toml", "Cargo.lock"]
 pull = "bin"
 
diff --git a/crates/maid/client/src/server/cli.rs b/crates/maid/client/src/server/cli.rs
index ef637b0..43eb731 100644
--- a/crates/maid/client/src/server/cli.rs
+++ b/crates/maid/client/src/server/cli.rs
@@ -128,7 +128,9 @@ pub fn remote(task: Task) {
             Ok(Message::Text(text)) => {
                 if let Ok(Websocket { time: _, data, level }) = serde_json::from_str::<Websocket>(&text) {
                     data.get("message").and_then(|m| m.as_str()).map(|msg| {
-                        if !msg.is_empty() {
+                        if level.as_str() == "none" {
+                            print!("{msg}");
+                        } else {
                             crate::log!(level.as_str(), "{}", msg);
                         }
                     });
diff --git a/crates/maid/server/src/docker/run.rs b/crates/maid/server/src/docker/run.rs
index d6c3fd7..ddad9ca 100644
--- a/crates/maid/server/src/docker/run.rs
+++ b/crates/maid/server/src/docker/run.rs
@@ -1,24 +1,24 @@
-use crate::table;
 use crate::structs::ConnectionData;
+use crate::table;
 
-use bytes::Bytes;
-use bollard::{Docker, errors::Error};
-use macros_rs::{string, fmtstr, str};
-use warp::ws::{Message, WebSocket};
+use bollard::container::{Config, DownloadFromContainerOptions, RemoveContainerOptions, UploadToContainerOptions};
+use bollard::exec::{CreateExecOptions, StartExecResults};
 use bollard::image::CreateImageOptions;
-use futures_util::{SinkExt, StreamExt};
+use bollard::{errors::Error, Docker};
+use bytes::Bytes;
+use flate2::{write::GzEncoder, Compression};
 use futures_core::Stream;
 use futures_util::stream::{SplitSink, SplitStream, TryStreamExt};
-use bollard::exec::{CreateExecOptions, StartExecResults};
-use bollard::container::{Config, RemoveContainerOptions, UploadToContainerOptions, DownloadFromContainerOptions};
+use futures_util::{SinkExt, StreamExt};
+use macros_rs::{fmtstr, str, string};
 use std::default::Default;
-use warp::hyper::Body;
-use std::sync::Arc;
-use tokio::sync::Mutex;
+use std::io::Write;
 use std::path::PathBuf;
+use std::sync::Arc;
 use text_placeholder::Template;
-use flate2::{Compression, write::GzEncoder};
-use std::io::Write;
+use tokio::sync::Mutex;
+use warp::hyper::Body;
+use warp::ws::{Message, WebSocket};
 
 pub async fn concat_byte_stream<S>(s: S) -> Result<Vec<u8>, Error>
 where
@@ -32,13 +32,13 @@ where
 }
 
 // add error handling to all the unwraps
-pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSocket>, docker: Docker) -> Result<(), Box<dyn std::error::Error + 'static>> { 
+pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSocket>, docker: Docker) -> Result<(), Box<dyn std::error::Error + 'static>> {
     let mut parsed: Option<ConnectionData> = None;
     let tx_ref = Arc::new(Mutex::new(tx));
-    
+
     while parsed.is_none() {
         if let Some(result) = rx.next().await {
-            let msg = result.unwrap();              
+            let msg = result.unwrap();
             match serde_json::from_str::<ConnectionData>(msg.to_str().unwrap()) {
                 Ok(value) => {
                     parsed = Some(value);
@@ -49,10 +49,10 @@ pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSock
             }
         }
     }
-        
+
     let parsed = parsed.unwrap();
     let name = &parsed.info.name;
-    
+
     println!("creating container for task [{name}]");
     docker
         .create_image(
@@ -65,25 +65,22 @@ pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSock
         )
         .for_each(|msg| {
             let tx_ref = Arc::clone(&tx_ref);
-            
+
             async move {
                 let msg = msg.as_ref().expect("Failed to get CreateImageInfo");
-                let formatted = format!(
-                    "{} {}",
-                    msg.status.clone().unwrap_or_else(|| string!("Waiting")),
-                    msg.progress.clone().unwrap_or_else(|| string!(""))
-                );
-                
+                let formatted = format!("{} {}", msg.status.clone().unwrap_or_else(|| string!("Waiting")), msg.progress.clone().unwrap_or_else(|| string!("")));
+
                 let mut tx_lock = tx_ref.lock().await;
-                tx_lock.send(Message::text(
-                    serde_json::to_string(&serde_json::json!({
-                        "level": "docker",
-                        "time": chrono::Utc::now().timestamp_millis(),
-                        "data": { "message": formatted },
-                    }))
-                    .unwrap(),
-                ))
-                .await;   
+                tx_lock
+                    .send(Message::text(
+                        serde_json::to_string(&serde_json::json!({
+                            "level": "docker",
+                            "time": chrono::Utc::now().timestamp_millis(),
+                            "data": { "message": formatted },
+                        }))
+                        .unwrap(),
+                    ))
+                    .await;
             }
         })
         .await;
@@ -96,60 +93,58 @@ pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSock
 
     let id = docker.create_container::<&str, String>(None, config).await?.id;
     println!("created container");
-    
+
     docker.start_container::<String>(&id, None).await?;
     println!("started container");
-    
+
     let tx_ref = Arc::clone(&tx_ref);
     let mut tx_lock = tx_ref.lock().await;
-    
-    tx_lock.send(Message::text(
-        serde_json::to_string(&serde_json::json!({
-            "level": "success",
-            "time": chrono::Utc::now().timestamp_millis(),
-            "data": { "binary": true },
-        }))
-        .unwrap(),
-    ))
-    .await
-    .unwrap();
-    
+
+    tx_lock
+        .send(Message::text(
+            serde_json::to_string(&serde_json::json!({
+                "level": "success",
+                "time": chrono::Utc::now().timestamp_millis(),
+                "data": { "binary": true },
+            }))
+            .unwrap(),
+        ))
+        .await
+        .unwrap();
+
     if let Some(result) = rx.next().await {
         println!("received message: binary");
-    
+
         let msg = result.unwrap();
-        fn bytes_to_body(bytes: &[u8]) -> Body {
-            Body::from(bytes.to_vec())
-        }
-        
+        fn bytes_to_body(bytes: &[u8]) -> Body { Body::from(bytes.to_vec()) }
+
         // note: this `Result` may be an `Err` variant, which should be handled
         // help: use `let _ = ...` to ignore the resulting value
-        docker.upload_to_container(&id, Some(UploadToContainerOptions{ path: "/opt", ..Default::default() }), bytes_to_body(&msg.as_bytes())).await;
+        docker
+            .upload_to_container(&id, Some(UploadToContainerOptions { path: "/opt", ..Default::default() }), bytes_to_body(&msg.as_bytes()))
+            .await;
         println!("wrote tarfile to container");
     }
-    
+
     let dependencies = match &parsed.maidfile.tasks[&parsed.info.name].depends {
-        Some(deps) => {     
+        Some(deps) => {
             let mut dep_script: Vec<String> = vec![];
             for item in deps.iter() {
                 dep_script.push(
                     parsed.maidfile.tasks[item]
                         .script
                         .as_array()
-                        .map(|arr| {
-                            arr.iter()
-                                .map(|val| val.as_str().unwrap_or_default())
-                                .collect::<Vec<_>>()
-                                .join("\n")
-                        })
-                        .unwrap_or_default()
+                        .map(|arr| arr.iter().map(|val| val.as_str().unwrap_or_default()).collect::<Vec<_>>().join("\n"))
+                        .unwrap_or_default(),
                 );
-            };
+            }
             dep_script.join("\n")
         }
-        None => { string!("") }
+        None => {
+            string!("")
+        }
     };
-    
+
     // move common things such as structs and helpers to seperate crate
     let table = table::create(parsed.maidfile.clone(), &parsed.info.args, PathBuf::new().join("/opt"));
     let script = Template::new_with_placeholder(str!(parsed.info.script.join("\n")), "%{", "}").fill_with_hashmap(&table);
@@ -161,7 +156,11 @@ pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSock
             CreateExecOptions {
                 attach_stdout: Some(true),
                 attach_stderr: Some(true),
-                cmd: Some(vec![str!(parsed.info.remote.shell), "-c", fmtstr!("cd /opt && touch script.sh && echo '{dependencies}\n{script}' > script.sh && chmod +x script.sh && ./script.sh")]),
+                cmd: Some(vec![
+                    str!(parsed.info.remote.shell),
+                    "-c",
+                    fmtstr!("cd /opt && touch script.sh && echo '{dependencies}\n{script}' > script.sh && chmod +x script.sh && ./script.sh"),
+                ]),
                 ..Default::default()
             },
         )
@@ -169,59 +168,63 @@ pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSock
         .id;
 
     if let StartExecResults::Attached { mut output, .. } = docker.start_exec(&exec, None).await? {
-        tx_lock.send(Message::text(
-            serde_json::to_string(&serde_json::json!({
-                "level": "build",
-                "time": chrono::Utc::now().timestamp_millis(),
-                "data": { "message": "waiting for build to finish..." },
-            }))
-            .unwrap(),
-        ))
-        .await
-        .unwrap();
-        
+        tx_lock
+            .send(Message::text(
+                serde_json::to_string(&serde_json::json!({
+                    "level": "build",
+                    "time": chrono::Utc::now().timestamp_millis(),
+                    "data": { "message": "waiting for build to finish..." },
+                }))
+                .unwrap(),
+            ))
+            .await
+            .unwrap();
+
         while let Some(Ok(msg)) = output.next().await {
             if !parsed.info.remote.silent {
-                let parsed = format!("{msg}");   
-                if parsed != "" {
-                    tx_lock.send(Message::text(
+                tx_lock
+                    .send(Message::text(
                         serde_json::to_string(&serde_json::json!({
-                            "level": "build",
+                            "level": "none",
                             "time": chrono::Utc::now().timestamp_millis(),
-                            "data": { "message": parsed.trim() },
+                            "data": { "message": msg.to_string() },
                         }))
                         .unwrap(),
                     ))
                     .await
                     .unwrap();
-                }
             }
         }
     }
-    
-    let res = docker.download_from_container(&id, Some(DownloadFromContainerOptions{ path: fmtstr!("/opt/{}", parsed.info.remote.pull.clone()) }));
+
+    let res = docker.download_from_container(
+        &id,
+        Some(DownloadFromContainerOptions {
+            path: fmtstr!("/opt/{}", parsed.info.remote.pull.clone()),
+        }),
+    );
     let bytes = concat_byte_stream(res).await?;
     let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
-    
+
     encoder.write_all(&bytes)?;
     let compressed_data = encoder.finish()?;
-    
+
     tx_lock.send(Message::binary(compressed_data)).await.unwrap();
     println!("sent message: binary, from [{}]", parsed.info.remote.pull);
-    
-    tx_lock.send(Message::text(
-        serde_json::to_string(&serde_json::json!({
-            "level": "success",
-            "time": chrono::Utc::now().timestamp_millis(),
-            "data": { "done": true, "message": "" },
-        }))
-        .unwrap(),
-    ))
-    .await
-    .unwrap();
+
+    tx_lock
+        .send(Message::text(
+            serde_json::to_string(&serde_json::json!({
+                "level": "success",
+                "time": chrono::Utc::now().timestamp_millis(),
+                "data": { "done": true },
+            }))
+            .unwrap(),
+        ))
+        .await
+        .unwrap();
     println!("sent message: [done]");
-    
-    
+
     println!("deleted old container");
     // delete container if socket closed
     docker.remove_container(&id, Some(RemoveContainerOptions { force: true, ..Default::default() })).await?;
diff --git a/scripts/build.toml b/scripts/build.toml
index 1ee5bd0..f1e7544 100644
--- a/scripts/build.toml
+++ b/scripts/build.toml
@@ -8,25 +8,25 @@ script = [
    "mkdir build",
    
    # build linux (x86_64)
-   "cargo zigbuild -r -p maid",
+   "cargo zigbuild -r -p maid --color always",
    "mv target/release/maid build/maid",
    "zip build/maid_%{env.VERSION}_linux_amd64.zip build/maid",
    "rm build/maid",
    
    # build windows (x86_64)
-   "cargo zigbuild -r -p maid --target x86_64-pc-windows-gnu",
+   "cargo zigbuild -r -p maid --target x86_64-pc-windows-gnu --color always",
    "mv target/x86_64-pc-windows-gnu/release/maid.exe build/maid.exe",
    "zip build/maid_%{env.VERSION}_windows_amd64.zip build/maid.exe",
    "rm build/maid.exe",
    
    # build macos (x86_64)
-   "cargo zigbuild -r -p maid --target x86_64-apple-darwin", 
+   "cargo zigbuild -r -p maid --target x86_64-apple-darwin --color always", 
    "mv target/x86_64-apple-darwin/release/maid build/maid",
    "zip build/maid_%{env.VERSION}_darwin_amd64.zip build/maid",
    "rm build/maid",
    
    # build macos (aarch64)
-   "cargo zigbuild -r -p maid --target aarch64-apple-darwin", 
+   "cargo zigbuild -r -p maid --target aarch64-apple-darwin --color always", 
    "mv target/aarch64-apple-darwin/release/maid build/maid",
    "zip build/maid_%{env.VERSION}_darwin_arm.zip build/maid",
    "rm build/maid",
-- 
GitLab