Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion crates/turborepo-daemon/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use miette::Diagnostic;
use thiserror::Error;
use tonic::{Code, IntoRequest, Status};
use tracing::info;
use turbopath::{AbsoluteSystemPathBuf, AnchoredSystemPath};
use turbopath::{AbsoluteSystemPathBuf, AnchoredSystemPath, PathError};
use turborepo_types::TaskInputs;

use super::{
Expand Down Expand Up @@ -241,6 +241,9 @@ pub enum DaemonError {
#[error("failed to setup cookie dir {1}: {0}")]
CookieDir(io::Error, AbsoluteSystemPathBuf),

#[error("unable to construct daemon paths: {0}")]
Path(#[from] PathError),

#[error("failed to determine package manager: {0}")]
PackageManager(#[from] turborepo_repository::package_manager::Error),

Expand Down
25 changes: 14 additions & 11 deletions crates/turborepo-daemon/src/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@

#[error("unable to use pid file: {0}")]
PidFile(#[from] PidFileError),

#[error("unable to construct daemon paths: {0}")]
Path(#[from] turbopath::PathError),
}

#[derive(Error, Debug)]
Expand Down Expand Up @@ -77,21 +80,21 @@
can_kill_server: bool,
repo_root: &AbsoluteSystemPath,
custom_turbo_json_path: Option<AbsoluteSystemPathBuf>,
) -> Self {
let paths = Paths::from_repo_root(repo_root);
Self {
) -> Result<Self, DaemonConnectorError> {
let paths = Paths::from_repo_root(repo_root)?;
Ok(Self {
can_start_server,
can_kill_server,
paths,
repo_root: repo_root.to_owned(),
custom_turbo_json_path,
}
})
}

const CONNECT_RETRY_MAX: usize = 5;
const CONNECT_TIMEOUT: Duration = Duration::from_secs(1);
const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(1);
const SOCKET_TIMEOUT: Duration = Duration::from_secs(5);

Check warning on line 97 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 5/10)

associated items `SOCKET_TIMEOUT` and `wait_for_socket` are never used

Check warning on line 97 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 3/10)

associated items `SOCKET_TIMEOUT` and `wait_for_socket` are never used

Check warning on line 97 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 7/10)

associated items `SOCKET_TIMEOUT` and `wait_for_socket` are never used

Check warning on line 97 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 2/10)

associated items `SOCKET_TIMEOUT` and `wait_for_socket` are never used

Check warning on line 97 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 1/10)

associated items `SOCKET_TIMEOUT` and `wait_for_socket` are never used

Check warning on line 97 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 4/10)

associated items `SOCKET_TIMEOUT` and `wait_for_socket` are never used

Check warning on line 97 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 8/10)

associated items `SOCKET_TIMEOUT` and `wait_for_socket` are never used

Check warning on line 97 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 6/10)

associated items `SOCKET_TIMEOUT` and `wait_for_socket` are never used

Check warning on line 97 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 9/10)

associated items `SOCKET_TIMEOUT` and `wait_for_socket` are never used

Check warning on line 97 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 10/10)

associated items `SOCKET_TIMEOUT` and `wait_for_socket` are never used
const SOCKET_ERROR_WAIT: Duration = Duration::from_millis(100);

/// Attempt, with retries, to:
Expand Down Expand Up @@ -438,7 +441,7 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum WaitAction {
/// Wait for the file to exist.
Exists,

Check warning on line 444 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 5/10)

variant `Exists` is never constructed

Check warning on line 444 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 3/10)

variant `Exists` is never constructed

Check warning on line 444 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 7/10)

variant `Exists` is never constructed

Check warning on line 444 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 2/10)

variant `Exists` is never constructed

Check warning on line 444 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 1/10)

variant `Exists` is never constructed

Check warning on line 444 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 4/10)

variant `Exists` is never constructed

Check warning on line 444 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 8/10)

variant `Exists` is never constructed

Check warning on line 444 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 6/10)

variant `Exists` is never constructed

Check warning on line 444 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 9/10)

variant `Exists` is never constructed

Check warning on line 444 in crates/turborepo-daemon/src/connector.rs

View workflow job for this annotation

GitHub Actions / Rust testing on windows (partition 10/10)

variant `Exists` is never constructed
/// Wait for the file to be deleted.
Deleted,
}
Expand Down Expand Up @@ -473,7 +476,7 @@
let tmp_dir = tempfile::tempdir().unwrap();
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();

let connector = DaemonConnector::new(false, false, &repo_root, None);
let connector = DaemonConnector::new(false, false, &repo_root, None).unwrap();
connector.paths.pid_file.ensure_dir().unwrap();
connector
.paths
Expand All @@ -491,7 +494,7 @@
async fn handles_missing_server_connect() {
let tmp_dir = tempfile::tempdir().unwrap();
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
let connector = DaemonConnector::new(false, false, &repo_root, None);
let connector = DaemonConnector::new(false, false, &repo_root, None).unwrap();

assert_matches!(
connector.connect().await,
Expand Down Expand Up @@ -537,7 +540,7 @@
async fn handles_kill_dead_server_missing_pid() {
let tmp_dir = tempfile::tempdir().unwrap();
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
let connector = DaemonConnector::new(false, false, &repo_root, None);
let connector = DaemonConnector::new(false, false, &repo_root, None).unwrap();

assert_matches!(
connector.kill_dead_server(Pid::from(usize::MAX)).await,
Expand All @@ -549,7 +552,7 @@
async fn handles_kill_dead_server_missing_process() {
let tmp_dir = tempfile::tempdir().unwrap();
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
let connector = DaemonConnector::new(false, false, &repo_root, None);
let connector = DaemonConnector::new(false, false, &repo_root, None).unwrap();

connector.paths.pid_file.ensure_dir().unwrap();
connector
Expand All @@ -575,7 +578,7 @@
async fn handles_kill_dead_server_wrong_process() {
let tmp_dir = tempfile::tempdir().unwrap();
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
let connector = DaemonConnector::new(false, false, &repo_root, None);
let connector = DaemonConnector::new(false, false, &repo_root, None).unwrap();

let proc = tokio::process::Command::new(NODE_EXE)
.stdout(Stdio::null())
Expand Down Expand Up @@ -613,7 +616,7 @@
async fn handles_kill_dead_server() {
let tmp_dir = tempfile::tempdir().unwrap();
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
let connector = DaemonConnector::new(false, true, &repo_root, None);
let connector = DaemonConnector::new(false, true, &repo_root, None).unwrap();

let proc = tokio::process::Command::new(NODE_EXE)
.stdout(Stdio::null())
Expand Down Expand Up @@ -754,7 +757,7 @@

let tmp_dir = tempfile::tempdir().unwrap();
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
let connector = DaemonConnector::new(false, false, &repo_root, None);
let connector = DaemonConnector::new(false, false, &repo_root, None).unwrap();

let mut client = Endpoint::try_from("http://[::]:50051")
.expect("this is a valid uri")
Expand Down
8 changes: 4 additions & 4 deletions crates/turborepo-daemon/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ mod test {
async fn test_stale_pid() {
let tmp_dir = tempfile::tempdir().unwrap();
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
let paths = Paths::from_repo_root(&repo_root);
let paths = Paths::from_repo_root(&repo_root).unwrap();
paths.pid_file.ensure_dir().unwrap();
// A pid that will never be running and is guaranteed not to be us
paths.pid_file.create_with_contents("100000").unwrap();
Expand All @@ -709,7 +709,7 @@ mod test {

let tmp_dir = tempfile::tempdir().unwrap();
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
let paths = Paths::from_repo_root(&repo_root);
let paths = Paths::from_repo_root(&repo_root).unwrap();
let socket_dir = paths.sock_file.parent().unwrap();
socket_dir.create_dir_all().unwrap();
std::fs::set_permissions(
Expand Down Expand Up @@ -750,7 +750,7 @@ mod test {
fn test_windows_socket_path_security_accepts_current_user() {
let tmp_dir = tempfile::tempdir().unwrap();
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
let paths = Paths::from_repo_root(&repo_root);
let paths = Paths::from_repo_root(&repo_root).unwrap();

secure_socket_dir(&paths.sock_file).unwrap();
validate_socket_owner(&paths.sock_file).unwrap();
Expand All @@ -764,7 +764,7 @@ mod test {
async fn test_existing_process() {
let tmp_dir = tempfile::tempdir().unwrap();
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
let paths = Paths::from_repo_root(&repo_root);
let paths = Paths::from_repo_root(&repo_root).unwrap();

#[cfg(windows)]
let node_bin = "node.exe";
Expand Down
70 changes: 45 additions & 25 deletions crates/turborepo-daemon/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ mod default_timeout_layer;
pub mod endpoint;
mod server;

use std::{collections::HashSet, sync::Arc};
use std::{collections::HashSet, path::PathBuf, sync::Arc};

pub use client::{DaemonClient, DaemonError};
pub use connector::{DaemonConnector, DaemonConnectorError};
pub use server::{CloseReason, FileWatching, TurboGrpcService};
use sha2::{Digest, Sha256};
use tokio::sync::broadcast;
use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf, AnchoredSystemPathBuf};
use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf, AnchoredSystemPathBuf, PathError};
use turborepo_repository::package_graph::PackageName;

/// Trait for watching package changes. Implemented by consumers who need
Expand Down Expand Up @@ -97,35 +97,37 @@ fn repo_hash(repo_root: &AbsoluteSystemPath) -> String {
}

#[cfg(unix)]
fn daemon_file_root(repo_hash: &str) -> AbsoluteSystemPathBuf {
fn daemon_file_root(repo_hash: &str) -> Result<AbsoluteSystemPathBuf, PathError> {
daemon_file_root_from_temp_dir(repo_hash, std::env::temp_dir())
}

#[cfg(unix)]
fn daemon_file_root_from_temp_dir(
repo_hash: &str,
temp_dir: PathBuf,
) -> Result<AbsoluteSystemPathBuf, PathError> {
let uid = unsafe { libc::geteuid() };
AbsoluteSystemPathBuf::new(std::env::temp_dir().to_str().expect("UTF-8 path"))
.expect("temp dir is valid")
Ok(AbsoluteSystemPathBuf::try_from(temp_dir)?
.join_component(format!("turbod-{uid}").as_str())
.join_component(repo_hash)
.join_component(repo_hash))
}

#[cfg(windows)]
fn daemon_file_root(repo_hash: &str) -> AbsoluteSystemPathBuf {
let root = std::env::var("LOCALAPPDATA").unwrap_or_else(|_| {
std::env::temp_dir()
.to_str()
.expect("UTF-8 path")
.to_string()
});

AbsoluteSystemPathBuf::new(&root)
.expect("daemon root is valid")
fn daemon_file_root(repo_hash: &str) -> Result<AbsoluteSystemPathBuf, PathError> {
let root = std::env::var("LOCALAPPDATA")
.map(PathBuf::from)
.unwrap_or_else(|_| std::env::temp_dir());

Ok(AbsoluteSystemPathBuf::try_from(root)?
.join_component("turbod")
.join_component(repo_hash)
.join_component(repo_hash))
}

#[cfg(not(any(unix, windows)))]
fn daemon_file_root(repo_hash: &str) -> AbsoluteSystemPathBuf {
AbsoluteSystemPathBuf::new(std::env::temp_dir().to_str().expect("UTF-8 path"))
.expect("temp dir is valid")
fn daemon_file_root(repo_hash: &str) -> Result<AbsoluteSystemPathBuf, PathError> {
Ok(AbsoluteSystemPathBuf::try_from(std::env::temp_dir())?
.join_component("turbod")
.join_component(repo_hash)
.join_component(repo_hash))
}

fn daemon_log_file_and_folder(
Expand All @@ -139,18 +141,36 @@ fn daemon_log_file_and_folder(
}

impl Paths {
pub fn from_repo_root(repo_root: &AbsoluteSystemPath) -> Self {
pub fn from_repo_root(repo_root: &AbsoluteSystemPath) -> Result<Self, PathError> {
let repo_hash = repo_hash(repo_root);
let daemon_root = daemon_file_root(&repo_hash);
let daemon_root = daemon_file_root(&repo_hash)?;
let (log_file, log_folder) = daemon_log_file_and_folder(repo_root, &repo_hash);
Self {
Ok(Self {
pid_file: daemon_root.join_component("turbod.pid"),
lock_file: daemon_root.join_component("turbod.lock"),
sock_file: daemon_root.join_component("turbod.sock"),
lsp_pid_file: daemon_root.join_component("lsp.pid"),
log_file,
log_folder,
}
})
}
}

#[cfg(test)]
mod tests {
#[cfg(unix)]
#[test]
fn daemon_file_root_rejects_non_utf8_temp_dir_without_panicking() {
use std::{ffi::OsString, os::unix::ffi::OsStringExt, path::PathBuf};

use turbopath::PathError;

let temp_dir = PathBuf::from(OsString::from_vec(b"/tmp/turbo-\xFF".to_vec()));

assert!(matches!(
super::daemon_file_root_from_temp_dir("repo-hash", temp_dir),
Err(PathError::FromPathBufError(_))
));
}
}

Expand Down
6 changes: 3 additions & 3 deletions crates/turborepo-daemon/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,7 @@ mod test {
.unwrap();

let repo_root = path.join_component("repo");
let paths = Paths::from_repo_root(&repo_root);
let paths = Paths::from_repo_root(&repo_root).unwrap();
tracing::info!("start");

let (tx, rx) = oneshot::channel::<CloseReason>();
Expand Down Expand Up @@ -942,7 +942,7 @@ mod test {
.unwrap();

let repo_root = path.join_component("repo");
let paths = Paths::from_repo_root(&repo_root);
let paths = Paths::from_repo_root(&repo_root).unwrap();

let now = Instant::now();
let (_tx, rx) = oneshot::channel::<CloseReason>();
Expand Down Expand Up @@ -1002,7 +1002,7 @@ mod test {
.join_component("package-lock.json")
.create_with_contents("")
.unwrap();
let paths = Paths::from_repo_root(&repo_root);
let paths = Paths::from_repo_root(&repo_root).unwrap();

let (_tx, rx) = oneshot::channel::<CloseReason>();
let exit_signal = rx.map(|_result| CloseReason::Interrupt);
Expand Down
45 changes: 34 additions & 11 deletions crates/turborepo-lib/src/commands/daemon.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::time::Duration;
use std::{path::PathBuf, time::Duration};

use camino::Utf8PathBuf;
use futures::FutureExt;
Expand Down Expand Up @@ -62,7 +62,7 @@ pub async fn daemon_client(
can_kill_server,
&base.repo_root,
custom_turbo_json_path,
);
)?;

match command {
DaemonCommand::Restart => {
Expand Down Expand Up @@ -221,15 +221,19 @@ pub async fn daemon_client(
Ok(())
}

async fn get_log_file_from_daemon(connector: DaemonConnector) -> Result<String, DaemonError> {
async fn get_log_file_from_daemon(connector: DaemonConnector) -> Result<PathBuf, DaemonError> {
let mut client = connector.connect().await?;
let status = client.status().await?;
Ok(log_filename(&status.log_file)?)
Ok(PathBuf::from(log_filename(&status.log_file)?))
}

async fn get_log_file_from_folder(base: &CommandBase) -> Result<String, DaemonError> {
async fn get_log_file_from_folder(base: &CommandBase) -> Result<PathBuf, DaemonError> {
warn!("couldn't connect to daemon, looking for old log files");
let log_folder = base.repo_root.join_components(&[".turbo", "daemon"]);
latest_log_file_from_dir(&log_folder)
}

fn latest_log_file_from_dir(log_folder: &AbsoluteSystemPath) -> Result<PathBuf, DaemonError> {
let Ok(dir) = std::fs::read_dir(log_folder) else {
return Err(DaemonError::LogFileNotFound);
};
Expand All @@ -243,11 +247,7 @@ async fn get_log_file_from_folder(base: &CommandBase) -> Result<String, DaemonEr
.max_by(|(_, mt1), (_, mt2)| mt1.cmp(mt2))
.ok_or(DaemonError::LogFileNotFound)?;

Ok(latest_file
.path()
.to_str()
.expect("log file should be utf-8")
.to_string())
Ok(latest_file.path())
}

fn clean(pid_file: &AbsoluteSystemPath, sock_file: &AbsoluteSystemPath) -> Result<(), DaemonError> {
Expand Down Expand Up @@ -311,7 +311,7 @@ pub async fn daemon_server(
turbo_json_path: Option<camino::Utf8PathBuf>,
logging: &TurboSubscriber,
) -> Result<(), DaemonError> {
let paths = Paths::from_repo_root(&base.repo_root);
let paths = Paths::from_repo_root(&base.repo_root)?;

tracing::trace!("logging to file: {:?}", paths.log_file);
if let Err(e) = logging.set_daemon_logger(tracing_appender::rolling::daily(
Expand Down Expand Up @@ -379,3 +379,26 @@ pub struct DaemonStatus {
pub pid_file: turbopath::AbsoluteSystemPathBuf,
pub sock_file: turbopath::AbsoluteSystemPathBuf,
}

#[cfg(test)]
mod tests {
#[cfg(all(unix, not(target_os = "macos")))]
#[test]
fn latest_log_file_from_dir_returns_non_utf8_path() {
use std::{ffi::OsString, os::unix::ffi::OsStringExt};

use turbopath::AbsoluteSystemPathBuf;

let tempdir = tempfile::tempdir().unwrap();
let log_folder = AbsoluteSystemPathBuf::try_from(tempdir.path()).unwrap();
let log_file = log_folder
.as_std_path()
.join(OsString::from_vec(b"turbo-\xFF.log".to_vec()));
std::fs::write(&log_file, "").unwrap();

assert_eq!(
super::latest_log_file_from_dir(&log_folder).unwrap(),
log_file
);
}
}
10 changes: 6 additions & 4 deletions crates/turborepo-lib/src/commands/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ fn is_wsl() -> bool {

pub async fn run(base: CommandBase) {
let system = System::new_all();
let connector = DaemonConnector::new(false, false, &base.repo_root, None);
let daemon_status = match connector.connect().await {
Ok(_status) => "Running",
Err(DaemonConnectorError::NotRunning) => "Not running",
let daemon_status = match DaemonConnector::new(false, false, &base.repo_root, None) {
Ok(connector) => match connector.connect().await {
Ok(_status) => "Running",
Err(DaemonConnectorError::NotRunning) => "Not running",
Err(_e) => "Error getting status",
},
Err(_e) => "Error getting status",
};
let package_manager = PackageJson::load(&base.repo_root.join_component("package.json"))
Expand Down
Loading
Loading