From 7daad81d95a85059d88c6b5bc947e8abf62eab60 Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Wed, 12 Jun 2024 11:28:58 +0200 Subject: [PATCH] feat: add environment file to environment dir (#1495) Co-authored-by: Bas Zalmstra --- src/cli/info.rs | 4 ++-- src/cli/self_update.rs | 5 +++-- src/consts.rs | 2 ++ src/environment.rs | 33 +++++++++++++++++++++++++++++++++ src/lock_file/update.rs | 11 +++++++++++ tests/install_tests.rs | 10 +++++++++- 6 files changed, 60 insertions(+), 5 deletions(-) diff --git a/src/cli/info.rs b/src/cli/info.rs index 7c218dbbb..2073cae73 100644 --- a/src/cli/info.rs +++ b/src/cli/info.rs @@ -16,7 +16,7 @@ use crate::progress::await_in_progress; use crate::project::has_features::HasFeatures; use crate::task::TaskName; use crate::util::default_channel_config; -use crate::{config, EnvironmentName, FeatureName, Project}; +use crate::{config, consts, EnvironmentName, FeatureName, Project}; static WIDTH: usize = 18; @@ -385,7 +385,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { let info = Info { platform: Platform::current().to_string(), virtual_packages, - version: env!("CARGO_PKG_VERSION").to_string(), + version: consts::PIXI_VERSION.to_string(), cache_dir: Some(config::get_cache_dir()?), cache_size, auth_dir: auth_file, diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index edfc26ef5..45030a0fc 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -6,6 +6,7 @@ use std::{ use flate2::read::GzDecoder; use tar::Archive; +use crate::consts; use miette::{Context, IntoDiagnostic}; use reqwest::Client; use serde::Deserialize; @@ -39,7 +40,7 @@ struct GithubReleaseAsset { } fn user_agent() -> String { - format!("pixi {}", env!("CARGO_PKG_VERSION")) + format!("pixi {}", consts::PIXI_VERSION) } fn default_archive_name() -> Option { @@ -99,7 +100,7 @@ You can always use `pixi self-update --force` to force the update.", let target_version = target_version_json.tag_name.trim_start_matches('v'); // Get the current version of the pixi binary - let current_version = env!("CARGO_PKG_VERSION"); + let current_version = consts::PIXI_VERSION; // Stop here if the target version is the same as the current version if target_version == current_version { diff --git a/src/consts.rs b/src/consts.rs index 52f5a41e4..5506818e0 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -7,6 +7,7 @@ pub const PROJECT_MANIFEST: &str = "pixi.toml"; pub const PYPROJECT_MANIFEST: &str = "pyproject.toml"; pub const PROJECT_LOCK_FILE: &str = "pixi.lock"; pub const PIXI_DIR: &str = ".pixi"; +pub const PIXI_VERSION: &str = env!("CARGO_PKG_VERSION"); pub const CONFIG_FILE: &str = "config.toml"; pub const PREFIX_FILE_NAME: &str = "pixi_env_prefix"; pub const ENVIRONMENTS_DIR: &str = "envs"; @@ -24,6 +25,7 @@ pub const DEFAULT_ENVIRONMENT_NAME: &str = "default"; /// The default channels to use for a new project. pub const DEFAULT_CHANNELS: &[&str] = &["conda-forge"]; pub const DEFAULT_FEATURE_NAME: &str = DEFAULT_ENVIRONMENT_NAME; +pub const ENVIRONMENT_FILE_NAME: &str = "pixi"; lazy_static! { pub static ref TASK_STYLE: Style = Style::new().blue(); diff --git a/src/environment.rs b/src/environment.rs index 9c0bdb3d3..c30121e37 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -26,7 +26,9 @@ use rattler::{ use rattler_conda_types::{Platform, PrefixRecord, RepoDataRecord}; use rattler_lock::{PypiPackageData, PypiPackageEnvironmentData}; use reqwest_middleware::ClientWithMiddleware; +use serde::{Deserialize, Serialize}; use std::convert::identity; +use std::path::PathBuf; use std::{collections::HashMap, io::ErrorKind, path::Path}; /// Verify the location of the prefix folder is not changed so the applied prefix path is still valid. @@ -159,6 +161,37 @@ fn create_history_file(environment_dir: &Path) -> miette::Result<()> { Ok(()) } +#[derive(Serialize, Deserialize)] +pub(crate) struct EnvironmentFile { + pub(crate) manifest_path: PathBuf, + pub(crate) environment_name: String, + pub(crate) pixi_version: String, +} +/// Write information about the environment to a file in the environment directory. +/// This can be useful for other tools that only know the environment directory to find the original project. +pub fn write_environment_file( + environment_dir: &Path, + env_file: EnvironmentFile, +) -> miette::Result { + let path = environment_dir + .join("conda-meta") + .join(consts::ENVIRONMENT_FILE_NAME); + + let parent = path + .parent() + .expect("There should already be a conda-meta folder"); + + std::fs::create_dir_all(parent).into_diagnostic()?; + + // Using json as it's easier to machine read it. + let contents = serde_json::to_string_pretty(&env_file).into_diagnostic()?; + std::fs::write(&path, contents).into_diagnostic()?; + + tracing::debug!("Wrote environment file to: {:?}", path); + + Ok(path) +} + /// Runs the following checks to make sure the project is in a sane state: /// 1. It verifies that the prefix location is unchanged. /// 2. It verifies that the system requirements are met. diff --git a/src/lock_file/update.rs b/src/lock_file/update.rs index 9f48f7ec4..a734f5dd7 100644 --- a/src/lock_file/update.rs +++ b/src/lock_file/update.rs @@ -27,6 +27,7 @@ use tracing::Instrument; use url::Url; use uv_normalize::ExtraName; +use crate::environment::{write_environment_file, EnvironmentFile}; use crate::{ config, consts, environment::{ @@ -113,6 +114,16 @@ impl<'p> LockFileDerivedData<'p> { /// Returns the up-to-date prefix for the given environment. pub async fn prefix(&mut self, environment: &Environment<'p>) -> miette::Result { + // Save an environment file to the environment directory + write_environment_file( + &environment.dir(), + EnvironmentFile { + manifest_path: environment.project().manifest_path(), + environment_name: environment.name().to_string(), + pixi_version: consts::PIXI_VERSION.to_string(), + }, + )?; + if let Some(prefix) = self.updated_pypi_prefixes.get(environment) { return Ok(prefix.clone()); } diff --git a/tests/install_tests.rs b/tests/install_tests.rs index d0ce82881..768cb29d3 100644 --- a/tests/install_tests.rs +++ b/tests/install_tests.rs @@ -12,7 +12,7 @@ use pixi::cli::run::Args; use pixi::cli::{run, LockFileUsageArgs}; use pixi::config::{Config, DetachedEnvironments}; use pixi::consts::{DEFAULT_ENVIRONMENT_NAME, PIXI_UV_INSTALLER}; -use pixi::FeatureName; +use pixi::{consts, FeatureName}; use rattler_conda_types::Platform; use serial_test::serial; use tempfile::TempDir; @@ -47,6 +47,14 @@ async fn install_run_python() { assert_eq!(result.exit_code, 0); assert_eq!(result.stdout.trim(), "Python 3.11.0"); assert!(result.stderr.is_empty()); + + // Test for existence of environment file + assert!(pixi + .default_env_path() + .unwrap() + .join("conda-meta") + .join(consts::ENVIRONMENT_FILE_NAME) + .exists()) } /// This is a test to check that creating incremental lock files works.