feat: add repo planning/binary repo support and Meson build integration

- add repos.toml source/binary repo configuration with arch overrides and priorities
- add dependency planner/execution plan flow with interactive provider selection and --yes support
- expand binary repo DB metadata (sha512, dependencies, file lists) and repo owns/search helpers
- add rootfs-scoped fd locking and minisign detached signing/verification helpers
- add Meson build/test support for the project with builddir-local CARGO_HOME/CARGO_TARGET_DIR
- install contrib example configs into sysconfdir (/etc/depot.d layout) via Meson
- improve builder env handling (sanitized PATH, tool env passthrough, CXXFLAGS support)
- support += appends in package specs and add build.flags.cxxflags parsing
- prompt for git credentials when private repo auth is required
- update docs/examples and dependency setup (system libs, zstdmt, tempfile pin)
This commit is contained in:
2026-02-24 21:38:12 -06:00
parent d63ad03e98
commit 9953d4b2ee
30 changed files with 4536 additions and 111 deletions
+66
View File
@@ -2,6 +2,9 @@
use anyhow::{Context, Result};
use std::io::{self, IsTerminal, Write};
use std::sync::atomic::{AtomicBool, Ordering};
static ASSUME_YES: AtomicBool = AtomicBool::new(false);
#[derive(Clone, Copy)]
enum Stream {
@@ -88,7 +91,17 @@ fn parse_yes_no_input(input: &str, default_yes: bool) -> Option<bool> {
}
}
/// Configure global prompt behavior for the current process.
pub fn set_assume_yes(assume_yes: bool) {
ASSUME_YES.store(assume_yes, Ordering::Relaxed);
}
pub fn prompt_yes_no(prompt: &str, default_yes: bool) -> Result<bool> {
if ASSUME_YES.load(Ordering::Relaxed) {
info(format!("{} [auto-yes]", prompt));
return Ok(true);
}
let default_hint = if default_yes { "Y/n" } else { "y/N" };
loop {
print!(
@@ -114,6 +127,59 @@ pub fn prompt_yes_no(prompt: &str, default_yes: bool) -> Result<bool> {
}
}
/// Prompt the user to choose one option by index.
pub fn prompt_select_index(prompt: &str, options: &[String], default_idx: usize) -> Result<usize> {
if options.is_empty() {
anyhow::bail!("No options available for selection");
}
let default_idx = default_idx.min(options.len() - 1);
if ASSUME_YES.load(Ordering::Relaxed) {
info(format!(
"{} [auto-select {}: {}]",
prompt,
default_idx + 1,
options[default_idx]
));
return Ok(default_idx);
}
loop {
println!("{} {}", label(Stream::Stdout, "?", "35"), prompt);
for (idx, option) in options.iter().enumerate() {
println!(
" {} {}{}",
idx + 1,
option,
if idx == default_idx { " [default]" } else { "" }
);
}
print!(
"{} Select an option [1-{}] (default {}): ",
label(Stream::Stdout, "?", "35"),
options.len(),
default_idx + 1
);
io::stdout()
.flush()
.context("Failed to flush prompt to stdout")?;
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.context("Failed to read user input from stdin")?;
let trimmed = input.trim();
if trimmed.is_empty() {
return Ok(default_idx);
}
if let Ok(num) = trimmed.parse::<usize>()
&& (1..=options.len()).contains(&num)
{
return Ok(num - 1);
}
warn("Please enter a valid option number.");
}
}
#[cfg(test)]
mod tests {
use super::parse_yes_no_input;