Skip to content
Draft
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
2 changes: 2 additions & 0 deletions cli/adopt/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ rust_library(
"//cli/ide:dx_ide",
"//cli/merge:dx_merge",
"//cli/path:dx_path",
"//cli/process:dx_process",
"//libs/families:dx_families",
"//libs/gomod:dx_gomod",
"//libs/module_bazel:dx_module_bazel",
Expand Down Expand Up @@ -249,6 +250,7 @@ rust_test(
"//cli/ide:dx_ide",
"//cli/merge:dx_merge",
"//cli/path:dx_path",
"//cli/process:dx_process",
"//cli/test_scratch:dx_test_scratch",
"//libs/families:dx_families",
"//libs/gomod:dx_gomod",
Expand Down
38 changes: 18 additions & 20 deletions cli/adopt/src/build_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,30 +201,30 @@ struct Git<'a> {
}

impl Git<'_> {
fn run(&self, args: &[&str]) -> Option<std::process::Output> {
let mut cmd = Command::new("git");
cmd.arg("-C").arg(self.root).args(args);
fn run(&self, args: &[&str]) -> Option<dx_process::capture::Captured> {
let mut cmd = dx_process::capture::git(self.root);
cmd.args(args);
(self.configure)(&mut cmd);
cmd.output().ok()
dx_process::capture::run_captured(&mut cmd, dx_process::capture::DEFAULT_TIMEOUT).ok()
}

fn ok_text(&self, args: &[&str]) -> Option<String> {
let output = self.run(args)?;
if !output.status.success() {
if !output.success() {
return None;
}
let text = String::from_utf8_lossy(&output.stdout).trim().to_owned();
let text = output.stdout_text().trim().to_owned();
(!text.is_empty()).then_some(text)
}

fn stdout(&self, args: &[&str]) -> Result<Vec<u8>, String> {
let output = self
.run(args)
.ok_or_else(|| "git did not start".to_owned())?;
if output.status.success() {
if output.success() {
return Ok(output.stdout);
}
let stderr = String::from_utf8_lossy(&output.stderr);
let stderr = output.stderr_text();
let line = stderr.lines().next().unwrap_or("git failed").trim();
Err(line.to_owned())
}
Expand Down Expand Up @@ -272,7 +272,7 @@ fn scan_with(root: &Path, configure: &dyn Fn(&mut Command)) -> Result<ScanReport
let Some(inside) = git.run(&["rev-parse", "--is-inside-work-tree"]) else {
return Ok(report);
};
if !inside.status.success() {
if !inside.success() {
return Ok(report);
}
let mut paths: Vec<(String, bool)> = Vec::new();
Expand Down Expand Up @@ -489,10 +489,8 @@ mod tests {
}

fn git(root: &Path, args: &[&str]) {
let mut cmd = Command::new("git");
cmd.arg("-C")
.arg(root)
.args([
let mut cmd = dx_process::capture::git(root);
cmd.args([
"-c",
"user.name=t",
"-c",
Expand All @@ -502,8 +500,8 @@ mod tests {
])
.args(args);
dx_test_scratch::isolate_git(&mut cmd, sandbox_of(root));
let status = cmd.status().expect("git");
assert!(status.success(), "{args:?}");
dx_process::capture::run_checked(&mut cmd, dx_process::capture::DEFAULT_TIMEOUT)
.unwrap_or_else(|error| panic!("{args:?}: {error}"));
}

fn write(root: &Path, rel: &str, body: &[u8]) {
Expand Down Expand Up @@ -651,11 +649,11 @@ mod tests {
#[test]
fn scan_outside_git_finds_nothing() {
let root = temp();
let version = Command::new("git")
.arg("--version")
.output()
.expect("git runs");
assert!(version.status.success(), "git is installed");
dx_process::capture::run_checked(
dx_process::capture::git(&root).arg("--version"),
dx_process::capture::DEFAULT_TIMEOUT,
)
.expect("git is installed");
write(&root, "target/x", b"x");
assert_eq!(scan_isolated(&root), Ok(ScanReport::default()));
}
Expand Down
29 changes: 8 additions & 21 deletions cli/adopt/src/families.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,39 +228,26 @@ fn git_files(root: &Path) -> Option<Vec<String>> {
if !root.join(".git").exists() {
return None;
}
let mut git = std::process::Command::new("git");
for var in [
"GIT_DIR",
"GIT_WORK_TREE",
"GIT_INDEX_FILE",
"GIT_COMMON_DIR",
"GIT_OBJECT_DIRECTORY",
] {
git.env_remove(var);
}
let mut git = dx_process::capture::git(root);
if let Some(parent) = root
.canonicalize()
.ok()
.and_then(|dir| dir.parent().map(Path::to_path_buf))
{
git.env("GIT_CEILING_DIRECTORIES", parent);
}
let out = git
.arg("-C")
.arg(root)
.args([
let out = dx_process::capture::run_checked(
git.args([
"ls-files",
"-z",
"--cached",
"--others",
"--exclude-standard",
])
.output()
.ok()?;
if !out.status.success() {
return None;
}
let text = String::from_utf8_lossy(&out.stdout);
]),
dx_process::capture::DEFAULT_TIMEOUT,
)
.ok()?;
let text = out.stdout_text();
Some(
text.split('\0')
.filter(|path| !path.is_empty())
Expand Down
15 changes: 5 additions & 10 deletions cli/adopt/src/git_changes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,11 @@ pub fn is_dx_state(path: &str) -> bool {

/// True when git ignores the generated `.dx` files, so they would not be committed.
pub fn dx_config_git_ignored(root: &Path) -> bool {
std::process::Command::new("git")
.arg("-C")
.arg(root)
.args(["check-ignore", "-q", ".dx/BUILD.bazel"])
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|status| status.code() == Some(0))
.unwrap_or(false)
dx_process::capture::run_captured(
dx_process::capture::git(root).args(["check-ignore", "-q", ".dx/BUILD.bazel"]),
dx_process::capture::DEFAULT_TIMEOUT,
)
.is_ok_and(|output| output.success())
}

/// Sorts changes by path and drops duplicate paths, keeping rename sources.
Expand Down
10 changes: 5 additions & 5 deletions cli/adopt/src/preset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,11 @@ fn ignore_with_missing(text: Option<String>) -> Option<String> {

/// Whether git lists files under `.dx/bazelrc`; false when git is missing or this is no repository.
fn layers_tracked(root: &Path) -> bool {
std::process::Command::new("git")
.args(["ls-files", "--", CONSUMER_DIR])
.current_dir(root)
.output()
.is_ok_and(|out| out.status.success() && !out.stdout.is_empty())
dx_process::capture::run_checked(
dx_process::capture::git(root).args(["ls-files", "--", CONSUMER_DIR]),
dx_process::capture::DEFAULT_TIMEOUT,
)
.is_ok_and(|out| !out.stdout.is_empty())
}

fn tracked_note() -> String {
Expand Down
53 changes: 21 additions & 32 deletions cli/adopt/src/scaffold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,14 +271,11 @@ impl CiProvider {
if root.join(".github/workflows/ci.yml").exists() {
return Self::Github;
}
let gitlab_remote = std::process::Command::new("git")
.arg("-C")
.arg(root)
.args(["remote", "get-url", "origin"])
.output()
.is_ok_and(|out| {
out.status.success() && String::from_utf8_lossy(&out.stdout).contains("gitlab")
});
let gitlab_remote = dx_process::capture::run_checked(
dx_process::capture::git(root).args(["remote", "get-url", "origin"]),
dx_process::capture::DEFAULT_TIMEOUT,
)
.is_ok_and(|out| out.stdout_text().contains("gitlab"));
if gitlab_remote {
Self::Gitlab
} else {
Expand All @@ -292,16 +289,12 @@ pub const DEFAULT_BRANCH_FALLBACK: &str = "main";
/// The repo's default branch: `origin/HEAD`, then `init.defaultBranch`, then `main`.
pub fn detect_default_branch(root: &Path) -> String {
let git = |args: &[&str]| -> Option<String> {
let out = std::process::Command::new("git")
.arg("-C")
.arg(root)
.args(args)
.output()
.ok()?;
if !out.status.success() {
return None;
}
Some(String::from_utf8_lossy(&out.stdout).trim().to_owned())
let out = dx_process::capture::run_checked(
dx_process::capture::git(root).args(args),
dx_process::capture::DEFAULT_TIMEOUT,
)
.ok()?;
Some(out.stdout_text().trim().to_owned())
};
let from_origin = git(&["symbolic-ref", "--short", "refs/remotes/origin/HEAD"])
.and_then(|name| name.strip_prefix("origin/").map(str::to_owned))
Expand Down Expand Up @@ -1611,13 +1604,11 @@ mod tests {
let dir = std::env::temp_dir().join(format!("dx-adopt-branch-{}", std::process::id()));
std::fs::create_dir_all(&dir).expect("temp dir");
let git = |args: &[&str]| {
std::process::Command::new("git")
.arg("-C")
.arg(&dir)
.args(args)
.output()
.map(|o| o.status.success())
.unwrap_or(false)
dx_process::capture::run_checked(
dx_process::capture::git(&dir).args(args),
dx_process::capture::DEFAULT_TIMEOUT,
)
.is_ok()
};
if git(&["init", "-q"]) && git(&["config", "init.defaultBranch", "trunk"]) {
assert_eq!(super::detect_default_branch(&dir), "trunk");
Expand All @@ -1636,13 +1627,11 @@ mod tests {
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).expect("temp dir");
let git = |args: &[&str]| {
std::process::Command::new("git")
.arg("-C")
.arg(&dir)
.args(args)
.output()
.map(|o| o.status.success())
.unwrap_or(false)
dx_process::capture::run_checked(
dx_process::capture::git(&dir).args(args),
dx_process::capture::DEFAULT_TIMEOUT,
)
.is_ok()
};
if git(&["init", "-q"])
&& git(&["config", "init.defaultBranch", "trunk"])
Expand Down
2 changes: 2 additions & 0 deletions cli/audit/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ rust_library(
) + [
"//cli/digest:dx_digest",
"//cli/dx_config",
"//cli/process:dx_process",
"//cli/sbom:dx_sbom",
"//libs/gomod:dx_gomod",
"//libs/module_bazel:dx_module_bazel",
Expand Down Expand Up @@ -189,6 +190,7 @@ rust_test(
) + [
"//cli/digest:dx_digest",
"//cli/dx_config",
"//cli/process:dx_process",
"//cli/sbom:dx_sbom",
"//cli/sbom/schema:dx_sbom_schema",
"//libs/gomod:dx_gomod",
Expand Down
14 changes: 7 additions & 7 deletions cli/audit/src/image_scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use serde::Deserialize;
use crate::advisory::{AdvisoryPin, CODE_ADVISORY_REFRESH_FAILED, PIN_MAX_AGE_DAYS, TRIVY_FAMILY};
use crate::exception::RiskException;

const FETCH_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(900);

pub const SEVERITIES: [&str; 4] = ["LOW", "MEDIUM", "HIGH", "CRITICAL"];

/// The exception set that covers every image.
Expand Down Expand Up @@ -184,13 +186,11 @@ fn fetch_trivy_pin(pin: &AdvisoryPin, cache: &Path, offline: bool) -> Result<(),
1,
)
.map_err(|error| error.to_string())?;
let done = Command::new(&plan.program)
.args(&plan.argv)
.status()
.map_err(|error| format!("{}: {error}", plan.program))?;
if !done.success() {
return Err(format!("{}: {} {done}", pin.url, plan.program));
}
dx_process::capture::run_checked(
Command::new(&plan.program).args(&plan.argv),
FETCH_TIMEOUT,
)
.map_err(|error| format!("{}: {error}", pin.url))?;
let bytes = std::fs::read(&archive).map_err(|error| format!("{}: {error}", pin.url));
let _ = std::fs::remove_file(&archive);
bytes?
Expand Down
19 changes: 6 additions & 13 deletions cli/cli/src/adopt/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,19 +152,12 @@ fn tool_config(tool: &str) -> Result<&'static ToolConfig, String> {
}

pub(super) fn blob_sha(workspace: &Path, base: &str) -> Result<String, String> {
let output = std::process::Command::new("git")
.arg("-C")
.arg(workspace)
.args(["hash-object", "--", base])
.output()
.map_err(|error| format!("cannot run git hash-object: {error}"))?;
if !output.status.success() {
return Err(format!(
"git hash-object {base} failed: {}",
String::from_utf8_lossy(&output.stderr).trim()
));
}
Ok(String::from_utf8_lossy(&output.stdout).trim().to_owned())
let output = dx_process::capture::run_checked(
dx_process::capture::git(workspace).args(["hash-object", "--", base]),
dx_process::capture::DEFAULT_TIMEOUT,
)
.map_err(|error| error.to_string())?;
Ok(output.stdout_text().trim().to_owned())
}

/// Copies the base to the tool's root file behind a `dx: override of base <sha>` comment and
Expand Down
24 changes: 8 additions & 16 deletions cli/cli/src/adopt/upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,22 +455,14 @@ fn dx_snapshot(workspace: &Path) -> BTreeMap<String, Vec<u8>> {
}

fn git(workspace: &Path, args: &[&str], input: Option<&[u8]>) -> Option<Vec<u8>> {
let mut child = std::process::Command::new("git")
.arg("-C")
.arg(workspace)
.args(args)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::null())
.spawn()
.ok()?;
let mut stdin = child.stdin.take()?;
if let Some(bytes) = input {
stdin.write_all(bytes).ok()?;
}
drop(stdin);
let output = child.wait_with_output().ok()?;
output.status.success().then_some(output.stdout)
dx_process::capture::run_captured_with_input(
dx_process::capture::git(workspace).args(args),
input,
dx_process::capture::DEFAULT_TIMEOUT,
)
.and_then(dx_process::capture::Captured::into_success)
.ok()
.map(|output| output.stdout)
}

/// The old base an override copy was taken from: the pre-upgrade base when its blob sha
Expand Down
14 changes: 7 additions & 7 deletions cli/cli/src/exec/audit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,16 +436,16 @@ fn fetch_pinned(url: &str) -> Result<Vec<u8>, String> {
1,
)
.map_err(|error| error.to_string())?;
let done = std::process::Command::new(&plan.program)
.args(&plan.argv)
.status()
.map_err(|error| format!("{}: {error}", plan.program))?;
if !done.success() {
return Err(format!("{url}: {} {done}", plan.program));
}
dx_process::capture::run_checked(
std::process::Command::new(&plan.program).args(&plan.argv),
FETCH_TIMEOUT,
)
.map_err(|error| format!("{url}: {error}"))?;
std::fs::read(&file).map_err(|error| format!("{url}: {error}"))
}

const FETCH_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(900);

const NO_DEPENDENCY_SETS: &str = "no dependency sets: this workspace declares no hubs in MODULE.bazel. Add one, for example:\n\ncrate = use_extension(\"@rules_rust//crate_universe:extensions.bzl\", \"crate\")\ncrate.from_cargo(\n name = \"crates\",\n cargo_lockfile = \"//:Cargo.lock\",\n manifests = [\"//:Cargo.toml\"],\n)";

/// Which lockfile each parsed package was read from.
Expand Down
Loading
Loading