diff --git a/src/cli/mod.rs b/src/cli/mod.rs index a3a4d1115..7dbf22bc6 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -85,7 +85,9 @@ pub async fn execute() -> miette::Result<()> { .from_env() .into_diagnostic()? // filter logs from apple codesign because they are very noisy - .add_directive("apple_codesign=off".parse().into_diagnostic()?); + .add_directive("apple_codesign=off".parse().into_diagnostic()?) + // set pixi's tracing level to warn + .add_directive("pixi=warn".parse().into_diagnostic()?); // Setup the tracing subscriber tracing_subscriber::fmt() diff --git a/src/cli/run.rs b/src/cli/run.rs index ea127520c..a7c4aefa7 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -221,9 +221,13 @@ pub async fn get_task_env(project: &Project) -> miette::Result miette::Result miette::Result> { +async fn run_activation( + prefix: Prefix, + additional_activation_scripts: Vec, +) -> miette::Result> { let activator_result = tokio::task::spawn_blocking(move || { // Run and cache the activation script let shell: ShellEnum = ShellEnum::default(); // Construct an activator for the script - let activator = Activator::from_path(prefix.root(), shell, Platform::current())?; + let mut activator = Activator::from_path(prefix.root(), shell, Platform::current())?; + activator + .activation_scripts + .extend(additional_activation_scripts); // Run the activation activator.run_activation(ActivationVariables { diff --git a/src/cli/shell.rs b/src/cli/shell.rs index a794fe434..5fea8e23f 100644 --- a/src/cli/shell.rs +++ b/src/cli/shell.rs @@ -24,9 +24,15 @@ pub async fn execute(args: Args) -> miette::Result<()> { // Construct an activator so we can run commands from the environment let prefix = get_up_to_date_prefix(&project).await?; - let activator = Activator::from_path(prefix.root(), shell.clone(), Platform::current()) + let activation_scripts: Vec<_> = project + .activation_scripts(Platform::current())? + .into_iter() + .collect(); + let mut activator = Activator::from_path(prefix.root(), shell.clone(), Platform::current()) .into_diagnostic()?; + activator.activation_scripts.extend(activation_scripts); + let activator_result = activator .activation(ActivationVariables { // Get the current PATH variable diff --git a/src/project/manifest.rs b/src/project/manifest.rs index 06f28f354..7e09bcc20 100644 --- a/src/project/manifest.rs +++ b/src/project/manifest.rs @@ -56,6 +56,12 @@ pub struct ProjectManifest { /// manifest. #[serde(default)] pub target: IndexMap, TargetMetadata>, + + /// Environment activation information. + /// + /// We use an [`IndexMap`] to preserve the order in which the items where defined in the + /// manifest. + pub activation: Option, } impl ProjectManifest { @@ -131,6 +137,10 @@ pub struct TargetMetadata { #[serde(default, rename = "build-dependencies")] #[serde_as(as = "Option>")] pub build_dependencies: Option>, + + /// Additional information to activate an environment. + #[serde(default)] + pub activation: Option, } /// Describes the contents of the `[package]` section of the project manifest. @@ -255,6 +265,11 @@ impl From for LibC { } } } +#[derive(Default, Clone, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct Activation { + pub scripts: Option>, +} // Create an error report for usign a platform that is not supported by the project. fn create_unsupported_platform_report( @@ -345,4 +360,25 @@ mod test { .collect::>() .join("\n")) } + + #[test] + fn test_activation_scripts() { + let contents = format!( + r#" + {PROJECT_BOILERPLATE} + [activation] + scripts = [".pixi/install/setup.sh"] + + [target.win-64.activation] + scripts = [".pixi/install/setup.ps1"] + + [target.linux-64.activation] + scripts = [".pixi/install/setup.sh", "test"] + "# + ); + + assert_debug_snapshot!( + toml_edit::de::from_str::(&contents).expect("parsing should succeed!") + ); + } } diff --git a/src/project/mod.rs b/src/project/mod.rs index 5f4afd937..1f5e9ce3e 100644 --- a/src/project/mod.rs +++ b/src/project/mod.rs @@ -503,6 +503,48 @@ impl Project { self.manifest.project.platforms.as_ref().as_slice() } + /// Returns the all specified activation scripts that are used in the current platform. + pub fn activation_scripts(&self, platform: Platform) -> miette::Result> { + let mut full_paths = Vec::new(); + let mut all_scripts = Vec::new(); + + // Gather platform-specific activation scripts + for target_metadata in self.target_specific_metadata(platform) { + if let Some(activation) = &target_metadata.activation { + if let Some(scripts) = &activation.scripts { + all_scripts.extend(scripts.clone()); + } + } + } + + // Gather the main activation scripts if there are no target scripts defined. + if all_scripts.is_empty() { + if let Some(activation) = &self.manifest.activation { + if let Some(scripts) = &activation.scripts { + all_scripts.extend(scripts.clone()); + } + } + } + + // Check if scripts exist + let mut missing_scripts = Vec::new(); + for script_name in &all_scripts { + let script_path = self.root().join(script_name); + if script_path.exists() { + full_paths.push(script_path); + tracing::debug!("Found activation script: {:?}", script_name); + } else { + missing_scripts.push(script_name); + } + } + + if !missing_scripts.is_empty() { + tracing::warn!("can't find activation scripts: {:?}", missing_scripts); + } + + Ok(full_paths) + } + /// Get the task with the specified name or `None` if no such task exists. pub fn task_opt(&self, name: &str) -> Option<&Task> { self.manifest.tasks.get(name) @@ -742,4 +784,33 @@ mod tests { assert_debug_snapshot!(project.all_dependencies(Platform::Linux64).unwrap()); } + #[test] + fn test_activation_scripts() { + // Using known files in the project so the test succeed including the file check. + let file_contents = r#" + [target.linux-64.activation] + scripts = ["Cargo.toml"] + + [target.win-64.activation] + scripts = ["Cargo.lock"] + + [activation] + scripts = ["pixi.toml", "pixi.lock"] + "#; + + let manifest = toml_edit::de::from_str::(&format!( + "{PROJECT_BOILERPLATE}\n{file_contents}" + )) + .unwrap(); + let project = Project { + root: Default::default(), + source: "".to_string(), + doc: Default::default(), + manifest, + }; + + assert_debug_snapshot!(project.activation_scripts(Platform::Linux64).unwrap()); + assert_debug_snapshot!(project.activation_scripts(Platform::Win64).unwrap()); + assert_debug_snapshot!(project.activation_scripts(Platform::OsxArm64).unwrap()); + } } diff --git a/src/project/snapshots/pixi__project__manifest__test__activation_scripts.snap b/src/project/snapshots/pixi__project__manifest__test__activation_scripts.snap new file mode 100644 index 000000000..a9248025f --- /dev/null +++ b/src/project/snapshots/pixi__project__manifest__test__activation_scripts.snap @@ -0,0 +1,83 @@ +--- +source: src/project/manifest.rs +expression: "toml_edit::de::from_str::(&contents).expect(\"parsing should succeed!\")" +--- +ProjectManifest { + project: ProjectMetadata { + name: "foo", + version: Version { + version: [[0], [0], [1], [0]], + local: [], + }, + description: None, + authors: [], + channels: [], + platforms: Spanned { + span: 121..123, + value: [], + }, + }, + tasks: {}, + system_requirements: SystemRequirements { + windows: None, + unix: None, + macos: None, + linux: None, + cuda: None, + libc: None, + archspec: None, + }, + dependencies: {}, + host_dependencies: None, + build_dependencies: None, + target: { + Spanned { + span: 228..234, + value: Platform( + Win64, + ), + }: TargetMetadata { + dependencies: {}, + host_dependencies: None, + build_dependencies: None, + activation: Some( + Activation { + scripts: Some( + [ + ".pixi/install/setup.ps1", + ], + ), + }, + ), + }, + Spanned { + span: 318..326, + value: Platform( + Linux64, + ), + }: TargetMetadata { + dependencies: {}, + host_dependencies: None, + build_dependencies: None, + activation: Some( + Activation { + scripts: Some( + [ + ".pixi/install/setup.sh", + "test", + ], + ), + }, + ), + }, + }, + activation: Some( + Activation { + scripts: Some( + [ + ".pixi/install/setup.sh", + ], + ), + }, + ), +} diff --git a/src/project/snapshots/pixi__project__manifest__test__dependency_types.snap b/src/project/snapshots/pixi__project__manifest__test__dependency_types.snap index 6a366ef24..88c125325 100644 --- a/src/project/snapshots/pixi__project__manifest__test__dependency_types.snap +++ b/src/project/snapshots/pixi__project__manifest__test__dependency_types.snap @@ -83,4 +83,5 @@ ProjectManifest { }, ), target: {}, + activation: None, } diff --git a/src/project/snapshots/pixi__project__manifest__test__target_specific.snap b/src/project/snapshots/pixi__project__manifest__test__target_specific.snap index 4771ff41d..17cd18a35 100644 --- a/src/project/snapshots/pixi__project__manifest__test__target_specific.snap +++ b/src/project/snapshots/pixi__project__manifest__test__target_specific.snap @@ -60,6 +60,7 @@ ProjectManifest { }, host_dependencies: None, build_dependencies: None, + activation: None, }, Spanned { span: 206..212, @@ -90,6 +91,8 @@ ProjectManifest { }, host_dependencies: None, build_dependencies: None, + activation: None, }, }, + activation: None, } diff --git a/src/project/snapshots/pixi__project__tests__activation_scripts-2.snap b/src/project/snapshots/pixi__project__tests__activation_scripts-2.snap new file mode 100644 index 000000000..6cae18912 --- /dev/null +++ b/src/project/snapshots/pixi__project__tests__activation_scripts-2.snap @@ -0,0 +1,7 @@ +--- +source: src/project/mod.rs +expression: "project.activation_scripts(Platform::Win64).unwrap()" +--- +[ + "Cargo.lock", +] diff --git a/src/project/snapshots/pixi__project__tests__activation_scripts-3.snap b/src/project/snapshots/pixi__project__tests__activation_scripts-3.snap new file mode 100644 index 000000000..c89eb90fe --- /dev/null +++ b/src/project/snapshots/pixi__project__tests__activation_scripts-3.snap @@ -0,0 +1,8 @@ +--- +source: src/project/mod.rs +expression: "project.activation_scripts(Platform::OsxArm64).unwrap()" +--- +[ + "pixi.toml", + "pixi.lock", +] diff --git a/src/project/snapshots/pixi__project__tests__activation_scripts.snap b/src/project/snapshots/pixi__project__tests__activation_scripts.snap new file mode 100644 index 000000000..662c5c370 --- /dev/null +++ b/src/project/snapshots/pixi__project__tests__activation_scripts.snap @@ -0,0 +1,7 @@ +--- +source: src/project/mod.rs +expression: "project.activation_scripts(Platform::Linux64).unwrap()" +--- +[ + "Cargo.toml", +]