Use local copy of gourgeist (#62)

This PR gets `gourgeist` passing our local CI and integrated into the
broader workspace.

There's some duplicate between concepts in `gourgeist` (like the
`InterpreterInfo`) and structs we have elsewhere, but we can tackle
those later.
This commit is contained in:
Charlie Marsh 2023-10-08 14:45:08 -04:00 committed by GitHub
parent 7caf5f42b8
commit 0ca17a1cf2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 207 additions and 1572 deletions

File diff suppressed because it is too large Load diff

View file

@ -8,54 +8,27 @@ license = "MIT OR Apache-2.0"
keywords = ["virtualenv", "venv", "python"]
readme = "Readme.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
camino = { version = "1.1.6", features = ["serde1"] }
clap = { version = "4.4.5", features = ["derive"] }
configparser = "3.0.2"
dirs = "5.0.1"
fs-err = "2.9.0"
install-wheel-rs = { version = "0.0.1", optional = true }
minreq = { version = "2.10.0", optional = true, features = ["https"] }
rayon = { version = "1.8.0", optional = true }
seahash = "4.1.0"
serde = { version = "1.0.188", features = ["derive"] }
serde_json = "1.0.107"
tempfile = "3.8.0"
thiserror = "1.0.49"
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
which = "4.4.2"
install-wheel-rs = { path = "../install-wheel-rs", optional = true }
wheel-filename = { path = "../wheel-filename" }
camino = { workspace = true }
clap = { workspace = true }
configparser = { workspace = true }
dirs = { workspace = true }
fs-err = { workspace = true }
reqwest = { workspace = true, optional = true, features = ["blocking"] }
rayon = { workspace = true, optional = true }
seahash = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tempfile = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
which = { workspace = true }
[features]
default = ["install"]
install = ["install-wheel-rs", "minreq"]
install = ["install-wheel-rs", "reqwest"]
parallel = ["rayon"]
# zip implementation
[profile.dev.package.adler]
opt-level = 3
[profile.profiling]
inherits = "release"
lto = "thin"
debug = true
# The profile that 'cargo dist' will build with
[profile.dist]
inherits = "release"
lto = "thin"
# Config for 'cargo dist'
[workspace.metadata.dist]
# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.3.1"
# CI backends to support
ci = ["github"]
# The installers to generate for each app
installers = ["shell", "powershell"]
# Target platforms to build apps for (Rust target-triple syntax)
targets = ["x86_64-unknown-linux-gnu", "aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-pc-windows-msvc"]
# Publish jobs to run in CI
pr-run-mode = "plan"

View file

@ -1,15 +1,17 @@
//! Create a bare virtualenv without any packages install
use crate::interpreter::InterpreterInfo;
use std::io;
use std::io::{BufWriter, Write};
use camino::{Utf8Path, Utf8PathBuf};
use fs_err as fs;
#[cfg(unix)]
use fs_err::os::unix::fs::symlink;
use fs_err::File;
use std::io;
use std::io::{BufWriter, Write};
use tracing::info;
use crate::interpreter::InterpreterInfo;
/// The bash activate scripts with the venv dependent paths patches out
const ACTIVATE_TEMPLATES: &[(&str, &str)] = &[
("activate", include_str!("activator/activate")),
@ -27,27 +29,34 @@ const VIRTUALENV_PATCH: &str = include_str!("_virtualenv.py");
/// Very basic `.cfg` file format writer.
fn write_cfg(f: &mut impl Write, data: &[(&str, String); 8]) -> io::Result<()> {
for (key, value) in data {
writeln!(f, "{} = {}", key, value)?;
writeln!(f, "{key} = {value}")?;
}
Ok(())
}
/// Absolute paths of the virtualenv
#[derive(Debug)]
pub struct VenvPaths {
pub(crate) struct VenvPaths {
/// The location of the virtualenv, e.g. `.venv`
pub root: Utf8PathBuf,
#[allow(unused)]
pub(crate) root: Utf8PathBuf,
/// The python interpreter.rs inside the virtualenv, on unix `.venv/bin/python`
pub interpreter: Utf8PathBuf,
#[allow(unused)]
pub(crate) interpreter: Utf8PathBuf,
/// The directory with the scripts, on unix `.venv/bin`
pub bin: Utf8PathBuf,
#[allow(unused)]
pub(crate) bin: Utf8PathBuf,
/// The site-packages directory where all the packages are installed to, on unix
/// and python 3.11 `.venv/lib/python3.11/site-packages`
pub site_packages: Utf8PathBuf,
#[allow(unused)]
pub(crate) site_packages: Utf8PathBuf,
}
/// Write all the files that belong to a venv without any packages installed.
pub fn create_bare_venv(
pub(crate) fn create_bare_venv(
location: &Utf8Path,
base_python: &Utf8Path,
info: &InterpreterInfo,

View file

@ -1,14 +1,16 @@
use crate::{crate_cache_dir, Error};
use camino::{FromPathBufError, Utf8Path, Utf8PathBuf};
use fs_err as fs;
use fs_err::File;
use serde::{Deserialize, Serialize};
use std::io;
use std::io::{BufReader, Write};
use std::process::{Command, Stdio};
use std::time::SystemTime;
use camino::{Utf8Path, Utf8PathBuf};
use fs_err as fs;
use fs_err::File;
use serde::{Deserialize, Serialize};
use tracing::{debug, error, info, warn};
use crate::{crate_cache_dir, Error};
const QUERY_PYTHON: &str = include_str!("query_python.py");
#[derive(Clone, Debug, Deserialize, Serialize)]
@ -44,20 +46,19 @@ pub fn get_interpreter_info(interpreter: &Utf8Path) -> Result<InterpreterInfo, E
debug!("Using cache entry {cache_file}");
if modified == cache_entry.modified && interpreter == cache_entry.interpreter {
return Ok(cache_entry.interpreter_info);
} else {
debug!(
"Removing mismatching cache entry {cache_file} ({} {} {} {})",
modified, cache_entry.modified, interpreter, cache_entry.interpreter
);
if let Err(remove_err) = fs::remove_file(&cache_file) {
warn!("Failed to mismatching cache file at {cache_file}: {remove_err}")
}
}
debug!(
"Removing mismatching cache entry {cache_file} ({} {} {} {})",
modified, cache_entry.modified, interpreter, cache_entry.interpreter
);
if let Err(remove_err) = fs::remove_file(&cache_file) {
warn!("Failed to mismatching cache file at {cache_file}: {remove_err}");
}
}
Err(cache_err) => {
debug!("Removing broken cache entry {cache_file} ({cache_err})");
if let Err(remove_err) = fs::remove_file(&cache_file) {
warn!("Failed to remove broken cache file at {cache_file}: {remove_err} (original error: {cache_err})")
warn!("Failed to remove broken cache file at {cache_file}: {remove_err} (original error: {cache_err})");
}
}
}
@ -188,7 +189,7 @@ pub fn parse_python_cli(cli_python: Option<Utf8PathBuf>) -> Result<Utf8PathBuf,
)
})?
.try_into()
.map_err(|err: FromPathBufError| err.into_io_error())?;
.map_err(camino::FromPathBufError::into_io_error)?;
info!("Resolved {python} to {python_in_path}");
python_in_path
};

View file

@ -1,13 +1,15 @@
use crate::bare::create_bare_venv;
use std::io;
use camino::{Utf8Path, Utf8PathBuf};
use dirs::cache_dir;
use interpreter::InterpreterInfo;
use std::io;
use tempfile::PersistError;
use thiserror::Error;
use interpreter::InterpreterInfo;
pub use interpreter::{get_interpreter_info, parse_python_cli};
use crate::bare::create_bare_venv;
mod bare;
mod interpreter;
#[cfg(feature = "install")]
@ -40,7 +42,7 @@ pub enum Error {
},
#[cfg(feature = "install")]
#[error("Failed to contact pypi")]
MinReq(#[from] minreq::Error),
Request(#[from] reqwest::Error),
#[cfg(feature = "install")]
#[error("Failed to install {package}")]
InstallWheel {

View file

@ -1,14 +1,16 @@
use camino::Utf8PathBuf;
use clap::Parser;
use gourgeist::{create_venv, get_interpreter_info, parse_python_cli};
use std::error::Error;
use std::process::ExitCode;
use std::time::Instant;
use camino::Utf8PathBuf;
use clap::Parser;
use tracing::info;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{fmt, EnvFilter};
use gourgeist::{create_venv, get_interpreter_info, parse_python_cli};
#[derive(Parser, Debug)]
struct Cli {
path: Option<Utf8PathBuf>,
@ -38,10 +40,17 @@ fn main() -> ExitCode {
let result = run();
info!("Took {}ms", start.elapsed().as_millis());
if let Err(err) = result {
eprintln!("💥 virtualenv creator failed");
#[allow(clippy::print_stderr)]
{
eprintln!("💥 virtualenv creator failed");
}
let mut last_error: Option<&(dyn Error + 'static)> = Some(&err);
while let Some(err) = last_error {
eprintln!(" Caused by: {}", err);
#[allow(clippy::print_stderr)]
{
eprintln!(" Caused by: {err}");
}
last_error = err.source();
}
ExitCode::FAILURE

View file

@ -1,19 +1,23 @@
use crate::bare::VenvPaths;
use crate::interpreter::InterpreterInfo;
use crate::{crate_cache_dir, Error};
use camino::{FromPathBufError, Utf8Path, Utf8PathBuf};
use fs_err as fs;
use fs_err::File;
use install_wheel_rs::{install_wheel, InstallLocation, WheelFilename};
#[cfg(feature = "parallel")]
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use std::io;
use std::io::BufWriter;
use std::str::FromStr;
use camino::{Utf8Path, Utf8PathBuf};
use fs_err as fs;
use fs_err::File;
#[cfg(feature = "parallel")]
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use tempfile::NamedTempFile;
use tracing::info;
pub fn download_wheel_cached(filename: &str, url: &str) -> Result<Utf8PathBuf, Error> {
use install_wheel_rs::{install_wheel, InstallLocation};
use wheel_filename::WheelFilename;
use crate::bare::VenvPaths;
use crate::interpreter::InterpreterInfo;
use crate::{crate_cache_dir, Error};
pub(crate) fn download_wheel_cached(filename: &str, url: &str) -> Result<Utf8PathBuf, Error> {
let wheels_cache = crate_cache_dir()?.join("wheels");
let cached_wheel = wheels_cache.join(filename);
if cached_wheel.is_file() {
@ -28,8 +32,8 @@ pub fn download_wheel_cached(filename: &str, url: &str) -> Result<Utf8PathBuf, E
.path()
.to_path_buf()
.try_into()
.map_err(|err: FromPathBufError| err.into_io_error())?;
let mut response = minreq::get(url).send_lazy()?;
.map_err(camino::FromPathBufError::into_io_error)?;
let mut response = reqwest::blocking::get(url)?;
io::copy(&mut response, &mut BufWriter::new(&mut tempfile)).map_err(|err| {
Error::WheelDownload {
url: url.to_string(),
@ -42,7 +46,7 @@ pub fn download_wheel_cached(filename: &str, url: &str) -> Result<Utf8PathBuf, E
}
/// Install pip, setuptools and wheel from cache pypi with atm fixed wheels
pub fn install_base_packages(
pub(crate) fn install_base_packages(
location: &Utf8Path,
info: &InterpreterInfo,
paths: &VenvPaths,
@ -71,7 +75,8 @@ pub fn install_base_packages(
install_wheel(
&install_location,
File::open(wheel_file)?,
parsed_filename,
&parsed_filename,
false,
false,
&[],
// Only relevant for monotrail style installation

View file

@ -1,13 +1,15 @@
//! Deprecated, use only as template when implementing caching
use crate::Error;
use std::io;
use std::path::Path;
use camino::{Utf8Path, Utf8PathBuf};
use dirs::data_dir;
use fs_err as fs;
use std::io;
use std::path::Path;
use tracing::debug;
use crate::Error;
/// Install wheel, pip and setuptools from the cache
pub(crate) fn install_base_packages(
bin_dir: &Utf8Path,