From bf6cf727fe4b717986c0156ec59b81192edea117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkan=20I=C5=9EIK?= Date: Fri, 25 Sep 2026 22:42:14 +0300 Subject: [PATCH] =?UTF-8?q?feat(P1):=20sihirbaza=203=20eksik=20ad=C4=B1m?= =?UTF-8?q?=C4=B1=20ba=C4=9Fla,=20StepRole=20ekle,=20=C3=A7ift=20crate=20r?= =?UTF-8?q?oot'u=20kald=C4=B1r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 1. Sihirbaza hiç bağlanmamış 3 adım - LicenseStep: steps/mod.rs'te HİÇ tanımlı değildi, yani dosya hiç derlenmiyordu. GlobalState'te license_accepted alanı da yoktu, bu yüzden tanımlansa bile derlenemezdi. İkisi de eklendi. Orijinal Python Yali'de bu ekran VAR (yali/gui/ScrLicense.py) ve sırada info → license → mediaCheck → keyboardSetup; Rust yeniden yazımında düşmüş. WelcomeStep'ten hemen sonra bağlandı. Demo modunda kabul zorunlu değil (--demo arayüz testi amacıyla tıklamadan ilerleyebilmeli). - DisplayManagerStep: steps/mod.rs:10'da yorum satırıydı. Bu yüzden state.display_manager kalıcı olarak "sddm" idi ve kullanıcı SEÇEMİYORDU; masaüstü ortamı da canlı sistemden gelen değere kilitliydi. Users → Bootloader arasına bağlandı. - NetinstallStep: yazılmış ama wizard vektöründe yoktu. on_enter çalışmadığı için selected_package_groups hep boş kalıyor ve InstallExtraPackagesJob GUI'den HİÇ çalışmıyordu. Bootloader → Summary arasına bağlandı; #[allow(dead_code)] kaldırıldı. Sihirbaz 10 → 13 adım. ## 2. Konumsal rol tespiti yerine StepRole (regresyon sınıfının kökü) main.rs rolü `cur == n_steps - 2` / `cur == n_steps - 1` ile tahmin ediyordu. Yeni bir adım eklemek bu tespiti sessizce kaydırıyor ve "Geri" butonu ile bitiş ekranını yanlış adıma bağlıyordu — üstelik test de yoktu. InstallerStep'a role() eklendi (StepRole: Normal / Execution / Finish, varsayılan Normal). ExecutionStep ve FinishStep kendilerini bildiriyor, main.rs tahmin etmiyor. main.rs'e 4 test: tek bir Execution/Finish olduğu, son ikide oldukları, Execution öncesi tüm adımların Normal olduğu ve — en önemlisi — Execution ÖNÜNE adım eklenince rol algısının bozulmadığı. ## 3. Çift crate root (src/lib.rs) lib.rs main.rs ile aynı 7 modülü tekrar tanımlıyordu. Sonuç: her modül iki kez tip denetiminden geçiyor, rust_i18n::i18n!("locales") iki kez çalışıp locales dizinini iki kez gömmeye çalışıyor ve her test iki hedefte koşuyordu. Kimse yali_rs kütüphanesini kullanmıyordu. README'deki gerekçe "yedek DLL yapısı" idi — Linux için anlamsız. Silindi; main.rs tek crate root. ## README Her iki dilde adım sayısı 10 → 13, akış listeleri güncellendi, lib.rs satırı çıkarıldı. cargo check --all-targets : 0 hata, 0 uyarı cargo test : 47/47 (tek hedefte; iki kez koşmuyor) Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 26 +++++----- README_en.md | 23 +++++---- src/installer.rs | 26 ++++++++++ src/lib.rs | 9 ---- src/main.rs | 106 ++++++++++++++++++++++++++++++++++++++-- src/steps/execution.rs | 4 ++ src/steps/finish.rs | 4 ++ src/steps/license.rs | 5 +- src/steps/mod.rs | 6 ++- src/steps/netinstall.rs | 1 - 10 files changed, 171 insertions(+), 39 deletions(-) delete mode 100644 src/lib.rs diff --git a/README.md b/README.md index b808cc6..e751ec2 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Rust ve egui ile yeniden yazılmış **Pisi GNU/Linux** kurulum aracı. YALI, ca ## Özellikler -- **10 adımlı kurulum sihirbazı**: Hoş Geldiniz → Konum → Klavye → Ağ → Disk Bölümleme → Kullanıcılar → Bootloader → Özet → Kurulum → Bitir +- **13 adımlı kurulum sihirbazı**: Hoş Geldiniz → Lisans → Konum → Klavye → Ağ → Disk Bölümleme → Kullanıcılar → Ekran Yöneticisi → Bootloader → Ek Paketler → Özet → Kurulum → Bitir - **İki mod**: Grafik arayüz (varsayılan) ve cevap dosyasıyla otomatik kurulum (`--auto-install answer.toml`) - **Demo modu**: Sistem kontrollerini atlar, kurulumu simüle eder (`--demo`) - **Disk bölümleme**: Otomatik (GPT/MBR) veya manuel, LVM ve LUKS şifreleme desteği @@ -44,8 +44,7 @@ cargo run --bin slides_test [desktop] [lang] ``` ├── src/ -│ ├── main.rs # Giriş noktası, eframe App, GUI düzeni -│ ├── lib.rs # Kütüphane kökü (yedek DLL yapısı için ayrıldı) +│ ├── main.rs # Giriş noktası, eframe App, GUI düzeni (tek crate root) │ ├── installer.rs # InstallerStep trait'i, GlobalState, WelcomeStep │ ├── funct.rs # Paylaşılan UI yardımcıları (form alanları, dil seçici) │ ├── branding.rs # Branding.toml yükleyici (logo, slayt, tema, OEM) @@ -95,15 +94,18 @@ cargo run --bin slides_test [desktop] [lang] ## Kurulum İş Akışı 1. **Hoş Geldiniz** — Dil seçimi, sistem kontrolleri, kurtarma modu -2. **Konum / Zaman** — Saat dilimi seçimi (bölge/alan çift ComboBox), GeoIP, NTP veya manuel tarih/saat -3. **Klavye** — Düzen/varyant seçimi, arama, canlı önizleme (setxkbmap) -4. **Ağ** — WiFi tarama, bağlantı, proxy -5. **Disk Bölümleme** — Otomatik (rehberli) veya manuel, LVM, LUKS -6. **Kullanıcılar** — Hesap oluşturma, parola gücü göstergesi, root parolası, otomatik oturum açma -7. **Bootloader** — GRUB aygıtı, zaman aşımı, parola, çekirdek parametreleri -8. **Özet** — Tüm seçimlerin gözden geçirilmesi -9. **Kurulum** — Slayt + ilerleme çubuğu + canlı log -10. **Bitir** — Başarılı kurulum, yeniden başlat veya canlı masaüstüne dön +2. **Lisans** — GPL metni ve kabul onayı +3. **Konum / Zaman** — Saat dilimi seçimi (bölge/alan çift ComboBox), GeoIP, NTP veya manuel tarih/saat +4. **Klavye** — Düzen/varyant seçimi, arama, canlı önizleme (setxkbmap) +5. **Ağ** — WiFi tarama, bağlantı, proxy +6. **Disk Bölümleme** — Otomatik (rehberli) veya manuel, LVM, LUKS +7. **Kullanıcılar** — Hesap oluşturma, parola gücü göstergesi, root parolası, otomatik oturum açma +8. **Ekran Yöneticisi** — DM ve masaüstü ortamı seçimi +9. **Bootloader** — GRUB aygıtı, zaman aşımı, parola, çekirdek parametreleri +10. **Ek Paketler** — Paket grubu seçimi (ofis, multimedya, geliştirme, …) +11. **Özet** — Tüm seçimlerin gözden geçirilmesi +12. **Kurulum** — Slayt + ilerleme çubuğu + canlı log +13. **Bitir** — Başarılı kurulum, yeniden başlat veya canlı masaüstüne dön ## Bağımlılıklar diff --git a/README_en.md b/README_en.md index 0a28a36..18c5d38 100644 --- a/README_en.md +++ b/README_en.md @@ -4,7 +4,7 @@ A rewritten **Pisi GNU/Linux** installation tool in Rust and egui. YALI installs ## Features -- **10-step installation wizard**: Welcome → Location → Keyboard → Network → Disk Partitioning → Users → Bootloader → Summary → Installation → Finish +- **13-step installation wizard**: Welcome → License → Location → Keyboard → Network → Disk Partitioning → Users → Display Manager → Bootloader → Extra Packages → Summary → Installation → Finish - **Two modes**: Graphical interface (default) and automatic installation with answer file (`--auto-install answer.toml`) - **Demo mode**: Skips system checks, simulates installation (`--demo`) - **Disk partitioning**: Automatic (GPT/MBR) or manual, LVM and LUKS encryption support @@ -92,15 +92,18 @@ cargo run --bin slides_test [desktop] [lang] ## Installation Workflow 1. **Welcome** — Language selection, system checks, rescue mode -2. **Location / Time** — Timezone selection (region/area dual ComboBox), GeoIP, NTP or manual date/time -3. **Keyboard** — Layout/variant selection, search, live preview (setxkbmap) -4. **Network** — WiFi scanning, connection, proxy -5. **Disk Partitioning** — Automatic (guided) or manual, LVM, LUKS -6. **Users** — Account creation, password strength indicator, root password, autologin -7. **Bootloader** — GRUB device, timeout, password, kernel parameters -8. **Summary** — Review all selections -9. **Installation** — Slideshow + progress bar + live log -10. **Finish** — Successful installation, reboot or return to live desktop +2. **License** — GPL text and acceptance +3. **Location / Time** — Timezone selection (region/area dual ComboBox), GeoIP, NTP or manual date/time +4. **Keyboard** — Layout/variant selection, search, live preview (setxkbmap) +5. **Network** — WiFi scanning, connection, proxy +6. **Disk Partitioning** — Automatic (guided) or manual, LVM, LUKS +7. **Users** — Account creation, password strength indicator, root password, autologin +8. **Display Manager** — DM and desktop environment selection +9. **Bootloader** — GRUB device, timeout, password, kernel parameters +10. **Extra Packages** — Package group selection (office, multimedia, development, …) +11. **Summary** — Review all selections +12. **Installation** — Slideshow + progress bar + live log +13. **Finish** — Successful installation, reboot or return to live desktop ## Dependencies diff --git a/src/installer.rs b/src/installer.rs index 63de09e..5747b09 100644 --- a/src/installer.rs +++ b/src/installer.rs @@ -59,12 +59,34 @@ pub fn part_path(disk: &str, num: u32) -> String { } +/// Bir adımın sihirbaz içindeki rolü. +/// +/// Rol, adımın LİSTEDEKİ KONUMUNDAN değil kendisinden bildirilir. Daha +/// önce `main.rs` rolü `cur == n_steps - 2` gibi konumsal olarak +/// tahmin ediyordu; sihirbaza yeni bir adım eklemek (ör. LicenseStep) +/// bu tespiti sessizce kaydırıp "Geri" butonunu veya bitiş ekranını +/// yanlış adıma bağlıyordu. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum StepRole { + /// Sıradan bir yapılandırma adımı. + Normal, + /// Kurulumun çalıştığı ekran. "İleri" gizlenir. + Execution, + /// Kurulum sonrası ekran. Yalnızca yeniden başlatma/çıkış sunar. + Finish, +} + /// Calamares'in "ViewStep" yapısına benzer bir Trait. pub trait InstallerStep { fn name(&self) -> String; fn on_enter(&mut self, _state: &mut GlobalState) {} fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState); fn is_complete(&self, state: &GlobalState) -> bool; + + /// Adımın rolü. Varsayılan `Normal`. + fn role(&self) -> StepRole { + StepRole::Normal + } } // ───────────────────────────────────────────── @@ -294,6 +316,9 @@ pub struct GlobalState { pub demo_mode: bool, pub is_dark: bool, pub rescue_mode: bool, + /// GPL lisans metnini kullanıcı okudu ve kabul etti. + /// Demo modunda zorunlu değildir. + pub license_accepted: bool, pub bootloader_device: String, pub bootloader_timeout: u32, pub bootloader_password: String, @@ -357,6 +382,7 @@ impl Default for GlobalState { demo_mode: false, is_dark: true, rescue_mode: false, + license_accepted: false, bootloader_device: String::new(), bootloader_timeout: 5, bootloader_password: String::new(), diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 3d83c3c..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod installer; -pub mod steps; -pub mod jobs; -pub mod ui; -pub mod autoinstall; -pub mod branding; -pub mod funct; - -rust_i18n::i18n!("locales"); \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e4abbd0..d8b7f5e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ mod branding; mod funct; use installer::{GlobalState, InstallerStep, WelcomeStep}; -use steps::{LocationStep, KeyboardStep, UsersStep, SummaryStep, ExecutionStep, FinishStep, PartitionStep, BootloaderStep, NetworkStep}; +use steps::{LocationStep, KeyboardStep, UsersStep, SummaryStep, ExecutionStep, FinishStep, PartitionStep, BootloaderStep, NetworkStep, DisplayManagerStep, NetinstallStep, LicenseStep}; use eframe::egui; use rust_i18n::t; @@ -247,12 +247,18 @@ impl Default for YaliApp { state: GlobalState::default(), steps: vec![ Box::new(WelcomeStep::default()), + // Orijinal Python Yali'de sıra: info → license → mediaCheck + // → keyboardSetup. Rust yeniden yazımında LicenseStep yazıldı + // ama hiç bağlanmadı (mod.rs'te tanımsızdı). + Box::new(LicenseStep), Box::new(LocationStep::default()), Box::new(KeyboardStep::default()), Box::new(NetworkStep::default()), Box::new(PartitionStep::default()), Box::new(UsersStep::default()), + Box::new(DisplayManagerStep), Box::new(BootloaderStep::default()), + Box::new(NetinstallStep), Box::new(SummaryStep), Box::new(ExecutionStep::default()), Box::new(FinishStep), @@ -328,9 +334,13 @@ impl eframe::App for YaliApp { } let cur = self.state.current_step; - let n_steps = self.steps.len(); - let is_exec = cur == n_steps - 2; - let is_finish = cur == n_steps - 1; + // Rol artık konumsal tahminle değil, adımın kendi bildirdiği + // `role()` ile belirleniyor. Sihirbaza yeni adım eklemek rol + // algısını bozmaz (bkz. StepRole). + let role = self.steps.get(cur).map(|s| s.role()) + .unwrap_or(installer::StepRole::Normal); + let is_exec = role == installer::StepRole::Execution; + let is_finish = role == installer::StepRole::Finish; // Adım değişmişse on_enter çağır if cur != self.last_step { @@ -518,4 +528,90 @@ impl eframe::App for YaliApp { }); }); } -} \ No newline at end of file +} + +#[cfg(test)] +mod tests { + use super::*; + use installer::StepRole; + + /// Sihirbazın adım dizisi. main.rs::YaliApp::default() ile AYNI + /// olmalı — test bu yüzden diziyi yeniden kuruyor. + fn wizard() -> Vec> { + vec![ + Box::new(WelcomeStep::default()), + Box::new(LicenseStep), + Box::new(LocationStep::default()), + Box::new(KeyboardStep::default()), + Box::new(NetworkStep::default()), + Box::new(PartitionStep::default()), + Box::new(UsersStep::default()), + Box::new(DisplayManagerStep), + Box::new(BootloaderStep::default()), + Box::new(NetinstallStep), + Box::new(SummaryStep), + Box::new(ExecutionStep::default()), + Box::new(FinishStep), + ] + } + + #[test] + fn wizard_has_one_execution_and_one_finish_step() { + let steps = wizard(); + assert_eq!( + steps.iter().filter(|s| s.role() == StepRole::Execution).count(), + 1, + "tam olarak bir ExecutionStep olmalı" + ); + assert_eq!( + steps.iter().filter(|s| s.role() == StepRole::Finish).count(), + 1, + "tam olarak bir FinishStep olmalı" + ); + } + + /// Konumsal tespit yerine role() kullanıldığı için, Execution ve + /// Finish listenin SONU olmak zorunda değil. Bu test yine de + /// beklenen sırayı sabitler; bir adım yanlış yere eklendiğinde + /// (ör. Finish'ten sonra yeni bir adım) kırılır. + #[test] + fn execution_and_finish_are_the_last_two_steps() { + let steps = wizard(); + let n = steps.len(); + assert_eq!(steps[n - 2].role(), StepRole::Execution); + assert_eq!(steps[n - 1].role(), StepRole::Finish); + } + + #[test] + fn all_ordinary_steps_precede_the_execution_step() { + let steps = wizard(); + let first_exec = steps.iter().position(|s| s.role() == StepRole::Execution) + .expect("ExecutionStep bulunmalı"); + assert!( + steps[..first_exec].iter().all(|s| s.role() == StepRole::Normal), + "ExecutionStep'tan önceki tüm adımlar Normal olmalı" + ); + } + + /// Yeni bir adım eklemek rol algısını bozmamalı. Bu, 22de7d5 + /// sonrası yaşanan regresyon sınıfının tersidir: orada liste + /// değişti ve konumsal `cur == n_steps - 2` tespiti sessizce + /// kaydı. + #[test] + fn roles_survive_inserting_a_step_before_execution() { + let mut steps = wizard(); + let exec_at = steps.iter().position(|s| s.role() == StepRole::Execution).unwrap(); + + // Yeni bir Normal adım ekle + steps.insert(exec_at, Box::new(NetinstallStep)); + let n = steps.len(); + + // Hâlâ doğru adımlar son ikide mi? + assert_eq!(steps[n - 2].role(), StepRole::Execution); + assert_eq!(steps[n - 1].role(), StepRole::Finish); + // Role() sayesinde Execution'ın KONUMU değil KENDİSİ bulunuyor: + // konum bir kayar, ama Execution yine n-2'de. + let exec_at2 = steps.iter().position(|s| s.role() == StepRole::Execution).unwrap(); + assert_eq!(exec_at2, exec_at + 1, "eklenen adım Execution'ı bir kaydırmalı"); + } +} diff --git a/src/steps/execution.rs b/src/steps/execution.rs index 5d083da..fbbfa1d 100644 --- a/src/steps/execution.rs +++ b/src/steps/execution.rs @@ -368,6 +368,10 @@ impl InstallerStep for ExecutionStep { } } + fn role(&self) -> crate::installer::StepRole { + crate::installer::StepRole::Execution + } + fn is_complete(&self, _state: &GlobalState) -> bool { self.finished && self.error_screen.is_none() } diff --git a/src/steps/finish.rs b/src/steps/finish.rs index 93eb771..a0b09b6 100644 --- a/src/steps/finish.rs +++ b/src/steps/finish.rs @@ -53,5 +53,9 @@ impl InstallerStep for FinishStep { }); } + fn role(&self) -> crate::installer::StepRole { + crate::installer::StepRole::Finish + } + fn is_complete(&self, _state: &GlobalState) -> bool { true } } diff --git a/src/steps/license.rs b/src/steps/license.rs index 387dcdc..e237172 100644 --- a/src/steps/license.rs +++ b/src/steps/license.rs @@ -47,6 +47,9 @@ impl InstallerStep for LicenseStep { } fn is_complete(&self, state: &GlobalState) -> bool { - state.license_accepted + // Demo modu gerçek kurulum yapmadığı için kabul zorunlu değil; + // demo gezintisinin tıklanmadan ilerleyebilmesi gerekir + // (README'deki "--demo ... arayüz testi" amacı). + state.license_accepted || state.demo_mode } } diff --git a/src/steps/mod.rs b/src/steps/mod.rs index ace9757..d86f98f 100644 --- a/src/steps/mod.rs +++ b/src/steps/mod.rs @@ -7,8 +7,9 @@ pub mod partition; pub mod bootloader; pub mod network; pub mod rescue; -//pub mod display_manager; +pub mod display_manager; pub mod netinstall; +pub mod license; pub mod location; pub use keyboard::KeyboardStep; @@ -20,4 +21,7 @@ pub use partition::PartitionStep; pub use bootloader::BootloaderStep; pub use network::NetworkStep; pub use rescue::RescueStep; +pub use display_manager::DisplayManagerStep; +pub use netinstall::NetinstallStep; +pub use license::LicenseStep; pub use location::LocationStep; diff --git a/src/steps/netinstall.rs b/src/steps/netinstall.rs index 68c6018..625ee63 100644 --- a/src/steps/netinstall.rs +++ b/src/steps/netinstall.rs @@ -14,7 +14,6 @@ pub const PACKAGE_GROUPS: &[(&str, &str, &[&str])] = &[ ]; #[derive(Default)] -#[allow(dead_code)] pub struct NetinstallStep; impl InstallerStep for NetinstallStep {