Skip to main content

idt/downloaders/
pip.rs

1use std::path::Path;
2use std::path::PathBuf;
3
4use rootcause::prelude::ResultExt as _;
5
6/// Downloads and installs Python packages using pip in a virtual environment.
7///
8/// # Errors
9/// - Executing the `python3 -m venv` command fails or returns a non-zero exit status.
10/// - Executing the shell pipeline to activate the venv and install packages fails.
11/// - A filesystem operation (create/read/write/remove) fails.
12pub fn run(dev_tools_dir: &Path, tool: &str, packages: &[&str]) -> rootcause::Result<PathBuf> {
13    let dev_tools_repo_dir = dev_tools_dir.join(tool);
14
15    std::fs::create_dir_all(&dev_tools_repo_dir)
16        .context("error creating pip tool directory")
17        .attach_with(|| format!("path={}", dev_tools_repo_dir.display()))?;
18
19    ytil_cmd::silent_cmd("python3")
20        .args(["-m", "venv", &dev_tools_repo_dir.join(".venv").to_string_lossy()])
21        .status()
22        .context("failed to spawn python3 venv")?
23        .exit_ok()
24        .context("python3 venv creation failed")
25        .attach_with(|| format!("tool={tool}"))
26        .attach_with(|| format!("venv_dir={}", dev_tools_repo_dir.join(".venv").display()))?;
27
28    ytil_cmd::silent_cmd("sh")
29        .args([
30            "-c",
31            &format!(
32                r"
33                    source {}/.venv/bin/activate && \
34                    pip install pip {packages} --upgrade
35                ",
36                dev_tools_repo_dir.display(),
37                packages = packages.join(" "),
38            ),
39        ])
40        .status()
41        .context("failed to spawn pip install")?
42        .exit_ok()
43        .context("pip install failed")
44        .attach_with(|| format!("tool={tool}"))
45        .attach_with(|| format!("packages={packages:?}"))?;
46
47    Ok(dev_tools_repo_dir.join(".venv").join("bin"))
48}