-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(Turborepo): Enable rust daemon #5964
Changes from all commits
0ef1173
23f04ef
58ff569
2eb534b
949c844
5baf5f7
2730d5c
c5f3d32
b87f94d
3cb457b
e40b88c
57ad7fc
df64f58
d3194da
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ package daemonclient | |
|
||
import ( | ||
"context" | ||
"path/filepath" | ||
|
||
"github.com/vercel/turbo/cli/internal/daemon/connector" | ||
"github.com/vercel/turbo/cli/internal/fs" | ||
|
@@ -33,9 +34,14 @@ func New(client *connector.Client) *DaemonClient { | |
|
||
// GetChangedOutputs implements runcache.OutputWatcher.GetChangedOutputs | ||
func (d *DaemonClient) GetChangedOutputs(ctx context.Context, hash string, repoRelativeOutputGlobs []string) ([]string, int, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are the output globs being passed in using a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. some parts of the glob have |
||
// The daemon expects globs to be unix paths | ||
var outputGlobs []string | ||
for _, outputGlob := range repoRelativeOutputGlobs { | ||
outputGlobs = append(outputGlobs, filepath.ToSlash(outputGlob)) | ||
} | ||
resp, err := d.client.GetChangedOutputs(ctx, &turbodprotocol.GetChangedOutputsRequest{ | ||
Hash: hash, | ||
OutputGlobs: repoRelativeOutputGlobs, | ||
OutputGlobs: outputGlobs, | ||
}) | ||
if err != nil { | ||
return nil, 0, err | ||
|
@@ -45,10 +51,19 @@ func (d *DaemonClient) GetChangedOutputs(ctx context.Context, hash string, repoR | |
|
||
// NotifyOutputsWritten implements runcache.OutputWatcher.NotifyOutputsWritten | ||
func (d *DaemonClient) NotifyOutputsWritten(ctx context.Context, hash string, repoRelativeOutputGlobs fs.TaskOutputs, timeSaved int) error { | ||
// The daemon expects globs to be unix paths | ||
var inclusions []string | ||
var exclusions []string | ||
for _, inclusion := range repoRelativeOutputGlobs.Inclusions { | ||
inclusions = append(inclusions, filepath.ToSlash(inclusion)) | ||
} | ||
for _, exclusion := range repoRelativeOutputGlobs.Exclusions { | ||
exclusions = append(exclusions, filepath.ToSlash(exclusion)) | ||
} | ||
_, err := d.client.NotifyOutputsWritten(ctx, &turbodprotocol.NotifyOutputsWrittenRequest{ | ||
Hash: hash, | ||
OutputGlobs: repoRelativeOutputGlobs.Inclusions, | ||
OutputExclusionGlobs: repoRelativeOutputGlobs.Exclusions, | ||
OutputGlobs: inclusions, | ||
OutputExclusionGlobs: exclusions, | ||
TimeSaved: uint64(timeSaved), | ||
}) | ||
return err | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
use std::{ | ||
collections::{HashMap, HashSet}, | ||
fmt::Display, | ||
future::IntoFuture, | ||
str::FromStr, | ||
}; | ||
|
@@ -24,26 +25,53 @@ pub struct GlobSet { | |
exclude: Any<'static>, | ||
} | ||
|
||
#[derive(Debug, Error)] | ||
pub struct GlobError { | ||
// Boxed to minimize error size | ||
underlying: Box<wax::BuildError>, | ||
raw_glob: String, | ||
} | ||
|
||
impl Display for GlobError { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(f, "{}: {}", self.underlying, self.raw_glob) | ||
} | ||
} | ||
|
||
fn compile_glob(raw: &str) -> Result<Glob<'static>, GlobError> { | ||
Glob::from_str(raw) | ||
.map(|g| g.to_owned()) | ||
.map_err(|e| GlobError { | ||
underlying: Box::new(e), | ||
raw_glob: raw.to_owned(), | ||
}) | ||
} | ||
|
||
impl GlobSet { | ||
pub fn from_raw( | ||
raw_includes: Vec<String>, | ||
raw_excludes: Vec<String>, | ||
) -> Result<Self, wax::BuildError> { | ||
) -> Result<Self, GlobError> { | ||
let include = raw_includes | ||
.into_iter() | ||
.map(|raw_glob| { | ||
let glob = Glob::from_str(&raw_glob)?.to_owned(); | ||
let glob = compile_glob(&raw_glob)?; | ||
Ok((raw_glob, glob)) | ||
}) | ||
.collect::<Result<HashMap<_, _>, wax::BuildError>>()?; | ||
.collect::<Result<HashMap<_, _>, GlobError>>()?; | ||
let excludes = raw_excludes | ||
.into_iter() | ||
.iter() | ||
.map(|raw_glob| { | ||
let glob = Glob::from_str(&raw_glob)?.to_owned(); | ||
let glob = compile_glob(raw_glob)?; | ||
Ok(glob) | ||
}) | ||
.collect::<Result<Vec<_>, wax::BuildError>>()?; | ||
let exclude = wax::any(excludes)?.to_owned(); | ||
.collect::<Result<Vec<_>, GlobError>>()?; | ||
let exclude = wax::any(excludes) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reading the doc comment of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went to switch this to something like "globs elided, combined excludes are too large", but decided to back out the change. I think in the (hopefully extremely rare) case when this happens, we still want to know what those globs are, as a pointer to where they came from, vs not having any idea. If in the future we have some provenance information, like "the globs from line 5 of |
||
.map_err(|e| GlobError { | ||
underlying: Box::new(e), | ||
raw_glob: format!("{{{}}}", raw_excludes.join(",")), | ||
})? | ||
.to_owned(); | ||
Ok(Self { include, exclude }) | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,9 +39,12 @@ pub async fn daemon_client(command: &DaemonCommand, base: &CommandBase) -> Resul | |
let client = connector.connect().await?; | ||
client.restart().await?; | ||
} | ||
// connector.connect will have already started the daemon if needed, | ||
// so this is a no-op | ||
DaemonCommand::Start => {} | ||
DaemonCommand::Start => { | ||
// We don't care about the client, but we do care that we can connect | ||
// which ensures that daemon is started if it wasn't already. | ||
let _ = connector.connect().await?; | ||
println!("Daemon is running"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice |
||
} | ||
DaemonCommand::Stop => { | ||
let client = connector.connect().await?; | ||
client.stop().await?; | ||
|
@@ -171,8 +174,20 @@ pub async fn daemon_server( | |
} | ||
CloseReason::Interrupt | ||
}); | ||
// TODO: be more methodical about this choice: | ||
let cookie_dir = base.repo_root.join_component(".git"); | ||
// We already store logs in .turbo and recommend it be gitignore'd. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fine with this, but we should note that we only add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We also write cached logs there: https://github.com/vercel/turbo/blob/2bb8c9a2477b5fb20b4711fe34412559bc85aab9/cli/internal/graph/graph.go#L241 |
||
// Watchman uses .git, but we can't guarantee that git is present _or_ | ||
// that the turbo root is the same as the git root. | ||
let cookie_dir = base.repo_root.join_components(&[".turbo", "cookies"]); | ||
// We need to ensure that the cookie directory is cleared out first so | ||
// that we can start over with cookies. | ||
if cookie_dir.exists() { | ||
cookie_dir | ||
.remove_dir_all() | ||
.map_err(|e| DaemonError::CookieDir(e, cookie_dir.clone()))?; | ||
} | ||
cookie_dir | ||
.create_dir_all() | ||
.map_err(|e| DaemonError::CookieDir(e, cookie_dir.clone()))?; | ||
let reason = crate::daemon::serve( | ||
&base.repo_root, | ||
cookie_dir, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's for debugging, and it ensures that we run the daemon that we're intending to debug, rather than whatever the repo depends on at the time.