Skip to content
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

fix(turborepo): Restructure reading from stderr, fix parsing of ls-tree #5181

Merged
merged 3 commits into from
Jun 2, 2023
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
8 changes: 3 additions & 5 deletions crates/turborepo-scm/src/hash_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::{
use nom::{Finish, IResult};
use turbopath::{AbsoluteSystemPathBuf, RelativeUnixPathBuf};

use crate::{package_deps::GitHashes, read_git_error, wait_for_success, Error};
use crate::{package_deps::GitHashes, wait_for_success, Error};

pub(crate) fn hash_objects(
pkg_path: &AbsoluteSystemPathBuf,
Expand Down Expand Up @@ -41,10 +41,8 @@ pub(crate) fn hash_objects(
.stderr
.take()
.ok_or_else(|| Error::git_error("failed to get stderr for git hash-object"))?;
read_object_hashes(stdout, stdin, &to_hash, pkg_prefix, hashes)
.map_err(|err| read_git_error(&mut stderr).unwrap_or(err))?;
wait_for_success(git, &mut stderr, "git hash-object", pkg_path)?;
Ok(())
let parse_result = read_object_hashes(stdout, stdin, &to_hash, pkg_prefix, hashes);
wait_for_success(git, &mut stderr, "git hash-object", pkg_path, parse_result)
}

const HASH_LEN: usize = 40;
Expand Down
53 changes: 31 additions & 22 deletions crates/turborepo-scm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,43 +40,52 @@ impl Error {
pub(crate) fn git_error(s: impl Into<String>) -> Self {
Error::Git(s.into(), Backtrace::capture())
}

fn from_git_exit_code(cmd: &str, path: &AbsoluteSystemPath, exit_code: Option<i32>) -> Error {
let s = format!(
"'{}' in {} exited with status code {}",
cmd,
path.to_string(),
exit_code
.map(|code| code.to_string())
.unwrap_or("unknown".to_string())
);
Error::Git(s, Backtrace::capture())
}
}

pub(crate) fn read_git_error<R: Read>(stderr: &mut R) -> Option<Error> {
fn read_git_error_to_string<R: Read>(stderr: &mut R) -> Option<String> {
let mut buf = String::new();
let bytes_read = stderr.read_to_string(&mut buf).ok()?;
if bytes_read > 0 {
// something failed with git, report that error
Some(Error::git_error(buf))
Some(buf)
} else {
None
}
}

pub(crate) fn wait_for_success<R: Read>(
pub(crate) fn wait_for_success<R: Read, T>(
mut child: Child,
stderr: &mut R,
command: &str,
root_path: impl AsRef<AbsoluteSystemPath>,
) -> Result<(), Error> {
parse_result: Result<T, Error>,
NicholasLYang marked this conversation as resolved.
Show resolved Hide resolved
) -> Result<T, Error> {
let exit_status = child.wait()?;
if !exit_status.success() {
Err(read_git_error(stderr).unwrap_or_else(|| {
Error::from_git_exit_code(command, root_path.as_ref(), exit_status.code())
}))
} else {
Ok(())
if exit_status.success() && parse_result.is_ok() {
return parse_result;
}
let stderr_output = read_git_error_to_string(stderr);
let stderr_text = stderr_output
.map(|stderr| format!(" stderr: {}", stderr))
.unwrap_or_default();
let exit_text = if exit_status.success() {
"".to_string()
} else {
let code = exit_status
.code()
.map(|code| code.to_string())
.unwrap_or("unknown".to_string());
format!(" exited with code {}", code)
};
let parse_error_text = if let Err(parse_error) = parse_result {
format!(" had a parse error {}", parse_error)
} else {
"".to_string()
};
let path_text = root_path.as_ref();
let err_text = format!(
NicholasLYang marked this conversation as resolved.
Show resolved Hide resolved
"'{}' in {}{}{}{}",
command, path_text, parse_error_text, exit_text, stderr_text
);
Err(Error::Git(err_text, Backtrace::capture()))
}
15 changes: 10 additions & 5 deletions crates/turborepo-scm/src/ls_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
use nom::Finish;
use turbopath::{AbsoluteSystemPathBuf, RelativeUnixPathBuf};

use crate::{package_deps::GitHashes, read_git_error, wait_for_success, Error};
use crate::{package_deps::GitHashes, wait_for_success, Error};

pub fn git_ls_tree(root_path: &AbsoluteSystemPathBuf) -> Result<GitHashes, Error> {
let mut hashes = GitHashes::new();
Expand All @@ -25,8 +25,8 @@ pub fn git_ls_tree(root_path: &AbsoluteSystemPathBuf) -> Result<GitHashes, Error
.stderr
.take()
.ok_or_else(|| Error::git_error("failed to get stderr for git ls-tree"))?;
read_ls_tree(stdout, &mut hashes).map_err(|err| read_git_error(&mut stderr).unwrap_or(err))?;
wait_for_success(git, &mut stderr, "git ls-tree", root_path)?;
let parse_result = read_ls_tree(stdout, &mut hashes);
wait_for_success(git, &mut stderr, "git ls-tree", root_path, parse_result)?;
Ok(hashes)
}

Expand Down Expand Up @@ -66,7 +66,7 @@ fn nom_parse_ls_tree(i: &[u8]) -> nom::IResult<&[u8], LsTreeEntry<'_>> {
let (i, _) = nom::character::complete::space1(i)?;
let (i, hash) = nom::bytes::complete::take(40usize)(i)?;
let (i, _) = nom::bytes::complete::take(1usize)(i)?;
let (i, filename) = nom::bytes::complete::is_not(" \0")(i)?;
let (i, filename) = nom::bytes::complete::is_not("\0")(i)?;
// We explicitly support a missing terminator
let (i, _) = nom::combinator::opt(nom::bytes::complete::tag(&[b'\0']))(i)?;
Ok((i, LsTreeEntry { filename, hash }))
Expand Down Expand Up @@ -110,7 +110,8 @@ mod tests {
7360f2d292aec95907cebdcbb412a6bf2bd10f8a\tapps\000100644 blob \
9ec2879b24ce2c817296eebe2cb3846f8e4751ea\tpackage.json\000040000 tree \
5759aadaea2cde55468a61e7104eb0a9d86c1d30\tpackages\000100644 blob \
33d0621ee2f4da4a2f6f6bdd51a42618d181e337\tturbo.json\0",
33d0621ee2f4da4a2f6f6bdd51a42618d181e337\tturbo.json\000100644 blob \
579f273c9536d324c20b2e8f0d7fe4784ed0d9df\tfile with spaces\0",
&[
("\t", "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"),
("\"", "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"),
Expand All @@ -121,6 +122,10 @@ mod tests {
("package.json", "9ec2879b24ce2c817296eebe2cb3846f8e4751ea"),
("packages", "5759aadaea2cde55468a61e7104eb0a9d86c1d30"),
("turbo.json", "33d0621ee2f4da4a2f6f6bdd51a42618d181e337"),
(
"file with spaces",
"579f273c9536d324c20b2e8f0d7fe4784ed0d9df",
),
],
),
];
Expand Down
8 changes: 3 additions & 5 deletions crates/turborepo-scm/src/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
use nom::Finish;
use turbopath::{AbsoluteSystemPathBuf, RelativeUnixPathBuf};

use crate::{package_deps::GitHashes, read_git_error, wait_for_success, Error};
use crate::{package_deps::GitHashes, wait_for_success, Error};

pub(crate) fn append_git_status(
root_path: &AbsoluteSystemPathBuf,
Expand Down Expand Up @@ -35,10 +35,8 @@ pub(crate) fn append_git_status(
.stderr
.take()
.ok_or_else(|| Error::git_error("failed to get stderr for git status"))?;
let to_hash = read_status(stdout, pkg_prefix, hashes)
.map_err(|err| read_git_error(&mut stderr).unwrap_or(err))?;
wait_for_success(git, &mut stderr, "git status", &root_path)?;
Ok(to_hash)
let parse_result = read_status(stdout, pkg_prefix, hashes);
wait_for_success(git, &mut stderr, "git status", &root_path, parse_result)
}

fn read_status<R: Read>(
Expand Down