From 265128858837530e6a888c13bc77ee25fb177cf5 Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Tue, 21 Jul 2026 03:09:43 +0200 Subject: [PATCH 1/2] Write Constructor installer metadata --- docs/how-to/customize-runtime.md | 6 +++ docs/reference/configuration.md | 6 +++ src/constructor_metadata.rs | 68 ++++++++++++++++++++++++++++++++ src/policy.rs | 4 ++ 4 files changed, 84 insertions(+) diff --git a/docs/how-to/customize-runtime.md b/docs/how-to/customize-runtime.md index 29223af..1713fed 100644 --- a/docs/how-to/customize-runtime.md +++ b/docs/how-to/customize-runtime.md @@ -67,6 +67,12 @@ runtime-name = "demo" installer = "homebrew" ``` +During automatic bootstrap, that stamp writes Constructor-compatible +`.installer.info` JSON into the managed prefix. Its `name`, `version`, and +`platform` fields identify the stamped distribution, while `type` contains the +configured `installer` value. This reports provenance. It does not prove that +the runtime binary may replace itself. + ## Choose Runtime Packages The selected source environment is the complete runtime package set. diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index c530397..4aa5219 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -176,6 +176,12 @@ For the naming model behind `runtime-name`, `artifact-name`, `install-name`, and Release workflows can override this with `cs build --installer INSTALLER` or the GitHub Action `installer` input. + When configured, automatic bootstrap writes Constructor-compatible + `/.installer.info` JSON with the exact fields `name`, `version`, + `platform`, and `type`. The configured `installer` value becomes `type`. + This metadata reports how the prefix was distributed. It is not proof that + the runtime binary may update or uninstall itself. + `condarc-file` : Optional path to a YAML condarc file. Relative paths are resolved from the selected project manifest. The builder requires a YAML mapping and stamps diff --git a/src/constructor_metadata.rs b/src/constructor_metadata.rs index 53667e2..23202c5 100644 --- a/src/constructor_metadata.rs +++ b/src/constructor_metadata.rs @@ -10,6 +10,15 @@ use rattler_conda_types::{Platform, RepoDataRecord}; use crate::{install, policy}; +#[derive(serde::Serialize)] +struct InstallerInfo<'a> { + name: &'a str, + version: &'a str, + platform: String, + #[serde(rename = "type")] + installer_type: &'a str, +} + pub(crate) fn write_prefix_metadata( prefix: &Path, lock_content: &str, @@ -63,6 +72,44 @@ fn write_prefix_metadata_from_records( })?; eprintln!(" Wrote {}", policy::path_for_display(&explicit_path)); + write_configured_installer_info(prefix, platform)?; + + Ok(()) +} + +fn write_configured_installer_info(prefix: &Path, platform: Platform) -> miette::Result<()> { + let Some(installer_type) = policy::installer() else { + return Ok(()); + }; + write_installer_info( + prefix, + policy::runtime_name(), + policy::runtime_version(), + platform, + installer_type, + ) +} + +fn write_installer_info( + prefix: &Path, + name: &str, + version: &str, + platform: Platform, + installer_type: &str, +) -> miette::Result<()> { + let path = prefix.join(".installer.info"); + let contents = serde_json::to_string(&InstallerInfo { + name, + version, + platform: platform.to_string(), + installer_type, + }) + .into_diagnostic() + .context("failed to render Constructor installer metadata")?; + std::fs::write(&path, contents) + .into_diagnostic() + .with_context(|| format!("failed to write {}", policy::path_for_display(&path)))?; + eprintln!(" Wrote {}", policy::path_for_display(&path)); Ok(()) } @@ -324,6 +371,27 @@ https://conda.anaconda.org/conda-forge/noarch/conda-spawn-1.0-0.conda#sha256:{SH )), "{explicit}" ); + assert!(!tmp.path().join(".installer.info").exists()); + } + + #[test] + fn test_write_installer_info_matches_constructor_json() { + let tmp = TempDir::new().unwrap(); + + write_installer_info( + tmp.path(), + "Demo Distribution", + "1.2.3", + Platform::Linux64, + "homebrew", + ) + .unwrap(); + + let contents = std::fs::read_to_string(tmp.path().join(".installer.info")).unwrap(); + assert_eq!( + contents, + r#"{"name":"Demo Distribution","version":"1.2.3","platform":"linux-64","type":"homebrew"}"# + ); } #[test] diff --git a/src/policy.rs b/src/policy.rs index c8b157a..ee8a3c0 100644 --- a/src/policy.rs +++ b/src/policy.rs @@ -28,6 +28,10 @@ pub(crate) fn delegate_executable() -> &'static str { &runtime_data::current().header.delegate_executable } +pub(crate) fn installer() -> Option<&'static str> { + runtime_data::current().header.installer.as_deref() +} + pub(crate) fn display_name() -> &'static str { runtime_name() } From 21bf4d0ed0fbf1ebc9dc961f3e0b998c1272f5f5 Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Tue, 21 Jul 2026 08:30:59 +0200 Subject: [PATCH 2/2] Clarify installer metadata scope --- docs/how-to/customize-runtime.md | 4 ++-- docs/reference/configuration.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/how-to/customize-runtime.md b/docs/how-to/customize-runtime.md index 1713fed..46e568f 100644 --- a/docs/how-to/customize-runtime.md +++ b/docs/how-to/customize-runtime.md @@ -70,8 +70,8 @@ installer = "homebrew" During automatic bootstrap, that stamp writes Constructor-compatible `.installer.info` JSON into the managed prefix. Its `name`, `version`, and `platform` fields identify the stamped distribution, while `type` contains the -configured `installer` value. This reports provenance. It does not prove that -the runtime binary may replace itself. +configured `installer` value. This metadata identifies the distribution and +installer. It is not a launcher ownership record. ## Choose Runtime Packages diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index 4aa5219..b751314 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -179,8 +179,8 @@ For the naming model behind `runtime-name`, `artifact-name`, `install-name`, and When configured, automatic bootstrap writes Constructor-compatible `/.installer.info` JSON with the exact fields `name`, `version`, `platform`, and `type`. The configured `installer` value becomes `type`. - This metadata reports how the prefix was distributed. It is not proof that - the runtime binary may update or uninstall itself. + This metadata records how the prefix was distributed. Update and uninstall + code must not use it as a launcher ownership record. `condarc-file` : Optional path to a YAML condarc file. Relative paths are resolved from