This commit is contained in:
Lucy 2025-09-30 19:09:14 +02:00
parent 30b6245514
commit 002cca571c
2 changed files with 105 additions and 82 deletions

View file

@ -1,13 +0,0 @@
name: "Build legacy Nix package on Ubuntu"
on:
push:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: cachix/install-nix-action@v26
- name: Building package
run: nix build

View file

@ -13,79 +13,74 @@ use ratatui::{
widgets::{Block, Borders, List, ListItem, ListState}, widgets::{Block, Borders, List, ListItem, ListState},
}; };
#[cfg(feature = "tui")] #[cfg(feature = "tui")]
use spinners::{Spinner, Spinners}; use std::collections::HashMap;
#[cfg(feature = "tui")] #[cfg(feature = "tui")]
use std::io::{self, stdout}; use std::io::{self, stdout};
#[cfg(feature = "tui")] #[cfg(feature = "tui")]
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(feature = "tui")] #[cfg(feature = "tui")]
use std::process::Command; use std::time::{Duration, Instant};
#[cfg(feature = "tui")] #[cfg(feature = "tui")]
use crate::{downloader, mirrors, wget_list}; use crate::{downloader, mirrors, wget_list};
#[cfg(feature = "tui")] #[cfg(feature = "tui")]
fn init_environment() -> PathBuf { fn init_environment() -> PathBuf {
let tmp_path = "/tmp/lfs_tmp"; // Simplified for demo let tmp_path = format!("/tmp/lfs_{}", rand::random::<u32>() % 9000 + 1000);
println!(" Using temporary path {}", tmp_path);
PathBuf::from(tmp_path).join("sources") PathBuf::from(tmp_path).join("sources")
} }
#[cfg(feature = "tui")] #[cfg(feature = "tui")]
fn download_packages(lfs_sources: &PathBuf) { fn select_mirrors_tui(mirrors: Vec<String>) -> Vec<String> {
let spinner = Spinner::new(Spinners::Dots9, "Downloading packages...".into()); if mirrors.is_empty() {
let wget_list = wget_list::get_wget_list().unwrap_or_default(); return vec![];
let package_mirror = }
mirrors::choose_package_mirror().unwrap_or_else(|| "ftp.fau.de".to_string());
// Simplified download call let mut selected: Vec<bool> = vec![false; mirrors.len()];
let _ = downloader::download_files(&wget_list, lfs_sources, Some(package_mirror), None);
spinner.stop();
}
#[cfg(feature = "tui")]
fn format_drive_tui() -> Result<(), Box<dyn std::error::Error>> {
// Mocked drive list for demo
let drives = vec!["/dev/sda".to_string(), "/dev/sdb".to_string()];
let mut state = ListState::default(); let mut state = ListState::default();
state.select(Some(0)); state.select(Some(0));
enable_raw_mode()?;
let mut stdout = stdout(); let mut stdout = stdout();
execute!(stdout, EnterAlternateScreen)?; execute!(stdout, EnterAlternateScreen).unwrap();
enable_raw_mode().unwrap();
let backend = CrosstermBackend::new(stdout); let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?; let mut terminal = Terminal::new(backend).unwrap();
loop { loop {
terminal.draw(|f| { terminal
.draw(|f| {
let size = f.size(); let size = f.size();
let block = Block::default() let block = Block::default()
.title("💾 Format Drive") .title("Select mirrors (space to toggle, Enter to confirm)")
.borders(Borders::ALL); .borders(Borders::ALL);
f.render_widget(block, size); f.render_widget(block, size);
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.margin(2) .margin(2)
.constraints(vec![Constraint::Length(3); drives.len()]) .constraints(vec![Constraint::Length(3); mirrors.len()])
.split(size); .split(size);
for (i, drive) in drives.iter().enumerate() { for (i, mirror) in mirrors.iter().enumerate() {
let mut style = Style::default(); let mut style = Style::default();
if Some(i) == state.selected() { if Some(i) == state.selected() {
style = style.bg(Color::Red).fg(Color::White); style = style.bg(Color::Red).fg(Color::White);
} }
let list_item = ListItem::new(drive.clone()).style(style); let prefix = if selected[i] { "[x] " } else { "[ ] " };
let list = List::new(vec![list_item]).block(Block::default().borders(Borders::ALL)); let list_item = ListItem::new(format!("{}{}", prefix, mirror)).style(style);
let list =
List::new(vec![list_item]).block(Block::default().borders(Borders::ALL));
f.render_widget(list, chunks[i]); f.render_widget(list, chunks[i]);
} }
})?; })
.unwrap();
if let Event::Key(key) = event::read()? { if let Event::Key(key) = event::read().unwrap() {
match key.code { match key.code {
KeyCode::Down => { KeyCode::Down => {
let i = state.selected().unwrap_or(0); let i = state.selected().unwrap_or(0);
if i < drives.len() - 1 { if i < mirrors.len() - 1 {
state.select(Some(i + 1)); state.select(Some(i + 1));
} }
} }
@ -95,33 +90,77 @@ fn format_drive_tui() -> Result<(), Box<dyn std::error::Error>> {
state.select(Some(i - 1)); state.select(Some(i - 1));
} }
} }
KeyCode::Char(' ') => {
let i = state.selected().unwrap_or(0);
selected[i] = !selected[i];
}
KeyCode::Enter => { KeyCode::Enter => {
if let Some(idx) = state.selected() { disable_raw_mode().unwrap();
let drive = &drives[idx]; execute!(terminal.backend_mut(), LeaveAlternateScreen).unwrap();
disable_raw_mode()?; return mirrors
execute!(terminal.backend_mut(), LeaveAlternateScreen)?; .into_iter()
println!("⚠️ Confirm formatting {}? (y/n)", drive); .enumerate()
let mut input = String::new(); .filter_map(|(i, m)| if selected[i] { Some(m) } else { None })
io::stdin().read_line(&mut input)?; .collect();
if matches!(input.trim().to_lowercase().as_str(), "y" | "yes") {
println!("Formatting {}...", drive);
let _ = Command::new("mkfs.ext4").arg(drive).status();
println!("✅ Done!");
} }
enable_raw_mode()?; KeyCode::Esc => {
execute!(terminal.backend_mut(), EnterAlternateScreen)?; disable_raw_mode().unwrap();
execute!(terminal.backend_mut(), LeaveAlternateScreen).unwrap();
return vec![];
} }
}
KeyCode::Esc => break,
_ => {} _ => {}
} }
} }
} }
}
disable_raw_mode()?; #[cfg(feature = "tui")]
execute!(terminal.backend_mut(), LeaveAlternateScreen)?; fn download_packages(lfs_sources: &PathBuf) {
terminal.show_cursor()?; let mirrors_list = mirrors::fetch_mirrors().unwrap_or_else(|_| vec![]);
Ok(()) let selected_mirrors = select_mirrors_tui(mirrors_list);
if selected_mirrors.is_empty() {
println!("⚠️ No mirrors selected!");
return;
}
let wget_list = wget_list::get_wget_list().unwrap_or_default();
if wget_list.is_empty() {
println!("⚠️ No packages to download!");
return;
}
let mut md5_map = HashMap::new();
if let Ok(md5_content) = crate::md5_utils::get_md5sums() {
for line in md5_content.lines() {
if let Some((hash, filename)) = line.split_once(' ') {
md5_map.insert(filename.to_string(), hash.to_string());
}
}
}
for file in wget_list {
let mut downloaded = false;
for mirror in &selected_mirrors {
print!("⬇️ Downloading {} from {} ... ", file, mirror);
io::stdout().flush().unwrap();
let start = Instant::now();
if downloader::download_files(&file, lfs_sources, Some(mirror.clone()), Some(&md5_map))
.is_ok()
{
println!("✅ done in {:?}", start.elapsed());
downloaded = true;
break;
} else {
println!("⚠️ failed, trying next mirror...");
}
}
if !downloaded {
println!("❌ Failed to download {}", file);
}
}
println!("🎉 All downloads finished!");
} }
#[cfg(feature = "tui")] #[cfg(feature = "tui")]
@ -129,14 +168,13 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
let mut stdout = stdout(); let mut stdout = stdout();
execute!(stdout, EnterAlternateScreen)?; execute!(stdout, EnterAlternateScreen)?;
enable_raw_mode()?; enable_raw_mode()?;
let backend = CrosstermBackend::new(stdout); let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?; let mut terminal = Terminal::new(backend)?;
let menu_items = vec![ let menu_items = vec![
"🌱 Init environment", "🌱 Init environment",
"📦 Download packages", "📦 Download packages",
"💾 Format drive", "🔍 Check status",
"❌ Exit", "❌ Exit",
]; ];
let mut state = ListState::default(); let mut state = ListState::default();
@ -192,9 +230,7 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
println!("⚠️ Please initialize environment first!"); println!("⚠️ Please initialize environment first!");
} }
} }
Some(2) => { Some(2) => println!("🔍 Status selected! (TODO)"),
format_drive_tui()?;
}
Some(3) | _ => break, Some(3) | _ => break,
}, },
KeyCode::Esc => break, KeyCode::Esc => break,