deploy and docker logs works ???

This commit is contained in:
tahahawa 2025-06-13 00:25:23 -04:00
parent f14c5e2b9d
commit 99e9aad687
3 changed files with 103 additions and 33 deletions

1
Cargo.lock generated
View File

@ -2791,6 +2791,7 @@ dependencies = [
"futures-util", "futures-util",
"inquire", "inquire",
"log", "log",
"serde_json",
"tokio", "tokio",
] ]

View File

@ -16,3 +16,4 @@ cargo = "0.88.0"
bollard = "0.19.0" bollard = "0.19.0"
current_platform = "0.2.0" current_platform = "0.2.0"
futures-util = "0.3.31" futures-util = "0.3.31"
serde_json = "1.0.140"

View File

@ -1,15 +1,17 @@
use bollard::models::ContainerCreateBody; use bollard::models::ContainerCreateBody;
use bollard::query_parameters::{ use bollard::query_parameters::{
CreateContainerOptionsBuilder, ListContainersOptionsBuilder, RemoveContainerOptions, CreateContainerOptionsBuilder, ListContainersOptionsBuilder, LogsOptions,
StartContainerOptions, WaitContainerOptions, RemoveContainerOptions, StartContainerOptions, WaitContainerOptions,
}; };
use bollard::secret::HostConfig; use bollard::secret::HostConfig;
use clap::{Args, Parser, Subcommand}; use clap::{Args, Parser, Subcommand};
use futures_util::StreamExt; use futures_util::{StreamExt, TryStreamExt};
use log::info; use log::info;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::fmt::format;
use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use tokio::fs;
#[derive(Parser)] #[derive(Parser)]
#[command(version, about, long_about = None, flatten_help = true, propagate_version = true)] #[command(version, about, long_about = None, flatten_help = true, propagate_version = true)]
@ -30,7 +32,6 @@ struct GlobalArgs {
#[derive(Subcommand, Clone, Debug)] #[derive(Subcommand, Clone, Debug)]
enum Commands { enum Commands {
Check(CheckArgs), Check(CheckArgs),
Package(PackageArgs),
Deploy(DeployArgs), Deploy(DeployArgs),
All(AllArgs), All(AllArgs),
} }
@ -41,12 +42,6 @@ struct CheckArgs {
check_script_path: String, check_script_path: String,
} }
#[derive(Args, Clone, Debug)]
struct PackageArgs {
#[arg(long, default_value_t = false)]
publish: bool,
}
#[derive(Args, Clone, Debug)] #[derive(Args, Clone, Debug)]
struct DeployArgs { struct DeployArgs {
#[arg(long, default_value_t = false)] #[arg(long, default_value_t = false)]
@ -64,9 +59,6 @@ struct AllArgs {
#[command(flatten)] #[command(flatten)]
check: CheckArgs, check: CheckArgs,
#[command(flatten)]
package: PackageArgs,
#[command(flatten)] #[command(flatten)]
deploy: DeployArgs, deploy: DeployArgs,
} }
@ -80,8 +72,12 @@ async fn main() {
.try_exists() .try_exists()
.expect("couldn't check if path exists"); .expect("couldn't check if path exists");
let harmony_bin_path: PathBuf;
match harmony_path { match harmony_path {
true => compile_harmony(cli_args.compile_method, None, cli_args.harmony_path.clone()).await, true => {
harmony_bin_path =
compile_harmony(cli_args.compile_method, None, cli_args.harmony_path.clone()).await
}
false => todo!("implement autodetect code"), false => todo!("implement autodetect code"),
} }
@ -112,12 +108,6 @@ async fn main() {
String::from_utf8(check_output.stderr).expect("couldn't parse from utf8") String::from_utf8(check_output.stderr).expect("couldn't parse from utf8")
); );
} }
Commands::Package(args) => {
if args.publish {
todo!("implement publish logic");
}
todo!("implement packaging logic");
}
Commands::Deploy(args) => { Commands::Deploy(args) => {
if args.staging { if args.staging {
todo!("implement staging deployment"); todo!("implement staging deployment");
@ -126,7 +116,15 @@ async fn main() {
if args.prod { if args.prod {
todo!("implement prod deployment"); todo!("implement prod deployment");
} }
todo!("implement deployment logic"); let deploy_output = Command::new(harmony_bin_path)
.arg("-y")
.arg("-a")
.output()
.expect("failed to run harmony deploy");
println!(
"deploy output: {}",
String::from_utf8(deploy_output.stdout).expect("couldn't parse from utf8")
);
} }
Commands::All(_args) => todo!( Commands::All(_args) => todo!(
"take all previous match arms and turn them into separate functions, and call them all one after the other" "take all previous match arms and turn them into separate functions, and call them all one after the other"
@ -146,7 +144,7 @@ async fn compile_harmony(
method: Option<CompileMethod>, method: Option<CompileMethod>,
platform: Option<String>, platform: Option<String>,
harmony_location: String, harmony_location: String,
) { ) -> PathBuf {
let platform = match platform { let platform = match platform {
Some(p) => p, Some(p) => p,
None => current_platform::CURRENT_PLATFORM.to_string(), None => current_platform::CURRENT_PLATFORM.to_string(),
@ -159,23 +157,24 @@ async fn compile_harmony(
Some(m) => m, Some(m) => m,
None => { None => {
if cargo_exists { if cargo_exists {
compile_cargo(gctx, harmony_location).await; return compile_cargo(gctx, harmony_location).await;
return;
} else { } else {
compile_docker(platform, harmony_location).await; return compile_docker(platform, harmony_location).await;
return;
} }
} }
}; };
match method { match method {
CompileMethod::LocalCargo => compile_cargo(gctx, harmony_location).await, CompileMethod::LocalCargo => return compile_cargo(gctx, harmony_location).await,
CompileMethod::Docker => compile_docker(platform, harmony_location).await, CompileMethod::Docker => return compile_docker(platform, harmony_location).await,
}; };
} }
async fn compile_cargo(gctx: cargo::util::context::GlobalContext, harmony_location: String) { async fn compile_cargo(
let _cargo_build = cargo::ops::compile( gctx: cargo::util::context::GlobalContext,
harmony_location: String,
) -> PathBuf {
let cargo_build = cargo::ops::compile(
&cargo::core::Workspace::new( &cargo::core::Workspace::new(
&Path::new(&format!("{}/Cargo.toml", harmony_location)) &Path::new(&format!("{}/Cargo.toml", harmony_location))
.canonicalize() .canonicalize()
@ -186,10 +185,24 @@ async fn compile_cargo(gctx: cargo::util::context::GlobalContext, harmony_locati
&cargo::ops::CompileOptions::new(&gctx, cargo::core::compiler::CompileMode::Build).unwrap(), &cargo::ops::CompileOptions::new(&gctx, cargo::core::compiler::CompileMode::Build).unwrap(),
) )
.expect("build didn't go successfully"); .expect("build didn't go successfully");
return;
let bin = cargo_build.binaries.first().expect("no binaries built");
let bin_out;
if let Some(ext) = bin.path.extension() {
bin_out = PathBuf::from(format!(
"{}/harmony.{}",
harmony_location,
ext.to_str().expect("couldn't convert OsStr to str")
));
let _copy_res = fs::copy(bin.path.clone(), bin_out.clone()).await;
} else {
bin_out = PathBuf::from(format!("{}/harmony", harmony_location));
let _copy_res = fs::copy(bin.path.clone(), bin_out.clone()).await;
}
return bin_out;
} }
async fn compile_docker(platform: String, harmony_location: String) { async fn compile_docker(platform: String, harmony_location: String) -> PathBuf {
let docker_client = let docker_client =
bollard::Docker::connect_with_local_defaults().expect("couldn't connect to docker"); bollard::Docker::connect_with_local_defaults().expect("couldn't connect to docker");
@ -226,6 +239,7 @@ async fn compile_docker(platform: String, harmony_location: String) {
"build".to_string(), "build".to_string(),
format!("--target={}", platform), format!("--target={}", platform),
"--release".to_string(), "--release".to_string(),
"--message-format=json".to_string(),
]), ]),
host_config: Some(HostConfig { host_config: Some(HostConfig {
binds: Some(vec![format!("{}:/mnt", harmony_location)]), binds: Some(vec![format!("{}:/mnt", harmony_location)]),
@ -251,6 +265,60 @@ async fn compile_docker(platform: String, harmony_location: String) {
.wait_container("harmony_build", Some(wait_options)) .wait_container("harmony_build", Some(wait_options))
.boxed(); .boxed();
let mut logs_stream = docker_client.logs(
"harmony_build",
Some(LogsOptions {
follow: true,
stdout: true,
stderr: true,
tail: "all".to_string(),
..Default::default()
}),
);
let mut bin_out = PathBuf::from(format!("{}/harmony", harmony_location));
while let Some(l) = logs_stream.next().await {
let l_str = l.expect("couldn't unwrap logoutput").to_string();
println!("{}", l_str);
let l_json: Option<serde_json::Value> =
serde_json::from_str(l_str.as_str()).unwrap_or(None);
match l_json {
Some(j) => match j.get("manifest_path") {
Some(p) => {
if p == "/mnt/Cargo.toml" {
let bin = PathBuf::from(format!(
"{}/{}",
harmony_location,
j.get("executable")
.expect("couldn't get json executable")
.as_str()
.expect("couldn't get json executable as str")
.replacen("/mnt/", "", 1)
));
if let Some(ext) = bin.extension() {
bin_out = PathBuf::from(format!(
"{}/harmony.{}",
harmony_location,
ext.to_str().expect("couldn't convert OsStr to str")
));
let _copy_res = fs::copy(bin.clone(), bin_out.clone()).await;
} else {
bin_out = PathBuf::from(format!("{}/harmony", harmony_location));
let _copy_res = fs::copy(bin.clone(), bin_out.clone()).await;
}
}
}
None => (),
},
None => (),
};
}
// wait until container is no longer running // wait until container is no longer running
while let Some(_) = wait.next().await {} while let Some(_) = wait.next().await {}
return bin_out;
} }