Prevent Windows console window for yaaknode and yaakprotoc

Add new_xplatform_command() helper in yaak-common that creates a
tokio::process::Command with CREATE_NO_WINDOW flag set on Windows.

Also converts git commands to async for consistency.
This commit is contained in:
Gregory Schier
2026-01-11 15:07:56 -08:00
parent 72a7e6963d
commit 42143249a2
15 changed files with 102 additions and 69 deletions

View File

@@ -1,38 +1,24 @@
use crate::error::Error::GitNotFound;
use crate::error::Result;
use std::path::Path;
use std::process::{Command, Stdio};
use std::process::Stdio;
use tokio::process::Command;
use yaak_common::command::new_xplatform_command;
use crate::error::Error::GitNotFound;
#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt;
#[cfg(target_os = "windows")]
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
pub(crate) fn new_binary_command(dir: &Path) -> Result<Command> {
pub(crate) async fn new_binary_command(dir: &Path) -> Result<Command> {
// 1. Probe that `git` exists and is runnable
let mut probe = Command::new("git");
let mut probe = new_xplatform_command("git");
probe.arg("--version").stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
#[cfg(target_os = "windows")]
{
probe.creation_flags(CREATE_NO_WINDOW);
}
let status = probe.status().map_err(|_| GitNotFound)?;
let status = probe.status().await.map_err(|_| GitNotFound)?;
if !status.success() {
return Err(GitNotFound);
}
// 2. Build the reusable git command
let mut cmd = Command::new("git");
let mut cmd = new_xplatform_command("git");
cmd.arg("-C").arg(dir);
#[cfg(target_os = "windows")]
{
cmd.creation_flags(CREATE_NO_WINDOW);
}
Ok(cmd)
}

View File

@@ -3,8 +3,9 @@ use crate::error::Error::GenericError;
use log::info;
use std::path::Path;
pub fn git_commit(dir: &Path, message: &str) -> crate::error::Result<()> {
let out = new_binary_command(dir)?.args(["commit", "--message", message]).output()?;
pub async fn git_commit(dir: &Path, message: &str) -> crate::error::Result<()> {
let out =
new_binary_command(dir).await?.args(["commit", "--message", message]).output().await?;
let stdout = String::from_utf8_lossy(&out.stdout);
let stderr = String::from_utf8_lossy(&out.stderr);

View File

@@ -1,9 +1,9 @@
use crate::binary::new_binary_command;
use crate::error::Error::GenericError;
use crate::error::Result;
use std::io::Write;
use std::path::Path;
use std::process::Stdio;
use tokio::io::AsyncWriteExt;
use url::Url;
pub async fn git_add_credential(
@@ -18,7 +18,8 @@ pub async fn git_add_credential(
let host = url.host_str().unwrap();
let path = Some(url.path());
let mut child = new_binary_command(dir)?
let mut child = new_binary_command(dir)
.await?
.args(["credential", "approve"])
.stdin(Stdio::piped())
.stdout(Stdio::null())
@@ -26,19 +27,21 @@ pub async fn git_add_credential(
{
let stdin = child.stdin.as_mut().unwrap();
writeln!(stdin, "protocol={}", protocol)?;
writeln!(stdin, "host={}", host)?;
stdin.write_all(format!("protocol={}\n", protocol).as_bytes()).await?;
stdin.write_all(format!("host={}\n", host).as_bytes()).await?;
if let Some(path) = path {
if !path.is_empty() {
writeln!(stdin, "path={}", path.trim_start_matches('/'))?;
stdin
.write_all(format!("path={}\n", path.trim_start_matches('/')).as_bytes())
.await?;
}
}
writeln!(stdin, "username={}", username)?;
writeln!(stdin, "password={}", password)?;
writeln!(stdin)?; // blank line terminator
stdin.write_all(format!("username={}\n", username).as_bytes()).await?;
stdin.write_all(format!("password={}\n", password).as_bytes()).await?;
stdin.write_all(b"\n").await?; // blank line terminator
}
let status = child.wait()?;
let status = child.wait().await?;
if !status.success() {
return Err(GenericError("Failed to approve git credential".to_string()));
}

View File

@@ -3,10 +3,12 @@ use crate::error::Error::GenericError;
use crate::error::Result;
use std::path::Path;
pub fn git_fetch_all(dir: &Path) -> Result<()> {
let out = new_binary_command(dir)?
pub async fn git_fetch_all(dir: &Path) -> Result<()> {
let out = new_binary_command(dir)
.await?
.args(["fetch", "--all", "--prune", "--tags"])
.output()
.await
.map_err(|e| GenericError(format!("failed to run git pull: {e}")))?;
let stdout = String::from_utf8_lossy(&out.stdout);
let stderr = String::from_utf8_lossy(&out.stderr);

View File

@@ -17,17 +17,25 @@ pub enum PullResult {
NeedsCredentials { url: String, error: Option<String> },
}
pub fn git_pull(dir: &Path) -> Result<PullResult> {
let repo = open_repo(dir)?;
let branch_name = get_current_branch_name(&repo)?;
let remote = get_default_remote_in_repo(&repo)?;
let remote_name = remote.name().ok_or(GenericError("Failed to get remote name".to_string()))?;
let remote_url = remote.url().ok_or(GenericError("Failed to get remote url".to_string()))?;
pub async fn git_pull(dir: &Path) -> Result<PullResult> {
// Extract all git2 data before any await points (git2 types are not Send)
let (branch_name, remote_name, remote_url) = {
let repo = open_repo(dir)?;
let branch_name = get_current_branch_name(&repo)?;
let remote = get_default_remote_in_repo(&repo)?;
let remote_name =
remote.name().ok_or(GenericError("Failed to get remote name".to_string()))?.to_string();
let remote_url =
remote.url().ok_or(GenericError("Failed to get remote url".to_string()))?.to_string();
(branch_name, remote_name, remote_url)
};
let out = new_binary_command(dir)?
let out = new_binary_command(dir)
.await?
.args(["pull", &remote_name, &branch_name])
.env("GIT_TERMINAL_PROMPT", "0")
.output()
.await
.map_err(|e| GenericError(format!("failed to run git pull: {e}")))?;
let stdout = String::from_utf8_lossy(&out.stdout);

View File

@@ -17,17 +17,25 @@ pub enum PushResult {
NeedsCredentials { url: String, error: Option<String> },
}
pub fn git_push(dir: &Path) -> Result<PushResult> {
let repo = open_repo(dir)?;
let branch_name = get_current_branch_name(&repo)?;
let remote = get_default_remote_for_push_in_repo(&repo)?;
let remote_name = remote.name().ok_or(GenericError("Failed to get remote name".to_string()))?;
let remote_url = remote.url().ok_or(GenericError("Failed to get remote url".to_string()))?;
pub async fn git_push(dir: &Path) -> Result<PushResult> {
// Extract all git2 data before any await points (git2 types are not Send)
let (branch_name, remote_name, remote_url) = {
let repo = open_repo(dir)?;
let branch_name = get_current_branch_name(&repo)?;
let remote = get_default_remote_for_push_in_repo(&repo)?;
let remote_name =
remote.name().ok_or(GenericError("Failed to get remote name".to_string()))?.to_string();
let remote_url =
remote.url().ok_or(GenericError("Failed to get remote url".to_string()))?.to_string();
(branch_name, remote_name, remote_url)
};
let out = new_binary_command(dir)?
let out = new_binary_command(dir)
.await?
.args(["push", &remote_name, &branch_name])
.env("GIT_TERMINAL_PROMPT", "0")
.output()
.await
.map_err(|e| GenericError(format!("failed to run git push: {e}")))?;
let stdout = String::from_utf8_lossy(&out.stdout);