fix: allow more than one venv per deployment
This commit is contained in:
+19
-9
@@ -56,11 +56,13 @@ pub struct Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Discover launchable deployments on disk under `deployments_base`, one
|
||||
/// entry per (deployment, virtualenv) pair, labelled `"<deployment>/<venv>"`.
|
||||
/// Discover launchable deployments on disk under `deployments_base`.
|
||||
///
|
||||
/// These are the "custom" deployments that are present on the machine but
|
||||
/// not named in the config. Entry points use the configured defaults.
|
||||
/// not named in the config; entry points use the configured defaults. Each
|
||||
/// is labelled by its deployment name, with the virtualenv kept inside the
|
||||
/// entry — only when a deployment has more than one virtualenv is the venv
|
||||
/// name appended (`"<deployment>/<venv>"`) to keep the labels unique.
|
||||
pub fn discovered_entries(&self) -> Vec<(String, LauncherEntry)> {
|
||||
let mut found = Vec::new();
|
||||
let Ok(dirs) = fs::read_dir(&self.deployments_base) else {
|
||||
@@ -74,13 +76,21 @@ impl Config {
|
||||
let dir = deployment.path();
|
||||
let name = deployment.file_name().to_string_lossy().into_owned();
|
||||
|
||||
for venv in detect_venvs(&dir).unwrap_or_default() {
|
||||
let venv_name = venv
|
||||
.file_name()
|
||||
.map(|n| n.to_string_lossy().into_owned())
|
||||
.unwrap_or_default();
|
||||
let venvs = detect_venvs(&dir).unwrap_or_default();
|
||||
let disambiguate = venvs.len() > 1;
|
||||
|
||||
for venv in venvs {
|
||||
let label = if disambiguate {
|
||||
let venv_name = venv
|
||||
.file_name()
|
||||
.map(|n| n.to_string_lossy().into_owned())
|
||||
.unwrap_or_default();
|
||||
format!("{name}/{venv_name}")
|
||||
} else {
|
||||
name.clone()
|
||||
};
|
||||
found.push((
|
||||
format!("{name}/{venv_name}"),
|
||||
label,
|
||||
LauncherEntry {
|
||||
python: venv.join("bin/python"),
|
||||
gui_file: join_relative(&dir, &self.default_gui_file),
|
||||
|
||||
@@ -83,7 +83,6 @@ fn main() -> Result<()> {
|
||||
let mut config = load_config(&json)?;
|
||||
|
||||
check_base_exists("deployments_base", &config.deployments_base, &config_path)?;
|
||||
check_base_exists("venvs_base", &config.deployments_base, &config_path)?;
|
||||
|
||||
// Populate the config with deployments found on disk so they are available
|
||||
// to both `lookup_entry` and the TUI. Configured entries take precedence.
|
||||
|
||||
+18
-16
@@ -27,7 +27,10 @@ pub struct EntriesTab {
|
||||
}
|
||||
|
||||
impl EntriesTab {
|
||||
pub fn new(entries: Vec<(String, LauncherEntry)>) -> Self {
|
||||
pub fn new(mut entries: Vec<(String, LauncherEntry)>) -> Self {
|
||||
// Production deployments first, then the rest, alphabetical within each.
|
||||
entries.sort_by(|a, b| sort_key(&a.0).cmp(&sort_key(&b.0)));
|
||||
|
||||
let mut list_state = ListState::default();
|
||||
if !entries.is_empty() {
|
||||
list_state.select(Some(0));
|
||||
@@ -117,7 +120,7 @@ impl EntriesTab {
|
||||
let items: Vec<ListItem> = self
|
||||
.entries
|
||||
.iter()
|
||||
.map(|(label, _)| ListItem::new(display_label(label)))
|
||||
.map(|(label, _)| ListItem::new(label.as_str()))
|
||||
.collect();
|
||||
let list = List::new(items)
|
||||
.block(Block::default().borders(Borders::ALL).title("Entries"))
|
||||
@@ -168,10 +171,12 @@ impl EntriesTab {
|
||||
}
|
||||
}
|
||||
|
||||
/// The label shown in the list: the default `/.venv` virtualenv suffix (used by
|
||||
/// discovered deployments) is dropped for readability.
|
||||
fn display_label(label: &str) -> &str {
|
||||
label.strip_suffix("/.venv").unwrap_or(label)
|
||||
/// Production deployment labels, pinned to the top of the list.
|
||||
const PRODUCTION: [&str; 3] = ["x06da", "x06sa", "x10sa"];
|
||||
|
||||
/// Sort key placing production deployments first, then alphabetical by label.
|
||||
fn sort_key(label: &str) -> (bool, &str) {
|
||||
(!PRODUCTION.contains(&label), label)
|
||||
}
|
||||
|
||||
/// A rectangle centered within `area`, sized as a percentage of it.
|
||||
@@ -187,20 +192,17 @@ fn centered_rect(width_pct: u16, height_pct: u16, area: Rect) -> Rect {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::display_label;
|
||||
use super::sort_key;
|
||||
|
||||
#[test]
|
||||
fn strips_default_venv_suffix() {
|
||||
assert_eq!(display_label("mydeploy/.venv"), "mydeploy");
|
||||
fn production_sorts_before_others() {
|
||||
// Production wins even though its label sorts later alphabetically.
|
||||
assert!(sort_key("x10sa") < sort_key("aaa"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_named_venv() {
|
||||
assert_eq!(display_label("mydeploy/pxii"), "mydeploy/pxii");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leaves_plain_labels_untouched() {
|
||||
assert_eq!(display_label("x10sa"), "x10sa");
|
||||
fn production_ordered_alphabetically() {
|
||||
assert!(sort_key("x06da") < sort_key("x06sa"));
|
||||
assert!(sort_key("x06sa") < sort_key("x10sa"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user