Skip to content

Commit 41fb353

Browse files
author
Greg Soltis
authoredFeb 12, 2024··
chore(Turborepo): Move daemon paths off of CommandBase (#7339)
### Description Stepping stone to moving `CommandBase` out of `run` - Move daemon path handling into the daemon module ### Testing Instructions Updated existing test suite Closes TURBO-2323 --------- Co-authored-by: Greg Soltis <Greg Soltis>
1 parent 0232a4b commit 41fb353

File tree

11 files changed

+249
-315
lines changed

11 files changed

+249
-315
lines changed
 

‎crates/turborepo-lib/src/commands/daemon.rs

+15-39
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use serde_json::json;
77
use time::{format_description, OffsetDateTime};
88
use tokio::signal::ctrl_c;
99
use tracing::{trace, warn};
10-
use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf};
10+
use turbopath::AbsoluteSystemPath;
1111
use turborepo_ui::{color, BOLD_GREEN, BOLD_RED, GREY};
1212
use which::which;
1313

@@ -16,6 +16,7 @@ use crate::{
1616
cli::DaemonCommand,
1717
daemon::{
1818
endpoint::SocketOpenError, CloseReason, DaemonConnector, DaemonConnectorError, DaemonError,
19+
Paths,
1920
},
2021
tracing::TurboSubscriber,
2122
};
@@ -32,15 +33,7 @@ pub async fn daemon_client(command: &DaemonCommand, base: &CommandBase) -> Resul
3233
DaemonCommand::Clean => (false, true),
3334
};
3435

35-
let pid_file = base.daemon_file_root().join_component("turbod.pid");
36-
let sock_file = base.daemon_file_root().join_component("turbod.sock");
37-
38-
let connector = DaemonConnector {
39-
can_start_server,
40-
can_kill_server,
41-
pid_file: pid_file.clone(),
42-
sock_file: sock_file.clone(),
43-
};
36+
let connector = DaemonConnector::new(can_start_server, can_kill_server, &base.repo_root);
4437

4538
match command {
4639
DaemonCommand::Restart => {
@@ -52,7 +45,7 @@ pub async fn daemon_client(command: &DaemonCommand, base: &CommandBase) -> Resul
5245
if let Err(e) = result {
5346
tracing::debug!("failed to restart the daemon: {:?}", e);
5447
tracing::debug!("falling back to clean");
55-
clean(&pid_file, &sock_file).await?;
48+
clean(&connector.paths.pid_file, &connector.paths.sock_file).await?;
5649
tracing::debug!("connecting for second time");
5750
let _ = connector.connect().await?;
5851
}
@@ -100,11 +93,12 @@ pub async fn daemon_client(command: &DaemonCommand, base: &CommandBase) -> Resul
10093
};
10194
let status = client.status().await?;
10295
let log_file = log_filename(&status.log_file)?;
96+
let paths = client.paths();
10397
let status = DaemonStatus {
10498
uptime_ms: status.uptime_msec,
10599
log_file: log_file.into(),
106-
pid_file: client.pid_file().to_owned(),
107-
sock_file: client.sock_file().to_owned(),
100+
pid_file: paths.pid_file.to_owned(),
101+
sock_file: paths.sock_file.to_owned(),
108102
};
109103

110104
if *json {
@@ -141,6 +135,7 @@ pub async fn daemon_client(command: &DaemonCommand, base: &CommandBase) -> Resul
141135
}
142136
DaemonCommand::Clean => {
143137
// try to connect and shutdown the daemon
138+
let paths = connector.paths.clone();
144139
let client = connector.connect().await;
145140
match client {
146141
Ok(client) => match client.stop().await {
@@ -155,7 +150,7 @@ pub async fn daemon_client(command: &DaemonCommand, base: &CommandBase) -> Resul
155150
tracing::trace!("unable to connect to the daemon: {:?}", e);
156151
}
157152
}
158-
clean(&pid_file, &sock_file).await?;
153+
clean(&paths.pid_file, &paths.sock_file).await?;
159154
println!("Done");
160155
}
161156
};
@@ -214,25 +209,12 @@ pub async fn daemon_server(
214209
idle_time: &String,
215210
logging: &TurboSubscriber,
216211
) -> Result<(), DaemonError> {
217-
let (log_folder, log_file) = {
218-
let directories = directories::ProjectDirs::from("com", "turborepo", "turborepo")
219-
.expect("user has a home dir");
220-
221-
let folder =
222-
AbsoluteSystemPathBuf::new(directories.data_dir().to_str().expect("UTF-8 path"))
223-
.expect("absolute");
224-
225-
let log_folder = folder.join_component("logs");
226-
let log_file =
227-
log_folder.join_component(format!("{}-turbo.log", base.repo_hash()).as_str());
228-
229-
(log_folder, log_file)
230-
};
212+
let paths = Paths::from_repo_root(&base.repo_root);
231213

232-
tracing::trace!("logging to file: {:?}", log_file);
214+
tracing::trace!("logging to file: {:?}", paths.log_file);
233215
if let Err(e) = logging.set_daemon_logger(tracing_appender::rolling::daily(
234-
log_folder,
235-
log_file.clone(),
216+
&paths.log_folder,
217+
&paths.log_file,
236218
)) {
237219
// error here is not fatal, just log it
238220
tracing::error!("failed to set file logger: {}", e);
@@ -242,20 +224,14 @@ pub async fn daemon_server(
242224
.map_err(|_| DaemonError::InvalidTimeout(idle_time.to_owned()))
243225
.map(|d| Duration::from_nanos(d as u64))?;
244226

245-
let daemon_root = base.daemon_file_root();
246227
let exit_signal = ctrl_c().map(|result| {
247228
if let Err(e) = result {
248229
tracing::error!("Error with signal handling: {}", e);
249230
}
250231
CloseReason::Interrupt
251232
});
252-
let server = crate::daemon::TurboGrpcService::new(
253-
base.repo_root.clone(),
254-
daemon_root,
255-
log_file,
256-
timeout,
257-
exit_signal,
258-
);
233+
let server =
234+
crate::daemon::TurboGrpcService::new(base.repo_root.clone(), paths, timeout, exit_signal);
259235

260236
let reason = server.serve().await?;
261237

‎crates/turborepo-lib/src/commands/mod.rs

-83
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::cell::OnceCell;
22

33
use dirs_next::config_dir;
4-
use sha2::{Digest, Sha256};
54
use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf};
65
use turborepo_api_client::{APIAuth, APIClient};
76
use turborepo_ui::UI;
@@ -134,89 +133,7 @@ impl CommandBase {
134133
&self.repo_root
135134
}
136135

137-
pub fn daemon_file_root(&self) -> AbsoluteSystemPathBuf {
138-
DaemonRootHasher(&self.repo_root).daemon_file_root()
139-
}
140-
141-
fn repo_hash(&self) -> String {
142-
DaemonRootHasher(&self.repo_root).repo_hash()
143-
}
144-
145136
pub fn version(&self) -> &'static str {
146137
self.version
147138
}
148139
}
149-
150-
pub struct DaemonRootHasher<'a>(&'a AbsoluteSystemPath);
151-
152-
impl<'a> DaemonRootHasher<'a> {
153-
pub fn new(repo_root: &'a AbsoluteSystemPath) -> Self {
154-
Self(repo_root)
155-
}
156-
157-
pub fn daemon_file_root(&self) -> AbsoluteSystemPathBuf {
158-
AbsoluteSystemPathBuf::new(std::env::temp_dir().to_str().expect("UTF-8 path"))
159-
.expect("temp dir is valid")
160-
.join_component("turbod")
161-
.join_component(self.repo_hash().as_str())
162-
}
163-
164-
pub fn sock_path(&self) -> AbsoluteSystemPathBuf {
165-
self.daemon_file_root().join_component("turbod.sock")
166-
}
167-
168-
pub fn lock_path(&self) -> AbsoluteSystemPathBuf {
169-
self.daemon_file_root().join_component("turbod.pid")
170-
}
171-
172-
pub fn lsp_path(&self) -> AbsoluteSystemPathBuf {
173-
self.daemon_file_root().join_component("lsp.pid")
174-
}
175-
176-
fn repo_hash(&self) -> String {
177-
let mut hasher = Sha256::new();
178-
hasher.update(self.0.as_bytes());
179-
hex::encode(&hasher.finalize()[..8])
180-
}
181-
}
182-
183-
#[cfg(test)]
184-
mod test {
185-
use test_case::test_case;
186-
use turbopath::AbsoluteSystemPathBuf;
187-
use turborepo_ui::UI;
188-
189-
use crate::get_version;
190-
191-
#[cfg(not(target_os = "windows"))]
192-
#[test_case("/tmp/turborepo", "6e0cfa616f75a61c"; "basic example")]
193-
fn test_repo_hash(path: &str, expected_hash: &str) {
194-
use super::CommandBase;
195-
use crate::Args;
196-
197-
let args = Args::default();
198-
let repo_root = AbsoluteSystemPathBuf::new(path).unwrap();
199-
let command_base = CommandBase::new(args, repo_root, get_version(), UI::new(true));
200-
201-
let hash = command_base.repo_hash();
202-
203-
assert_eq!(hash, expected_hash);
204-
assert_eq!(hash.len(), 16);
205-
}
206-
207-
#[cfg(target_os = "windows")]
208-
#[test_case("C:\\\\tmp\\turborepo", "0103736e6883e35f"; "basic example")]
209-
fn test_repo_hash_win(path: &str, expected_hash: &str) {
210-
use super::CommandBase;
211-
use crate::Args;
212-
213-
let args = Args::default();
214-
let repo_root = AbsoluteSystemPathBuf::new(path).unwrap();
215-
let command_base = CommandBase::new(args, repo_root, get_version(), UI::new(true));
216-
217-
let hash = command_base.repo_hash();
218-
219-
assert_eq!(hash, expected_hash);
220-
assert_eq!(hash.len(), 16);
221-
}
222-
}

‎crates/turborepo-lib/src/daemon/client.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use super::{
1010
connector::{DaemonConnector, DaemonConnectorError},
1111
endpoint::SocketOpenError,
1212
proto::DiscoverPackagesResponse,
13+
Paths,
1314
};
1415
use crate::{daemon::proto, globwatcher::HashGlobSetupError};
1516

@@ -140,12 +141,8 @@ impl DaemonClient<DaemonConnector> {
140141
self.stop().await?.connect().await.map_err(Into::into)
141142
}
142143

143-
pub fn pid_file(&self) -> &turbopath::AbsoluteSystemPathBuf {
144-
&self.connect_settings.pid_file
145-
}
146-
147-
pub fn sock_file(&self) -> &turbopath::AbsoluteSystemPathBuf {
148-
&self.connect_settings.sock_file
144+
pub fn paths(&self) -> &Paths {
145+
&self.connect_settings.paths
149146
}
150147
}
151148

‎crates/turborepo-lib/src/daemon/connector.rs

+80-111
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ use thiserror::Error;
1313
use tokio::{sync::mpsc, time::timeout};
1414
use tonic::transport::Endpoint;
1515
use tracing::debug;
16+
use turbopath::AbsoluteSystemPath;
1617

17-
use super::{proto::turbod_client::TurbodClient, DaemonClient};
18+
use super::{proto::turbod_client::TurbodClient, DaemonClient, Paths};
1819
use crate::daemon::DaemonError;
1920

2021
#[derive(Error, Debug)]
@@ -64,11 +65,23 @@ pub struct DaemonConnector {
6465
/// Whether the connector is allowed to kill a running daemon (for example,
6566
/// in the event of a version mismatch).
6667
pub can_kill_server: bool,
67-
pub pid_file: turbopath::AbsoluteSystemPathBuf,
68-
pub sock_file: turbopath::AbsoluteSystemPathBuf,
68+
pub paths: Paths,
6969
}
7070

7171
impl DaemonConnector {
72+
pub fn new(
73+
can_start_server: bool,
74+
can_kill_server: bool,
75+
repo_root: &AbsoluteSystemPath,
76+
) -> Self {
77+
let paths = Paths::from_repo_root(repo_root);
78+
Self {
79+
can_start_server,
80+
can_kill_server,
81+
paths,
82+
}
83+
}
84+
7285
const CONNECT_RETRY_MAX: usize = 3;
7386
const CONNECT_TIMEOUT: Duration = Duration::from_secs(1);
7487
const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(1);
@@ -93,7 +106,7 @@ impl DaemonConnector {
93106
let pid = self.get_or_start_daemon().await?;
94107
debug!("got daemon with pid: {}", pid);
95108

96-
let conn = match self.get_connection(self.sock_file.clone()).await {
109+
let conn = match self.get_connection(self.paths.sock_file.clone()).await {
97110
Err(DaemonConnectorError::Watcher(_)) => continue,
98111
Err(DaemonConnectorError::Socket(e)) => {
99112
// assume the server is not yet ready
@@ -130,7 +143,7 @@ impl DaemonConnector {
130143
///
131144
/// If a daemon is not running, it starts one.
132145
async fn get_or_start_daemon(&self) -> Result<sysinfo::Pid, DaemonConnectorError> {
133-
debug!("looking for pid in lockfile: {:?}", self.pid_file);
146+
debug!("looking for pid in lockfile: {:?}", self.paths.pid_file);
134147

135148
let pidfile = self.pid_lock();
136149

@@ -225,7 +238,7 @@ impl DaemonConnector {
225238

226239
match timeout(
227240
Self::SHUTDOWN_TIMEOUT,
228-
wait_for_file(&self.pid_file, WaitAction::Deleted),
241+
wait_for_file(&self.paths.pid_file, WaitAction::Deleted),
229242
)
230243
.await?
231244
{
@@ -273,19 +286,19 @@ impl DaemonConnector {
273286
// exists to protect against stale .sock files
274287
timeout(
275288
Self::SOCKET_TIMEOUT,
276-
wait_for_file(&self.pid_file, WaitAction::Exists),
289+
wait_for_file(&self.paths.pid_file, WaitAction::Exists),
277290
)
278291
.await??;
279292
timeout(
280293
Self::SOCKET_TIMEOUT,
281-
wait_for_file(&self.sock_file, WaitAction::Exists),
294+
wait_for_file(&self.paths.sock_file, WaitAction::Exists),
282295
)
283296
.await?
284297
.map_err(Into::into)
285298
}
286299

287300
fn pid_lock(&self) -> pidlock::Pidlock {
288-
pidlock::Pidlock::new(self.pid_file.clone().into())
301+
pidlock::Pidlock::new(self.paths.pid_file.clone().into())
289302
}
290303
}
291304

@@ -396,7 +409,7 @@ enum WaitAction {
396409

397410
#[cfg(test)]
398411
mod test {
399-
use std::{assert_matches::assert_matches, path::Path};
412+
use std::assert_matches::assert_matches;
400413

401414
use sysinfo::Pid;
402415
use tokio::{
@@ -414,28 +427,18 @@ mod test {
414427
#[cfg(target_os = "windows")]
415428
const NODE_EXE: &str = "node.exe";
416429

417-
fn pid_path(tmp_path: &Path) -> AbsoluteSystemPathBuf {
418-
AbsoluteSystemPathBuf::try_from(tmp_path.join("turbod.pid")).unwrap()
419-
}
420-
421-
fn sock_path(tmp_path: &Path) -> AbsoluteSystemPathBuf {
422-
AbsoluteSystemPathBuf::try_from(tmp_path.join("turbod.sock")).unwrap()
423-
}
424-
425430
#[tokio::test]
426431
async fn handles_invalid_pid() {
427432
let tmp_dir = tempfile::tempdir().unwrap();
428-
let tmp_path = tmp_dir.path().to_owned();
429-
430-
let pid = pid_path(&tmp_path);
431-
std::fs::write(&pid, "not a pid").unwrap();
432-
433-
let connector = DaemonConnector {
434-
pid_file: pid,
435-
sock_file: sock_path(&tmp_path),
436-
can_kill_server: false,
437-
can_start_server: false,
438-
};
433+
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
434+
435+
let connector = DaemonConnector::new(false, false, &repo_root);
436+
connector.paths.pid_file.ensure_dir().unwrap();
437+
connector
438+
.paths
439+
.pid_file
440+
.create_with_contents("not a pid")
441+
.unwrap();
439442

440443
assert_matches!(
441444
connector.get_or_start_daemon().await,
@@ -446,17 +449,8 @@ mod test {
446449
#[tokio::test]
447450
async fn handles_missing_server_connect() {
448451
let tmp_dir = tempfile::tempdir().unwrap();
449-
let tmp_path = tmp_dir.path().to_owned();
450-
451-
let pid = pid_path(&tmp_path);
452-
let sock = sock_path(&tmp_path);
453-
454-
let connector = DaemonConnector {
455-
pid_file: pid,
456-
sock_file: sock,
457-
can_kill_server: false,
458-
can_start_server: false,
459-
};
452+
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
453+
let connector = DaemonConnector::new(false, false, &repo_root);
460454

461455
assert_matches!(
462456
connector.connect().await,
@@ -467,17 +461,8 @@ mod test {
467461
#[tokio::test]
468462
async fn handles_kill_dead_server_missing_pid() {
469463
let tmp_dir = tempfile::tempdir().unwrap();
470-
let tmp_path = tmp_dir.path().to_owned();
471-
472-
let pid = pid_path(&tmp_path);
473-
let sock = sock_path(&tmp_path);
474-
475-
let connector = DaemonConnector {
476-
pid_file: pid,
477-
sock_file: sock,
478-
can_kill_server: false,
479-
can_start_server: false,
480-
};
464+
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
465+
let connector = DaemonConnector::new(false, false, &repo_root);
481466

482467
assert_matches!(
483468
connector.kill_dead_server(Pid::from(usize::MAX)).await,
@@ -488,35 +473,34 @@ mod test {
488473
#[tokio::test]
489474
async fn handles_kill_dead_server_missing_process() {
490475
let tmp_dir = tempfile::tempdir().unwrap();
491-
let tmp_path = tmp_dir.path().to_owned();
492-
493-
let pid = pid_path(&tmp_path);
494-
std::fs::write(&pid, i32::MAX.to_string()).unwrap();
495-
let sock = sock_path(&tmp_path);
496-
std::fs::write(&sock, "").unwrap();
497-
498-
let connector = DaemonConnector {
499-
pid_file: pid,
500-
sock_file: sock,
501-
can_kill_server: false,
502-
can_start_server: false,
503-
};
476+
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
477+
let connector = DaemonConnector::new(false, false, &repo_root);
478+
479+
connector.paths.pid_file.ensure_dir().unwrap();
480+
connector
481+
.paths
482+
.pid_file
483+
.create_with_contents(i32::MAX.to_string())
484+
.unwrap();
485+
connector.paths.sock_file.ensure_dir().unwrap();
486+
connector.paths.sock_file.create_with_contents("").unwrap();
504487

505488
assert_matches!(
506489
connector.kill_dead_server(Pid::from(usize::MAX)).await,
507490
Ok(())
508491
);
509492

510493
assert!(
511-
!connector.pid_file.exists(),
494+
!connector.paths.pid_file.exists(),
512495
"pid file should be cleaned up when getting the owner of a stale pid"
513496
);
514497
}
515498

516499
#[tokio::test]
517500
async fn handles_kill_dead_server_wrong_process() {
518501
let tmp_dir = tempfile::tempdir().unwrap();
519-
let tmp_path = tmp_dir.path().to_owned();
502+
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
503+
let connector = DaemonConnector::new(false, false, &repo_root);
520504

521505
let proc = tokio::process::Command::new(NODE_EXE)
522506
.stdout(Stdio::null())
@@ -526,17 +510,14 @@ mod test {
526510
.spawn()
527511
.unwrap();
528512

529-
let pid = pid_path(&tmp_path);
530-
std::fs::write(&pid, proc.id().unwrap().to_string()).unwrap();
531-
let sock = sock_path(&tmp_path);
532-
std::fs::write(&sock, "").unwrap();
533-
534-
let connector = DaemonConnector {
535-
pid_file: pid,
536-
sock_file: sock,
537-
can_kill_server: true,
538-
can_start_server: false,
539-
};
513+
connector.paths.pid_file.ensure_dir().unwrap();
514+
connector
515+
.paths
516+
.pid_file
517+
.create_with_contents(proc.id().unwrap().to_string())
518+
.unwrap();
519+
connector.paths.sock_file.ensure_dir().unwrap();
520+
connector.paths.sock_file.create_with_contents("").unwrap();
540521

541522
let kill_pid = Pid::from(usize::MAX);
542523
let proc_id = Pid::from(proc.id().unwrap() as usize);
@@ -546,13 +527,17 @@ mod test {
546527
Err(DaemonConnectorError::WrongPidProcess(daemon, running)) if daemon == kill_pid && running == proc_id
547528
);
548529

549-
assert!(connector.pid_file.exists(), "pid file should still exist");
530+
assert!(
531+
connector.paths.pid_file.exists(),
532+
"pid file should still exist"
533+
);
550534
}
551535

552536
#[tokio::test]
553537
async fn handles_kill_dead_server() {
554538
let tmp_dir = tempfile::tempdir().unwrap();
555-
let tmp_path = tmp_dir.path().to_owned();
539+
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
540+
let connector = DaemonConnector::new(false, true, &repo_root);
556541

557542
let proc = tokio::process::Command::new(NODE_EXE)
558543
.stdout(Stdio::null())
@@ -562,17 +547,14 @@ mod test {
562547
.spawn()
563548
.unwrap();
564549

565-
let pid = pid_path(&tmp_path);
566-
std::fs::write(&pid, proc.id().unwrap().to_string()).unwrap();
567-
let sock = sock_path(&tmp_path);
568-
std::fs::write(&sock, "").unwrap();
569-
570-
let connector = DaemonConnector {
571-
pid_file: pid,
572-
sock_file: sock,
573-
can_kill_server: true,
574-
can_start_server: false,
575-
};
550+
connector.paths.pid_file.ensure_dir().unwrap();
551+
connector
552+
.paths
553+
.pid_file
554+
.create_with_contents(proc.id().unwrap().to_string())
555+
.unwrap();
556+
connector.paths.sock_file.ensure_dir().unwrap();
557+
connector.paths.sock_file.create_with_contents("").unwrap();
576558

577559
assert_matches!(
578560
connector
@@ -581,7 +563,10 @@ mod test {
581563
Ok(())
582564
);
583565

584-
assert!(connector.pid_file.exists(), "pid file should still exist");
566+
assert!(
567+
connector.paths.pid_file.exists(),
568+
"pid file should still exist"
569+
);
585570
}
586571

587572
struct DummyServer {
@@ -664,25 +649,9 @@ mod test {
664649
}))
665650
.serve_with_incoming(stream);
666651

667-
let (pid_file, sock_file) = if cfg!(windows) {
668-
(
669-
AbsoluteSystemPathBuf::new("C:\\pid").unwrap(),
670-
AbsoluteSystemPathBuf::new("C:\\sock").unwrap(),
671-
)
672-
} else {
673-
(
674-
AbsoluteSystemPathBuf::new("/pid").unwrap(),
675-
AbsoluteSystemPathBuf::new("/sock").unwrap(),
676-
)
677-
};
678-
679-
// set up the client
680-
let conn = DaemonConnector {
681-
pid_file,
682-
sock_file,
683-
can_kill_server: false,
684-
can_start_server: false,
685-
};
652+
let tmp_dir = tempfile::tempdir().unwrap();
653+
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
654+
let connector = DaemonConnector::new(false, false, &repo_root);
686655

687656
let mut client = Endpoint::try_from("http://[::]:50051")
688657
.expect("this is a valid uri")
@@ -716,7 +685,7 @@ mod test {
716685
assert_matches!(hello_resp, DaemonError::VersionMismatch(_));
717686
let client = DaemonClient::new(client);
718687

719-
let shutdown_fut = conn.kill_live_server(client, Pid::from(1000));
688+
let shutdown_fut = connector.kill_live_server(client, Pid::from(1000));
720689

721690
// drive the futures to completion
722691
select! {

‎crates/turborepo-lib/src/daemon/endpoint.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ const WINDOWS_POLL_DURATION: Duration = Duration::from_millis(1);
3030
/// code path to shut down the non-blocking polling
3131
#[tracing::instrument]
3232
pub async fn listen_socket(
33-
daemon_root: &AbsoluteSystemPath,
33+
pid_path: &AbsoluteSystemPath,
34+
sock_path: &AbsoluteSystemPath,
3435
#[allow(unused)] running: Arc<AtomicBool>,
3536
) -> Result<
3637
(
@@ -39,8 +40,6 @@ pub async fn listen_socket(
3940
),
4041
SocketOpenError,
4142
> {
42-
let pid_path = daemon_root.join_component("turbod.pid");
43-
let sock_path = daemon_root.join_component("turbod.sock");
4443
let mut lock = pidlock::Pidlock::new(pid_path.as_std_path().to_owned());
4544

4645
trace!("acquiring pidlock");
@@ -186,7 +185,7 @@ mod test {
186185
use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf};
187186

188187
use super::listen_socket;
189-
use crate::daemon::endpoint::SocketOpenError;
188+
use crate::daemon::{endpoint::SocketOpenError, Paths};
190189

191190
fn pid_path(daemon_root: &AbsoluteSystemPath) -> AbsoluteSystemPathBuf {
192191
daemon_root.join_component("turbod.pid")
@@ -195,13 +194,14 @@ mod test {
195194
#[tokio::test]
196195
async fn test_stale_pid() {
197196
let tmp_dir = tempfile::tempdir().unwrap();
198-
let daemon_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
199-
let pid_path = pid_path(&daemon_root);
197+
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
198+
let paths = Paths::from_repo_root(&repo_root);
199+
paths.pid_file.ensure_dir().unwrap();
200200
// A pid that will never be running and is guaranteed not to be us
201-
pid_path.create_with_contents("100000").unwrap();
201+
paths.pid_file.create_with_contents("100000").unwrap();
202202

203203
let running = Arc::new(AtomicBool::new(true));
204-
let result = listen_socket(&daemon_root, running).await;
204+
let result = listen_socket(&paths.pid_file, &paths.sock_file, running).await;
205205

206206
assert!(
207207
result.is_ok(),
@@ -212,21 +212,23 @@ mod test {
212212
#[tokio::test]
213213
async fn test_existing_process() {
214214
let tmp_dir = tempfile::tempdir().unwrap();
215-
let daemon_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
216-
let pid_path = pid_path(&daemon_root);
215+
let repo_root = AbsoluteSystemPathBuf::try_from(tmp_dir.path()).unwrap();
216+
let paths = Paths::from_repo_root(&repo_root);
217217

218218
#[cfg(windows)]
219219
let node_bin = "node.exe";
220220
#[cfg(not(windows))]
221221
let node_bin = "node";
222222

223223
let mut child = Command::new(node_bin).spawn().unwrap();
224-
pid_path
224+
paths.pid_file.ensure_dir().unwrap();
225+
paths
226+
.pid_file
225227
.create_with_contents(format!("{}", child.id()))
226228
.unwrap();
227229

228230
let running = Arc::new(AtomicBool::new(true));
229-
let result = listen_socket(&daemon_root, running).await;
231+
let result = listen_socket(&paths.pid_file, &paths.sock_file, running).await;
230232

231233
// Note: PidLock doesn't implement Debug, so we can't unwrap_err()
232234

‎crates/turborepo-lib/src/daemon/mod.rs

+74
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,60 @@ mod server;
88
pub use client::{DaemonClient, DaemonError};
99
pub use connector::{DaemonConnector, DaemonConnectorError};
1010
pub use server::{CloseReason, TurboGrpcService};
11+
use sha2::{Digest, Sha256};
12+
use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf};
13+
14+
#[derive(Clone, Debug)]
15+
pub struct Paths {
16+
pub pid_file: AbsoluteSystemPathBuf,
17+
pub lock_file: AbsoluteSystemPathBuf,
18+
pub sock_file: AbsoluteSystemPathBuf,
19+
pub lsp_pid_file: AbsoluteSystemPathBuf,
20+
pub log_file: AbsoluteSystemPathBuf,
21+
pub log_folder: AbsoluteSystemPathBuf,
22+
}
23+
24+
fn repo_hash(repo_root: &AbsoluteSystemPath) -> String {
25+
let mut hasher = Sha256::new();
26+
hasher.update(repo_root.to_string().as_bytes());
27+
hex::encode(&hasher.finalize()[..8])
28+
}
29+
30+
fn daemon_file_root(repo_hash: &str) -> AbsoluteSystemPathBuf {
31+
AbsoluteSystemPathBuf::new(std::env::temp_dir().to_str().expect("UTF-8 path"))
32+
.expect("temp dir is valid")
33+
.join_component("turbod")
34+
.join_component(repo_hash)
35+
}
36+
37+
fn daemon_log_file_and_folder(repo_hash: &str) -> (AbsoluteSystemPathBuf, AbsoluteSystemPathBuf) {
38+
let directories = directories::ProjectDirs::from("com", "turborepo", "turborepo")
39+
.expect("user has a home dir");
40+
41+
let folder = AbsoluteSystemPathBuf::new(directories.data_dir().to_str().expect("UTF-8 path"))
42+
.expect("absolute");
43+
44+
let log_folder = folder.join_component("logs");
45+
let log_file = log_folder.join_component(format!("{}-turbo.log", repo_hash).as_str());
46+
47+
(log_file, log_folder)
48+
}
49+
50+
impl Paths {
51+
pub fn from_repo_root(repo_root: &AbsoluteSystemPath) -> Self {
52+
let repo_hash = repo_hash(repo_root);
53+
let daemon_root = daemon_file_root(&repo_hash);
54+
let (log_file, log_folder) = daemon_log_file_and_folder(&repo_hash);
55+
Self {
56+
pid_file: daemon_root.join_component("turbod.pid"),
57+
lock_file: daemon_root.join_component("turbod.lock"),
58+
sock_file: daemon_root.join_component("turbod.sock"),
59+
lsp_pid_file: daemon_root.join_component("lsp.pid"),
60+
log_file,
61+
log_folder,
62+
}
63+
}
64+
}
1165

1266
pub(crate) mod proto {
1367

@@ -54,3 +108,23 @@ pub(crate) mod proto {
54108
}
55109
}
56110
}
111+
112+
#[cfg(test)]
113+
mod test {
114+
use turbopath::AbsoluteSystemPathBuf;
115+
116+
use super::repo_hash;
117+
118+
#[test]
119+
fn test_repo_hash() {
120+
#[cfg(not(target_os = "windows"))]
121+
let (path, expected_hash) = ("/tmp/turborepo", "6e0cfa616f75a61c");
122+
#[cfg(target_os = "windows")]
123+
let (path, expected_hash) = ("C:\\\\tmp\\turborepo", "0103736e6883e35f");
124+
let repo_root = AbsoluteSystemPathBuf::new(path).unwrap();
125+
let hash = repo_hash(&repo_root);
126+
127+
assert_eq!(hash, expected_hash);
128+
assert_eq!(hash.len(), 16);
129+
}
130+
}

‎crates/turborepo-lib/src/daemon/server.rs

+23-42
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,7 @@ use turborepo_repository::discovery::{
4242
DiscoveryResponse, LocalPackageDiscoveryBuilder, PackageDiscovery, PackageDiscoveryBuilder,
4343
};
4444

45-
use super::{
46-
bump_timeout::BumpTimeout,
47-
endpoint::SocketOpenError,
48-
proto::{self},
49-
};
45+
use super::{bump_timeout::BumpTimeout, endpoint::SocketOpenError, proto, Paths};
5046
use crate::daemon::{bump_timeout_layer::BumpTimeoutLayer, endpoint::listen_socket};
5147

5248
#[derive(Debug)]
@@ -120,8 +116,7 @@ pub struct TurboGrpcService<S, PDB> {
120116
watcher_tx: watch::Sender<Option<Arc<FileWatching>>>,
121117
watcher_rx: watch::Receiver<Option<Arc<FileWatching>>>,
122118
repo_root: AbsoluteSystemPathBuf,
123-
daemon_root: AbsoluteSystemPathBuf,
124-
log_file: AbsoluteSystemPathBuf,
119+
paths: Paths,
125120
timeout: Duration,
126121
external_shutdown: S,
127122

@@ -140,8 +135,7 @@ where
140135
/// state if the filewatcher encounters errors.
141136
pub fn new(
142137
repo_root: AbsoluteSystemPathBuf,
143-
daemon_root: AbsoluteSystemPathBuf,
144-
log_file: AbsoluteSystemPathBuf,
138+
paths: Paths,
145139
timeout: Duration,
146140
external_shutdown: S,
147141
) -> Self {
@@ -157,8 +151,7 @@ where
157151
watcher_tx,
158152
watcher_rx,
159153
repo_root,
160-
daemon_root,
161-
log_file,
154+
paths,
162155
timeout,
163156
external_shutdown,
164157
package_discovery_backup,
@@ -179,9 +172,8 @@ where
179172
package_discovery_backup: PDB2,
180173
) -> TurboGrpcService<S, PDB2> {
181174
TurboGrpcService {
182-
daemon_root: self.daemon_root,
183175
external_shutdown: self.external_shutdown,
184-
log_file: self.log_file,
176+
paths: self.paths,
185177
repo_root: self.repo_root,
186178
timeout: self.timeout,
187179
watcher_rx: self.watcher_rx,
@@ -194,19 +186,19 @@ where
194186
let Self {
195187
watcher_tx,
196188
watcher_rx,
197-
daemon_root,
198189
external_shutdown,
199-
log_file,
190+
paths,
200191
repo_root,
201192
timeout,
202193
package_discovery_backup,
203194
} = self;
204195

205196
let running = Arc::new(AtomicBool::new(true));
206-
let (_pid_lock, stream) = match listen_socket(&daemon_root, running.clone()).await {
207-
Ok((pid_lock, stream)) => (pid_lock, stream),
208-
Err(e) => return Ok(CloseReason::SocketOpenError(e)),
209-
};
197+
let (_pid_lock, stream) =
198+
match listen_socket(&paths.pid_file, &paths.sock_file, running.clone()).await {
199+
Ok((pid_lock, stream)) => (pid_lock, stream),
200+
Err(e) => return Ok(CloseReason::SocketOpenError(e)),
201+
};
210202
trace!("acquired connection stream for socket");
211203

212204
let watcher_repo_root = repo_root.to_owned();
@@ -261,7 +253,7 @@ where
261253
watcher_rx,
262254
times_saved: Arc::new(Mutex::new(HashMap::new())),
263255
start_time: Instant::now(),
264-
log_file: log_file.to_owned(),
256+
log_file: paths.log_file.clone(),
265257
};
266258
let server_fut = {
267259
let service = ServiceBuilder::new()
@@ -567,7 +559,7 @@ mod test {
567559
};
568560

569561
use super::compare_versions;
570-
use crate::daemon::{proto::VersionRange, CloseReason, TurboGrpcService};
562+
use crate::daemon::{proto::VersionRange, CloseReason, Paths, TurboGrpcService};
571563

572564
#[test_case("1.2.3", "1.2.3", VersionRange::Exact, true ; "exact match")]
573565
#[test_case("1.2.3", "1.2.3", VersionRange::Patch, true ; "patch match")]
@@ -623,19 +615,15 @@ mod test {
623615
.unwrap();
624616

625617
let repo_root = path.join_component("repo");
626-
let daemon_root = path.join_component("daemon");
627-
let log_file = daemon_root.join_component("log");
618+
let paths = Paths::from_repo_root(&repo_root);
628619
tracing::info!("start");
629620

630-
let pid_path = daemon_root.join_component("turbod.pid");
631-
632621
let (tx, rx) = oneshot::channel::<CloseReason>();
633622
let exit_signal = rx.map(|_result| CloseReason::Interrupt);
634623

635624
let service = TurboGrpcService::new(
636625
repo_root.clone(),
637-
daemon_root,
638-
log_file,
626+
paths.clone(),
639627
Duration::from_secs(60 * 60),
640628
exit_signal,
641629
)
@@ -651,9 +639,9 @@ mod test {
651639

652640
tokio::time::sleep(Duration::from_millis(2000)).await;
653641
assert!(
654-
pid_path.exists(),
642+
paths.pid_file.exists(),
655643
"pid file must be present at {:?}",
656-
pid_path
644+
paths.pid_file
657645
);
658646
// signal server exit
659647
tx.send(CloseReason::Interrupt).unwrap();
@@ -662,7 +650,7 @@ mod test {
662650
// The serve future should be dropped here, closing the server.
663651
tracing::info!("yay we are done");
664652

665-
assert!(!pid_path.exists(), "pid file must be deleted");
653+
assert!(!paths.pid_file.exists(), "pid file must be deleted");
666654

667655
tracing::info!("and files cleaned up");
668656
}
@@ -679,19 +667,15 @@ mod test {
679667
.unwrap();
680668

681669
let repo_root = path.join_component("repo");
682-
let daemon_root = path.join_component("daemon");
683-
let log_file = daemon_root.join_component("log");
684-
685-
let pid_path = daemon_root.join_component("turbod.pid");
670+
let paths = Paths::from_repo_root(&repo_root);
686671

687672
let now = Instant::now();
688673
let (_tx, rx) = oneshot::channel::<CloseReason>();
689674
let exit_signal = rx.map(|_result| CloseReason::Interrupt);
690675

691676
let server = TurboGrpcService::new(
692677
repo_root.clone(),
693-
daemon_root,
694-
log_file,
678+
paths.clone(),
695679
Duration::from_millis(10),
696680
exit_signal,
697681
)
@@ -714,7 +698,7 @@ mod test {
714698
Ok(CloseReason::Timeout),
715699
"must close due to timeout"
716700
);
717-
assert!(!pid_path.exists(), "pid file must be deleted");
701+
assert!(!paths.pid_file.exists(), "pid file must be deleted");
718702
}
719703

720704
#[tokio::test(flavor = "multi_thread")]
@@ -727,17 +711,14 @@ mod test {
727711
.unwrap();
728712

729713
let repo_root = path.join_component("repo");
730-
let daemon_root = path.join_component("daemon");
731-
daemon_root.create_dir_all().unwrap();
732-
let log_file = daemon_root.join_component("log");
714+
let paths = Paths::from_repo_root(&repo_root);
733715

734716
let (_tx, rx) = oneshot::channel::<CloseReason>();
735717
let exit_signal = rx.map(|_result| CloseReason::Interrupt);
736718

737719
let server = TurboGrpcService::new(
738720
repo_root.clone(),
739-
daemon_root,
740-
log_file,
721+
paths,
741722
Duration::from_secs(60 * 60),
742723
exit_signal,
743724
)

‎crates/turborepo-lib/src/lib.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ mod unescape;
3737
pub use crate::{
3838
child::spawn_child,
3939
cli::Args,
40-
commands::DaemonRootHasher,
41-
daemon::{DaemonClient, DaemonConnector},
40+
daemon::{DaemonClient, DaemonConnector, Paths as DaemonPaths},
4241
run::package_discovery::DaemonPackageDiscovery,
4342
};
4443

‎crates/turborepo-lib/src/run/mod.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,10 @@ impl Run {
249249
None
250250
}
251251
(_, Some(true)) | (false, None) => {
252-
let connector = DaemonConnector {
253-
can_start_server: true,
254-
can_kill_server: true,
255-
pid_file: self.base.daemon_file_root().join_component("turbod.pid"),
256-
sock_file: self.base.daemon_file_root().join_component("turbod.sock"),
257-
};
258-
252+
let can_start_server = true;
253+
let can_kill_server = true;
254+
let connector =
255+
DaemonConnector::new(can_start_server, can_kill_server, &self.base.repo_root);
259256
match (connector.connect().await, self.opts.run_opts.daemon) {
260257
(Ok(client), _) => {
261258
run_telemetry.track_daemon_init(DaemonInitStatus::Started);

‎crates/turborepo-lsp/src/lib.rs

+9-13
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use tower_lsp::{
1818
Client, LanguageServer,
1919
};
2020
use turbopath::AbsoluteSystemPathBuf;
21-
use turborepo_lib::{DaemonClient, DaemonConnector, DaemonPackageDiscovery, DaemonRootHasher};
21+
use turborepo_lib::{DaemonClient, DaemonConnector, DaemonPackageDiscovery, DaemonPaths};
2222
use turborepo_repository::{
2323
discovery::{self, DiscoveryResponse, PackageDiscovery},
2424
package_json::PackageJson,
@@ -60,22 +60,18 @@ impl LanguageServer for Backend {
6060
.expect("only fails if poisoned")
6161
.replace(repo_root.clone());
6262

63-
let hasher = DaemonRootHasher::new(&repo_root);
63+
let paths = DaemonPaths::from_repo_root(&repo_root);
6464

6565
let (_, daemon) = tokio::join!(
66-
self.client.log_message(
67-
MessageType::INFO,
68-
format!("root uri: {}", hasher.sock_path()),
69-
),
66+
self.client
67+
.log_message(MessageType::INFO, format!("root uri: {}", paths.sock_file),),
7068
tokio_retry::Retry::spawn(
7169
tokio_retry::strategy::FixedInterval::from_millis(100).take(5),
7270
|| {
73-
let connector = DaemonConnector {
74-
can_start_server: true,
75-
can_kill_server: false,
76-
pid_file: hasher.lock_path(),
77-
sock_file: hasher.sock_path(),
78-
};
71+
let can_start_server = true;
72+
let can_kill_server = false;
73+
let connector =
74+
DaemonConnector::new(can_start_server, can_kill_server, &repo_root);
7975
connector.connect()
8076
},
8177
)
@@ -98,7 +94,7 @@ impl LanguageServer for Backend {
9894
.send(Some(daemon))
9995
.expect("there is a receiver");
10096

101-
let mut lock = pidlock::Pidlock::new(hasher.lsp_path().as_std_path().to_owned());
97+
let mut lock = pidlock::Pidlock::new(paths.lsp_pid_file.as_std_path().to_owned());
10298

10399
if let Err(e) = lock.acquire() {
104100
self.client
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "@turbo/repository-linux-x64-musl",
3+
"version": "0.0.1-canary.5",
4+
"repository": {
5+
"type": "git",
6+
"url": "https://github.com/vercel/turbo",
7+
"directory": "packages/turbo-repository/npm/linux-x64-musl"
8+
},
9+
"os": [
10+
"linux"
11+
],
12+
"cpu": [
13+
"x64"
14+
],
15+
"libc": [
16+
"musl"
17+
],
18+
"main": "repository.linux-x64-musl.node",
19+
"files": [
20+
"repository.linux-x64-musl.node"
21+
],
22+
"license": "MPL-2.0",
23+
"engines": {
24+
"node": ">= 10"
25+
}
26+
}

0 commit comments

Comments
 (0)
Please sign in to comment.