143 Commits

Author SHA1 Message Date
nocode 53ceeabcfb Merge branch 'Main' into Multi-Desktop 2026-07-07 18:48:21 +00:00
nocode 335b59ceca Merge branch 'Multi-Desktop' into Main 2026-07-07 20:46:57 +02:00
nocode ac810f2ee3 docs(trademarks): correct fws version number from 1.0 to 0.6 2026-07-07 13:57:22 +02:00
nocode 5592c57642 docs(readme): update fws version from 1.0 to 0.6 in installation header 2026-07-07 13:57:17 +02:00
nocode 8f6b8e7a95 docs(licensing): update fws version from 1.0 to 0.6 in installation header example 2026-07-07 13:57:12 +02:00
nocode 1826dbfead docs(changelog): update fws version from 1.0 to 0.6 2026-07-07 13:57:08 +02:00
nocode b7bf497cd6 feat(releng): enable systemd-timesyncd ntp synchronization
Add explicit activation of systemd-timesyncd service to ensure proper clock synchronization on installed systems. This prevents clock drift issues that can affect TLS and pacman operations, and meets user expectations for accurate system time.

The service is already present in the image but inherited symlinks from the live environment may be ordinary files that systemd ignores, so explicit enablement ensures correct behavior on all installations.
2026-07-07 13:57:04 +02:00
nocode ae70926ed1 chore(release): bump version to 0.6.0 2026-07-07 13:56:58 +02:00
nocode aac3c8b59e chore(.xinitrc): update anaconda default product version to 0.6 2026-07-07 13:56:54 +02:00
nocode 260547f6b6 chore(releng): convert systemd service files to symlinks
Convert three systemd service configuration files from regular files to symlinks pointing to their respective system libraries. This ensures that the services reference the actual systemd unit files from /usr/lib/systemd/system rather than maintaining duplicate copies.
2026-07-07 13:56:43 +02:00
nocode 730cfd697c chore: bump version to 0.6.0 2026-07-07 02:53:18 +02:00
nocode 292e3071c6 docs(changelog): release version 0.6.0
Update changelog with version 0.6.0 release notes dated 2026-07-07.

Documents major additions including:
- Anaconda installer 45 ported to Arch with Live OS payload
- FWS tooling suite (fws-hello, fws-update, fws-keys, etc.)
- System recovery functionality (fws-recovery-gui and engine)
- zram swap support and firewalld enablement
- Hyprland and i3 desktop parity

Includes changelog entries for changed items (FWS branding, boot entries, compression), fixed issues (splash watchdog, low RAM handling, profile synchronization), and removed items (syslinux config).
2026-07-07 02:53:14 +02:00
nocode 8a90ef4794 docs(environnement): add native arch linux build reference section
Document the native Arch Linux build environment that has been the reference since July 2026. This section explains that build.sh and setup-aur.sh now detect and execute directly on Arch Linux, replacing the previous Windows 11 + WSL setup as the primary reference.

The Windows + WSL path remains functional with automatic fallback from Git Bash but is no longer the reference environment.
2026-07-07 02:48:34 +02:00
nocode 9c7f9b46c5 docs(trademarks): document branding removals and anaconda customization
Add detailed documentation of trademark and branding changes:

- BIOS boot menu: replaced archiso syslinux splash with FWS original artwork
- Login banner: uses FWS PRETTY_NAME without kernel version suffix
- User-facing text: replaced references to Arch Linux/Fedora with generic descriptions
- Anaconda installer: configured to display "FWS 1.0" via environment variables
- Splash screen filtering: removes Fedora mentions from visible logs
- Technical logs: intentionally preserve upstream component identification for diagnostics

All changes maintain nominative fair use and preserve functional upstream tool identifiers.
2026-07-07 02:48:29 +02:00
nocode bf8b29c975 docs(readme): update fws architecture and recovery system documentation
- Add FWS branding details for Anaconda installer (ANACONDA_PRODUCT* exports via .xinitrc)
- Enhance splash screen description with real-time anaconda activity monitoring and auto-diagnostic
- Document memory requirements (4 GB recommended, 2 GB minimum with zram swap)
- Clarify boot menu entries and recovery accessibility from live, GRUB, and installed system
- Add fws-recovery as GTK application with bash engine and console fallback
- Document zram swap configuration for both live and installed system
- Detail recovery system integration: GRUB entry, systemd target, polkit policy, and desktop launcher
- Update syslinux splash and DEFAULT entry documentation
- Expand merge rule warning with syslinux-linux.cfg example and BIOS boot status
- Enhance troubleshooting section with detailed recovery procedures and known issues resolutions
- Add information about splash watchdog, RAM warnings, and diagnostic auto-generation
2026-07-07 02:48:22 +02:00
nocode 9c9891d98e docs(licensing): mark anaconda branding removal as completed and update date
Update the Anaconda branding removal checklist item from incomplete to completed, documenting the implementation details:
- Product name changed to FWS via ANACONDA_PRODUCTNAME and ANACONDA_PRODUCTVERSION variables
- Variables exported by live .xinitrc and read from FWS os-release
- Installation header displays "FWS 1.0 INSTALLATION"
- BIOS boot menu visual replaced with FWS splash screen
- Modified Anaconda version documented in LICENSING.md and TRADEMARKS.md

Also update the compliance date from 2026-06-17 to 2026-07-07.
2026-07-07 02:48:09 +02:00
nocode de05361b28 feat(releng): add polkit policy for fws-recovery privileged operations
Add PolicyKit authorization policy file to allow fws-recovery-gui to execute fws-recovery engine via pkexec with administrative privileges. The policy uses auth_admin_keep to require authentication only once per session.
2026-07-07 02:48:03 +02:00
nocode 9e3a02d96d feat(icons): add fws-recovery scalable application icon
Add a new scalable SVG icon for FWS recovery application featuring a medical cross symbol in FWS brand colors (blues) on a dark background. Icon is placed in the standard hicolor scalable apps directory.
2026-07-07 02:47:59 +02:00
nocode 881f771ed1 feat(releng): add fws-recovery desktop application launcher
Add desktop entry file for FWS Recovery application launcher in the installed OS. Provides bilingual (French/English) interface for system repair operations including GRUB, initramfs, and pacman restoration. The application runs as a regular user with privilege escalation handled via pkexec.
2026-07-07 02:47:54 +02:00
nocode bc08f59e74 feat(releng): add fws-recovery-session script for recovery mode
Add a new script that launches an X session with Openbox and the fws-recovery-gui application. This script is executed by startx during the FWS repair boot entry, providing a minimal graphical environment with Openbox as a fallback if the GUI crashes.
2026-07-07 02:47:48 +02:00
nocode e35b352a2a feat(recovery): add fws-recovery-gui GTK3 application for system repair
Implement a graphical interface for the FWS system recovery tool using GTK3.

Features:
- Scans for FWS installations on disk
- Provides action buttons for common repairs: GRUB reinstall, initramfs
  regeneration, pacman repair, and full system repair
- Displays real-time engine output in a log viewer
- Supports both root session (live mode) and regular user (pkexec elevation)
- Includes terminal access (chroot) for manual diagnostics
- Offers system reboot/poweroff controls

The GUI replaces Arch Linux branding with FWS in output for consistency
while preserving technical identifiers (package names, file paths) for
accurate diagnostics.
2026-07-07 02:47:43 +02:00
nocode 44b476e1d0 feat(zram): add zram-generator configuration for live environment
Enable compressed swap in RAM (zram) for the live environment to prevent memory saturation issues on systems with limited RAM. This configuration provides necessary headroom during Anaconda installation by leveraging zram's ~3:1 compression ratio on Python heaps. The configuration is automatically copied to installed systems via LiveOS payload, maintaining this safety net post-installation on modest hardware.
2026-07-07 02:47:35 +02:00
nocode f3feff4dc2 feat(systemd): add fws-recovery.target unit
Add new systemd target unit for FWS system recovery mode. This target is invoked via GRUB boot menu entry "Reparer FWS" and provides a multi-user environment with network access plus recovery session. The service includes proper dependency management to ensure getty@tty1 is properly handled.
2026-07-07 02:47:30 +02:00
nocode d00bcd4144 feat(releng): add fws-recovery-boot systemd service
Add systemd service unit for FWS system recovery boot mode. This service handles recovery session initialization on tty1 with fallback from GUI (via startx/openbox) to console assistant if graphical startup fails. The service is only activated via fws-recovery.target kernel command line parameter and does not interfere with normal boot processes.
2026-07-07 02:47:24 +02:00
nocode 34bf7909ad chore(releng): add issue file to airootfs
Add /etc/issue file to the releng airootfs configuration. This file provides pre-login banner information displayed to users connecting to the system.
2026-07-07 02:47:19 +02:00
nocode 50773635e8 feat(grub): add fws-recovery boot menu entry script
Add a new GRUB menu entry script that provides system recovery functionality.

The script generates a 'Réparer FWS (récupération du système)' menu entry that boots the system into fws-recovery.target with the fws.recovery=1 marker. This allows the recovery application to launch and perform system repairs via GUI or console fallback.

The script uses the fallback initramfs for maximum hardware compatibility during recovery operations, matching the UUID-based root detection logic of the standard 10_linux script.
2026-07-07 02:47:14 +02:00
nocode a75964e391 chore(releng): optimize syslinux splash image
reduce splash.png file size from 45400 to 14037 bytes through image optimization
2026-07-07 02:47:06 +02:00
nocode 7a44c4c291 chore(releng): change default syslinux boot option from arch to fws 2026-07-07 02:47:01 +02:00
nocode e25935fa75 chore(releng): add file permissions for recovery utilities and grub module
Add file permissions configuration for new recovery-related executables and grub module:
- fws-recovery-session executable
- fws-recovery-gui executable
- grub.d recovery module

All set with standard executable permissions (755) and root ownership.
2026-07-07 02:46:56 +02:00
nocode 40d49cb9e2 feat(releng): add zram-generator to x86_64 packages 2026-07-07 02:46:49 +02:00
nocode 3a437a8bfe docs(anaconda): add comment about fws-recovery boot entry and service
Document the FWS recovery boot entry generated by GRUB configuration and explain how the fws-recovery.target and fws-recovery-boot.service work together. Clarify that no explicit activation is needed as the service has no [Install] section and is pulled by the target through Requires, while the desktop launcher provides an alternative way to invoke recovery.
2026-07-07 02:46:43 +02:00
nocode 8e2fc2ec6a refactor(fws-recovery): restructure recovery script with subcommands and GUI support
Split monolithic script into modular functions and add subcommand interface:

- Add scan subcommand to list FWS installations
- Add repair subcommand with actions: grub, initramfs, pacman, pacman-update, all
- Add shell subcommand for chroot terminal access
- Implement support for operating on running system (no mount/chroot needed)
- Extract detection and mounting logic into reusable functions
- Add in_target() helper to execute commands in target (chroot or direct)
- Improve mkinitcpio warning documentation
- Maintain backward-compatible TUI mode when no args provided
- Enable GUI (fws-recovery-gui) to pilot repairs via subcommands
2026-07-07 02:46:30 +02:00
nocode c7221247f2 fix(fws-about): update distribution description to be more generic
Change the description from "Une distribution Arch Linux" to "Une distribution Linux" to better reflect the project's identity and avoid limiting it to Arch Linux specifically.
2026-07-07 02:46:20 +02:00
nocode 57f86ef5f7 chore(syslinux): remove legacy syslinux linux configuration file
Remove the outdated syslinux-linux.cfg configuration file from the baseline syslinux directory. This file is no longer needed as the project has moved away from syslinux-based boot configurations.
2026-07-07 02:46:11 +02:00
nocode a9ba4be111 chore(baseline): add file permissions for recovery tools and grub config
Add file permission entries for new recovery-related executables and grub configuration:
- /usr/local/bin/fws-recovery-session
- /usr/local/bin/fws-recovery-gui
- /etc/grub.d/42_fws-recovery

All entries set to 0:0:755 (root owner, executable).
2026-07-07 02:46:01 +02:00
nocode 6f5fc95e82 feat(baseline): add zram-generator package 2026-07-07 02:45:52 +02:00
nocode 680ee39e94 fix(baseline): update recovery boot flow to use X with gtk application
Changed recovery mode (fws.recovery=1) to start X server with the GTK recovery application (fws-recovery-session) instead of directly launching the console assistant. This provides a graphical interface when possible, with a fallback to the console assistant if X fails to start.

Also updated comments to clarify the different recovery mechanisms on the live ISO versus the installed system.
2026-07-07 02:45:47 +02:00
nocode ec442193f5 feat(xinitrc): enhance anaconda startup splash with better diagnostics and feedback
Add memory check warning for systems with less than 2GB RAM.

Replace simple pulsing splash with active monitoring that displays:
- Real-time anaconda activity from log files
- Elapsed time and current operation
- Stall detection after 4 minutes of inactivity
- Automatic diagnostic report collection (journal, dmesg, udev status)
- Detection of GUI readiness to auto-hide splash

Add anaconda branding environment variables (ANACONDA_PRODUCTNAME,
ANACONDA_PRODUCTVERSION, ANACONDA_ISFINAL) to show "FWS" instead of
"Fedora" in installer headers and logs.

Improve splash cleanup to handle both feeder process and optional
diagnostic dialogs.
2026-07-07 02:45:32 +02:00
nocode a0fc2c1019 chore(releng): add zenity, xterm, and fws-dnd to installer packages
Add zenity for graphical dialogs in the installer, xterm for terminal debugging and Openbox menu entry, and fws-dnd for D&D 5e management functionality (dice, characters, grimoire, sessions, combat).
2026-07-06 18:40:42 +02:00
nocode 9e62cedff0 feat(anaconda): add desktop installation retry mechanism for network failures
Implement a robust desktop installation retry system that handles transient network issues during the Arch installation process.

Changes:
- Create fws-desktop-retry script in /usr/local/bin for manual recovery after installation
- Add DNS resolution wait loop (up to 10 attempts with 3-second intervals)
- Implement pacman retry logic with up to 3 attempts (15-second delay between retries)
- Generate /var/lib/fws/desktop-failed marker file on installation failure
- Create informative MOTD message guiding users to retry desktop installation
- Preserve FWS-specific MOTD when it contains retry instructions

This allows users to recover from network/mirror failures without reinstalling the entire system.
2026-07-06 18:40:37 +02:00
nocode bfd43ff41a refactor(baseline syslinux)!: restructure syslinux configuration for multi-boot selection
Replace simple serial-based menu with system detection logic using whichsys.c32 to automatically select between PXE and direct system boot. This enables dynamic boot method selection while maintaining support for both boot scenarios.

BREAKING CHANGE: Changes syslinux.cfg structure from single menu-based boot to conditional boot selection. Systems relying on the previous serial menu interface and default 'arch' label will need to adapt to the new whichsys-based selection mechanism.
2026-07-06 18:40:32 +02:00
nocode 6fb48bd3a8 chore(pacman): remove fws-local repository configuration
Remove the custom fws-local repository section from the baseline pacman configuration. This local repository reference is no longer needed in the baseline setup.
2026-07-06 18:40:27 +02:00
nocode 61fa81dd24 feat(baseline): add firewalld to baseline packages
Include firewalld in the baseline x86_64 package list for system firewall management capabilities.
2026-07-06 18:40:23 +02:00
nocode 45ff906097 chore(efiboot): update efifi boot loader entry configuration
Update the UEFI boot loader entry to reflect FWS install medium branding, add sort-key directive for boot order control, align configuration formatting, and increase udev event timeout to 45 seconds for improved hardware compatibility during boot.
2026-07-06 18:40:19 +02:00
nocode 2daac4a455 docs(readme): clarify baseline and releng config merge rules
Expand the ISO branding documentation to include a detailed warning about the build process where baseline configs overwrite releng configs. Add a list of critical files that must be kept in sync between both profiles (profiledef.sh, packages.x86_64, pacman.conf, efiboot/loader/entries/01-archiso-linux.conf, syslinux/syslinux.cfg) and include a verification command to detect file divergences. This prevents silent data loss from config mismatches during the build.
2026-07-06 18:40:10 +02:00
nocode d65f08cfad fix(i3-config): disable picom in virtual machines to prevent rendering artifacts
picom's GPU compositing causes GTK window repaint issues in virtual environments (vmwgfx/llvmpipe), resulting in black content and visual artifacts. Use systemd-detect-virt to skip picom execution on VMs while keeping it enabled on real hardware.

The rounded corners feature is sacrificed in VM environments to maintain stable window rendering.
2026-07-04 14:10:27 +02:00
nocode 7268e7e23d docs(readme): document anaconda integration and fws tooling comprehensive updates
Update README to reflect completed Anaconda 45 integration on Arch Linux, replacing Calamares. Document:

- Desktop choices (GNOME, KDE Plasma, XFCE, Hyprland, i3) with VR compatibility matrix
- Installation flow: Anaconda hub with FWS spokes, LiveOS payload, recap page before write
- fws-* tooling ecosystem: fws-hello, fws-update, fws-keys, fws-powermenu, fws-about, fws-vr-setup, fws-proton-ge, fws-recovery
- Hyprland & i3 visual parity: Catppuccin Mocha theme, waybar/polybar, wofi/rofi, custom SDDM theme
- Boot sequence: menu entries (normal, safe graphics, recovery, speech) → Openbox live → Anaconda → recap → installation
- Build process: incremental AUR + local PKGBUILD compilation (anaconda stack)
- Expanded troubleshooting: VM rendering issues, SteamVR, Bluetooth, recovery mode, BIOS/UEFI boot
- Log locations for live and post-install debugging
- Clarified file locations: kickstart, spokes, configs, tools, branding
2026-07-04 14:10:15 +02:00
nocode d4bef2fe91 chore(releng): add fws-about desktop entry file
Add desktop entry file for the FWS About application with localized names and metadata for system integration.
2026-07-04 13:43:54 +02:00
nocode a1255eb5c6 build(releng): hide xfce4-about application from menus
Add desktop entry file to hide the xfce4-about application from application menus. Since xfce4-about is a dependency of Thunar through libxfce4ui and does not require a full XFCE session, this entry prevents it from appearing in application launchers by leveraging XDG_DATA_DIRS priority where /usr/local/share takes precedence over /usr/share.
2026-07-04 13:43:50 +02:00
nocode cbdb386ba0 feat(powermenu): add fws-powermenu universal power menu utility
Add a Python-based power menu script that works with both Hyprland (Wayland) and i3 (X11) desktop environments. Supports lock, logout, suspend, reboot, and poweroff actions with appropriate display server backends (wofi for Hyprland, rofi for i3).
2026-07-04 13:43:45 +02:00
nocode 4df4b2bff3 feat(fws-about): add fws-about dialog application for system information
Add a new GTK3-based Python application that displays an "About FWS" dialog with two tabs:

- "Le projet FWS" tab: shows project name, description, features, and home URL
- "Système" tab: displays system information including OS, kernel, desktop environment, hardware (CPU, memory, GPUs, disks)

The application detects system information from /proc, lsblk, lspci, and environment variables. It replaces the default Xfce About dialog with FWS branding.
2026-07-04 13:43:39 +02:00
nocode 73cf60e876 chore(releng): add fws-powermenu and fws-about to file permissions 2026-07-04 13:43:32 +02:00
nocode 07ff62af55 chore(anaconda): replace thunar with dolphin and gvfs with udisks2 in desktop environments
Update package lists for Hyprland and i3 desktop environments:
- Replace thunar file manager with dolphin (KDE's file manager)
- Add kio-extras for dolphin functionality
- Replace gvfs with udisks2 for USB mounting
- Update comments to reflect new mount mechanism

This change improves consistency and provides better integration for the respective desktop environments.
2026-07-04 13:43:29 +02:00
nocode 22a95badcf style(waybar): add custom power button styling
Add styling for custom power button with Catppuccin color scheme.

- Default color: #f38ba8 (rouge doux)
- Hover color: #ff6b81 (lighter red)
2026-07-04 13:42:44 +02:00
nocode a1adefe890 feat(waybar): add power menu module to waybar config
Add custom power menu module to waybar configuration with power icon and SUPER+X keyboard shortcut that launches fws-powermenu utility.
2026-07-04 13:42:41 +02:00
nocode 1f465bf647 feat(polybar): add power module to polybar configuration
Add a new power module to the polybar configuration that displays a power icon and triggers the fws-powermenu command on click. The module is styled with red foreground color and integrated into the modules-right bar.
2026-07-04 13:42:37 +02:00
nocode ba2dc4637e chore(i3-config): update file manager and add new keybindings and window rules
- Change default file manager from thunar to dolphin
- Add $mod+x keybinding for fws-powermenu (power menu)
- Add floating window rules for fws-hello and fws-about dialogs
2026-07-04 13:42:32 +02:00
nocode ccdbdf4adf chore(hyprland): update hyprland config with dolphin, powermenu binding, and window rules
Replace thunar file manager with dolphin for consistency.
Add power menu keybinding (Super+X) for shutdown/restart operations.
Add floating window rules for fws-hello and fws-about applications.
2026-07-04 13:42:28 +02:00
nocode 1c74b59459 fix(fws-hello): set window type hint to dialog and prevent resizing
Import Gdk and GLib modules to properly configure window behavior.

Set deterministic WM_CLASS using GLib.set_prgname() so that i3/Hyprland
window rules targeting "fws-hello" floating window behavior work correctly.

Set window type hint to DIALOG and disable resizing to ensure the window
remains floating with its natural size instead of being stretched across
the entire workspace, which would result in unreadable layout.
2026-07-04 13:42:24 +02:00
nocode 1cb48ce4e7 chore(baseline): add file permissions for fws-powermenu and fws-about scripts 2026-07-04 13:42:11 +02:00
nocode 17f9c79983 feat(rofi): add rofi configuration for fws application menu
Add rofi configuration file with FWS styling matching the Hyprland wofi look. Includes dark background, blue borders and selection, icon support, and Catppuccin Mocha color scheme.
2026-07-04 13:15:45 +02:00
nocode 3cdfdcfaeb feat(i3-config): add rofi configuration for i3 desktop environment
Install rofi configuration file (rofi-config.rasi) for i3 desktop to provide an application menu with FWS styling, equivalent to wofi in Hyprland. Configure rofi for both skeleton and existing user home directories with proper ownership settings.
2026-07-04 13:15:33 +02:00
nocode 9e59be79c3 feat(efiboot): add FWS recovery boot entry configuration
Add a new EFI boot loader entry for FWS recovery mode that boots the live system without the installer and launches the recovery assistant console. This enables system repair functionality including GRUB reinstallation, initramfs regeneration, and pacman repair.
2026-07-04 12:57:05 +02:00
nocode 080a77c5e3 feat(releng): add fws-updates-check script for update notifications
Add a new shell script that checks for available package updates using checkupdates from pacman-contrib. The script outputs the number of available updates with an icon for use in waybar/polybar status bars. Silent output when no updates are available allows the bar module to hide.
2026-07-04 12:57:01 +02:00
nocode 7e40a565cc feat(fws-update): add system update utility script
Add fws-update script to provide a one-command system update solution accessible from taskbar, fws-hello menu, or terminal.

The script performs:
- System update via pacman -Syu
- Package cache cleanup (retains 2 latest versions)
- Orphaned package detection and optional removal
- Flatpak updates if available

Requires root privileges and prompts for user confirmation on orphan removal.
2026-07-04 12:56:57 +02:00
nocode 338f6b87ee feat(recovery): add fws-recovery script for system repair from live environment
Add a comprehensive system recovery tool that:
- Detects FWS installations on disks including LVM volumes
- Mounts target root filesystem with /boot and ESP partitions
- Provides interactive menu for common repair operations:
  - GRUB reinstallation (UEFI and BIOS support)
  - Initramfs regeneration via mkinitcpio
  - Pacman database repair and system updates
  - Interactive chroot shell for manual intervention

The script handles automatic LVM activation, filesystem detection via os-release, and proper cleanup on exit.
2026-07-04 12:56:51 +02:00
nocode 4f4afec6b2 feat(releng): add fws-proton-ge script for proton-ge installation
Add a user-friendly bash script that automates downloading and installing
Proton-GE (GloriousEggroll) for Steam. The script fetches the latest release
from GitHub, verifies integrity with SHA512 checksums, and installs it to the
user's Steam compatibility tools directory. Includes error handling and user
guidance for Steam configuration.
2026-07-04 12:56:44 +02:00
nocode 640aa15af8 feat(releng): add fws-hello welcome window script
Add a new GTK3-based welcome window script that displays on first boot after installation. The script provides quick access to system setup tasks including system update, multimedia codecs installation, Proton-GE setup, VR configuration, and keyboard shortcuts (on Hyprland/i3). Users can opt to hide the window on subsequent boots via a checkbox and flag file.
2026-07-04 12:56:39 +02:00
nocode 73f73ff17e feat(releng): add fws-hello desktop autostart entry
Add desktop entry file to automatically launch fws-hello welcome window on first startup. This provides a user-friendly greeting interface when the system boots.
2026-07-04 12:56:33 +02:00
nocode 6c8e7c0f78 feat(syslinux): add fws recovery boot option to syslinux config
Add a new boot menu entry for FWS installation recovery that boots into a guided console assistant without reinstalling. The recovery mode is triggered via the fws.recovery=1 kernel parameter.
2026-07-04 12:56:28 +02:00
nocode 5762f59d95 chore(releng): add new executable file permissions to profiledef
Add permission entries for five new utility scripts:
- fws-hello
- fws-update
- fws-updates-check
- fws-proton-ge
- fws-recovery

All are configured with standard permissions (0:0:755).
2026-07-04 12:56:24 +02:00
nocode affba77349 feat(releng): add firewalld to x86_64 packages 2026-07-04 12:56:17 +02:00
nocode 87977ce8d7 build(releng): add python-gobject, pacman-contrib packages and enable firewalld
- Add python-gobject for fws-hello welcome window (GTK3 comes with Firefox)
- Add pacman-contrib for checkupdates/paccache (update indicator and fws-update)
- Enable firewalld.service by default with public zone (blocks inbound, allows outbound)
- firewalld is already in the base image and can be activated offline
- GUI included in firewall-config package; KDE has additional plasma-firewall
2026-07-04 12:56:13 +02:00
nocode b5f00d8dd2 feat(waybar): add custom updates module to waybar configuration
Add a new custom/updates module to the waybar right-side modules that displays available system updates. The module executes fws-updates-check every 30 minutes (1800 seconds) and allows users to install updates by clicking, which launches a kitty terminal running fws-update. Tooltip text is displayed in French.
2026-07-04 12:56:08 +02:00
nocode 349cc28d7d feat(polybar): add updates module to polybar configuration
Add a new custom script module to check for system updates in the polybar status bar. The module runs fws-updates-check every 30 minutes and allows users to trigger updates with a left click to open the update utility in kitty terminal.
2026-07-04 12:56:03 +02:00
nocode fc1bc218a4 feat(i3-config): add fws-hello welcome window on startup
Add execution of fws-hello with --autostart flag to display a welcome window on first boot only. The window will automatically hide after the initial startup.
2026-07-04 12:55:56 +02:00
nocode d399fc5fa6 feat(hyprland): add fws-hello welcome window on first startup
Add execution of fws-hello with --autostart flag to display a welcome window on first launch. The window will be automatically hidden on subsequent startups.
2026-07-04 12:55:50 +02:00
nocode 4d36e86f51 feat(hardware-setup): add mangohud gaming performance overlay packages
Add mangohud and lib32-mangohud packages to the desktop environment setup for gaming performance monitoring. Mangohud provides an in-game FPS and performance metrics overlay that can be used with Steam games via '%command%' prefix.
2026-07-04 12:55:45 +02:00
nocode a0f269cf90 chore(baseline): add file permissions for new fws utility scripts
Add file permission entries for five new utility scripts in the fws toolkit:
- fws-hello
- fws-update
- fws-updates-check
- fws-proton-ge
- fws-recovery

All scripts are configured with owner:group 0:0 and executable permission 755.
2026-07-04 12:55:38 +02:00
nocode 7831b537d7 feat(baseline): add recovery mode boot option to zprofile
Add support for recovery mode that can be triggered via the fws.recovery=1 kernel parameter. When recovery mode is enabled, the system launches the fws-recovery console assistant directly instead of starting the graphical environment.

This allows users to access repair tools without needing to start X server, improving system recovery capabilities.
2026-07-04 12:55:25 +02:00
nocode 2c5f08330a refactor(fws-keys): make keyboard shortcuts tool universal for hyprland and i3
Rename fws-hypr-keys to fws-keys and extend it to support both Hyprland and i3 desktop environments. The tool now detects the session type and uses appropriate methods:

- Hyprland: queries hyprctl binds for real loaded bindings (wofi dmenu)
- i3: parses ~/.config/i3/config with variable resolution (rofi dmenu)

Update all references in configs and templates. Add VM detection warning in fws-vr-setup to prevent VR attempts in virtual machines where GPU/USB access is unavailable.

Add ttf-liberation font dependency for Steam UI stability on minimal installations and improve error guidance for Steam crashes.
2026-07-04 03:59:09 +02:00
nocode a63021cde7 feat(icons): add fws-vr vr headset icon
Add scalable SVR icon for the fws-vr-setup launcher using FWS/Catppuccin color palette. The icon depicts a VR headset with straps, lenses, and nose notch in blue and dark theme colors.
2026-07-04 03:30:46 +02:00
nocode b408c84a8c feat(releng): add fws-vr-setup desktop application entry
Add desktop application entry for VR setup tool that allows users to install drivers and runtimes for VR headsets including HTC Vive, Valve Index, and Meta Quest devices. The application launches fws-vr-setup command in a kitty terminal.
2026-07-04 03:30:41 +02:00
nocode 2a3824a991 feat(i3): add default i3 window manager configuration
Add FWS default i3 configuration file with X11 session support.

Includes:
- Color scheme based on Catppuccin Mocha palette
- Keyboard bindings for common applications and window management
- VR headset driver setup integration (Super+Shift+V)
- Multi-monitor and workspace navigation support
- Screenshot and multimedia control bindings
- Status bar configuration with i3status
- Autostart for dunst, nm-applet, polkit-gnome, and blueman
2026-07-04 03:30:38 +02:00
nocode 06121343e7 feat(releng): add fws-vr-setup script for VR driver installation
Add interactive shell script for installing VR drivers and runtimes based on headset type. Supports HTC Vive/Valve Index (native SteamVR), Meta Quest/Pico (ALVR streaming), and Monado (OpenXR runtime). Script automatically detects GPU and installs appropriate 32-bit Vulkan libraries required by SteamVR. Includes multilib configuration and user guidance for each VR setup path.
2026-07-04 03:30:28 +02:00
nocode 3c12d9514c chore(releng): add fws-vr-setup executable to file permissions
Register the new fws-vr-setup script with executable permissions (755) in the releng profile configuration.
2026-07-04 03:30:23 +02:00
nocode 5f108c607b feat(releng): add i3 window manager desktop environment configuration
Add i3 window manager as a selectable desktop environment option in the anaconda kickstart configuration. Includes:

- i3-wm with essential packages (i3status, i3lock, rofi, kitty, etc.)
- X11 server requirement for VR compatibility (SteamVR incompatible with Wayland)
- Bluetooth service enablement for i3 sessions
- Icon cache update for VR launcher visibility in rofi
- i3 configuration template installation for skeleton and existing users
- Keyboard layout handling via X11 xorg.conf.d (no keyboard injection needed)

This enables users to install a lightweight X11-based desktop environment with VR support capabilities.
2026-07-04 03:30:18 +02:00
nocode 95eb6c15e0 fix(fws-setup-hardware): add blueman bluetooth manager support for i3 desktop
Include blueman in the base packages for i3 desktop environment, alongside hyprland and xfce, to provide graphical bluetooth management and pairing assistant for i3 users.
2026-07-04 03:30:12 +02:00
nocode 4a598c854a chore(baseline): add fws-vr-setup executable permission 2026-07-04 03:30:07 +02:00
nocode 9f46b9bb1b feat(fws_desktop): add i3 window manager option with VR compatibility note
Add i3 X11 window manager as a desktop environment option with explicit VR compatibility information. Update Hyprland label to clarify it uses Wayland and is not VR compatible. Adjust position indices for subsequent radio button options.
2026-07-04 03:30:02 +02:00
nocode 14d138cae4 feat(fws_recap): add i3 desktop option and update hyprland description
Add i3 desktop environment option with X11 and VR compatibility note.
Update hyprland description to clarify Wayland protocol and VR incompatibility.
Both descriptions now include information about included applications (Firefox, SDDM).
2026-07-04 03:29:56 +02:00
nocode 8e2e74a227 feat(fws_desktop): add i3 desktop option and update hyprland description
Add i3 window manager as a desktop choice with X11 and SteamVR compatibility note. Update Hyprland description to clarify it uses Wayland and is not compatible with VR.
2026-07-04 03:29:44 +02:00
nocode d0e431329d refactor(hyprland): disable splash rendering in hyprland configuration
Add disable_splash_rendering option to misc section to hide the startup splash message at the bottom of the screen.
2026-07-04 03:06:39 +02:00
nocode 75113980f0 refactor(waybar): add format-no-controller for bluetooth module
Configure waybar bluetooth module to use an empty format when no bluetooth controller is detected, improving UI consistency.
2026-07-04 03:06:25 +02:00
nocode 4d123b6f5d fix(hyprland): only launch blueman-applet if bluetooth adapter exists
Prevent blueman-applet from showing connection errors in virtual machines where no Bluetooth adapter is present. The applet now checks for the existence of a Bluetooth adapter in /sys/class/bluetooth before launching.

This change adds a conditional check that gracefully skips the applet initialization when no hardware is available, improving user experience in VM environments.
2026-07-04 03:05:57 +02:00
nocode a09278b63f feat(anaconda): add modified anaconda summary hub glade template
Add anaconda-summary-hub.glade file, a customized copy of anaconda 45.8's summary.glade without the "We won't touch your disks…" disclaimer label at the bottom of the hub.

This file is placed in a neutral path (usr/local/share/fws/) because mkarchiso applies the airootfs before pacstrap. Placing it at the anaconda package path would cause pacman to refuse installation due to filesystem conflicts. The live environment's .xinitrc copies this file to /usr/share/anaconda/ui/hubs/summary.glade during boot.
2026-07-04 02:52:33 +02:00
nocode dd71664b9e chore(baseline): remove anaconda summary glade file overlay
Delete the customized summary.glade file that was overlaying the anaconda package file. This file was previously used to hide the "We won't touch your disks…" message at the bottom of the installation summary hub.
2026-07-04 02:52:24 +02:00
nocode 2aaf1cbfd5 chore(baseline): add anaconda summary hub glade override in xinitrc
Add logic to override the anaconda summary hub glade file at live boot time. The custom glade file is stored in /usr/local/share/fws and copied to /usr/share/anaconda/ui/hubs/summary.glade during X initialization.

This workaround is necessary because mkarchiso applies the airootfs overlay before pacstrap, which prevents pacman from installing anaconda over existing files. The file is therefore delivered to /usr/local/share/fws and overlaid at boot when the live overlay is writable.
2026-07-04 02:52:18 +02:00
nocode e20523470f feat(anaconda): add customized summary glade ui file
Add a customized version of anaconda's summary.glade UI definition file to the baseline airootfs configuration. This file is based on anaconda 45.8 but with the "We won't touch your disks..." label removed from the bottom of the hub, allowing the overlay to mask the original package file without requiring an anaconda rebuild.
2026-07-04 01:07:39 +02:00
nocode c56db3de04 chore(releng): remove fws-dnd package from x86_64 build
Remove the fws-dnd package and its associated comment from the x86_64 package list. This package is no longer needed for the release engineering build.
2026-07-04 01:02:50 +02:00
nocode 81dfac3762 chore(releng): add fws-dnd package to x86_64 configuration
Add fws-dnd (D&D 5e manager for dice, characters, grimoire, sessions, and combat) to the releng packages.x86_64 configuration.
2026-07-04 01:01:27 +02:00
nocode 43ecf7f54f chore(releng): add pipewire and bluetooth packages to desktop environments
Add PipeWire audio stack (pipewire, pipewire-pulse, wireplumber, pavucontrol)
and Bluetooth support (bluez, bluez-utils, blueman) to xfce and hyprland
package lists to ensure they are installed alongside the desktop environment.

Enable bluetooth.service at installation time for hyprland to have Bluetooth
ready at first boot. Update comments to reflect the new guaranteed installation
of audio and Bluetooth components as part of the desktop environment transaction.
2026-07-04 01:01:20 +02:00
nocode 96e5721ad8 chore(hyprland): add blueman applet and remove steam keybinding
Add blueman-applet to the exec-once commands for bluetooth management.
Remove the Steam launcher keybinding (SUPER+KP_Multiply) from the configuration.
2026-07-04 01:01:12 +02:00
nocode cef3b291d1 chore(packages): add fws-dnd package to baseline x86_64 configuration
Add the fws-dnd package (D&D 5e Manager for dice, characters, grimoire, sessions, and combat) to the baseline x86_64 packages list under the FWS section.
2026-07-04 01:01:09 +02:00
nocode 6e59e1d4c4 chore(baseline): update installer preparation message in french
Change the estimated time message from '2 à 4 minutes' to 'Encore une à deux minutes' to provide more accurate timing information to users during the FWS installer preparation phase.
2026-07-04 01:00:53 +02:00
nocode 3a5efffc61 feat(wofi-style): add wofi application menu styling with catppuccin mocha theme
Add a new CSS stylesheet for wofi (application launcher menu triggered with Super+R) that implements the Catppuccin Mocha blue color scheme. The stylesheet includes styling for the window, input field, menu entries, and selection states with rounded corners and appropriate contrast ratios.
2026-07-04 00:26:37 +02:00
nocode 53942aece0 feat(releng): add wofi keyboard shortcuts panel styling
Add CSS stylesheet for the FWS keyboard shortcuts panel (fws-hypr-keys) using monospace font to align key combinations with descriptions. Uses the same color palette as the wofi menu with Catppuccin theme colors.
2026-07-04 00:26:31 +02:00
nocode e98a8320fb feat(wofi): add wofi application menu configuration
Add wofi configuration file for FWS with settings for case-insensitive search, image support, custom dimensions, and application prompt.
2026-07-04 00:26:27 +02:00
nocode 038700e22d feat(hyprland): add desktop portal and notification support to hyprland setup
Add missing packages for hyprland desktop environment:
- mako + libnotify for notifications
- polkit-gnome for GUI authentication
- network-manager-applet for network management in tray
- xdg-desktop-portal-hyprland for screen sharing
- gvfs for USB mounting in thunar
- ttf-nerd-fonts-symbols for waybar icons

Add start-hyprland launcher support for hyprland ≥0.53 to properly prepare systemd session/environment.

Add wofi configuration files (config and style.css) for application menu styling.

Update skeleton directory setup to install wofi configs for new users and fix chown path formatting.
2026-07-04 00:26:23 +02:00
nocode 4857a9fda9 style(waybar): redesign waybar stylesheet with catppuccin mocha theme and pill-style modules
Update waybar-style.css to version 2 with the following changes:

- Switch to Catppuccin Mocha color scheme with blue accents
- Redesign bar as floating rounded container instead of bottom border
- Style modules as pill-shaped boxes with individual backgrounds
- Add Nerd Font symbols support for better icons
- Improve workspace buttons with hover effects and transitions
- Add tooltip styling and proper color coding for each module
- Update clock styling with dedicated background
- Enhance battery, network, and bluetooth status colors
- Refine spacing, padding, and border-radius throughout for modern appearance
2026-07-04 00:26:16 +02:00
nocode 289a70c10c style(waybar): enhance waybar configuration with improved styling and additional modules
Update waybar configuration with:
- Adjusted height (34 to 36) and spacing (4 to 2)
- Added margins (top: 6, left: 10, right: 10)
- Added workspace navigation with scroll (e-1/e+1)
- Added backlight, bluetooth, and battery modules
- Enhanced clock format with calendar styling
- Improved audio controls with pavucontrol integration
- Added network bandwidth display
- Updated all module icons to use modern emoji/nerd font glyphs
- Improved tooltips with more detailed information
- Adjusted max window name length from 55 to 45 characters
2026-07-04 00:26:09 +02:00
nocode 8224a24d02 chore(hyprland): add startup services and brightness/blueman window rules
- Add mako notification daemon to startup services
- Add nm-applet for network tray icon
- Add polkit-gnome authentication agent for privilege escalation dialogs
- Add brightness control keybinds for XF86MonBrightnessUp/Down
- Add float window rule for blueman-manager
- Update startup comment to describe all session initialization tasks
2026-07-04 00:26:02 +02:00
nocode 0ab90857e5 feat(fws-setup-hardware): add brightnessctl and blueman to base stack
Add brightnessctl for brightness control without root via logind,
consumed by XF86MonBrightness* keys and waybar backlight module.

Add blueman as graphical Bluetooth manager for desktop environments
that don't provide their own (KDE and GNOME have built-in managers).
Blueman provides system tray integration and pairing assistant.

Update comments to clarify the purpose of these new packages.
2026-07-04 00:25:57 +02:00
nocode d6212deaa9 feat(fws-hypr-keys): add optional style support and adjust wofi dimensions
- Import os module to check for style file existence
- Add _STYLE constant pointing to wofi-keys.css stylesheet
- Increase wofi window dimensions from 780x560 to 820x600
- Conditionally apply custom style if the stylesheet exists
- Refactor wofi command construction for better readability
2026-07-04 00:25:45 +02:00
nocode 754b6e8802 fix(syslinux): add udev event timeout to all boot options
Add udev.event_timeout=45 parameter to all APPEND kernel boot options in the syslinux configuration to prevent udev event timeout issues during boot on systems with slow device initialization.
2026-07-04 00:13:13 +02:00
nocode 053998d1c0 fix(releng): add udev event timeout to speech boot entry
Add udev.event_timeout=45 parameter to the accessibility boot entry to prevent timeout issues during device initialization on systems with slower hardware or many devices.
2026-07-04 00:13:09 +02:00
nocode 218b0707a4 fix(efiboot): add udev event timeout to nomodeset boot entry
Add udev.event_timeout=45 parameter to the safe graphics boot option to prevent timeout issues during device initialization on systems with slower hardware or complex device trees.
2026-07-04 00:13:04 +02:00
nocode 7c11f53520 fix(releng): add udev event timeout to archiso linux boot entry
Add udev.event_timeout=45 kernel parameter to the UEFI boot entry for the archiso linux image. This timeout helps prevent boot delays caused by udev waiting for device events to complete.
2026-07-04 00:12:53 +02:00
nocode be0844b50b feat(releng): add uefi boot entry with nomodeset graphics option
Add a new UEFI boot loader entry for Arch Linux installation medium with the nomodeset kernel parameter. This provides a fallback boot option for systems with GPU or hypervisor drivers that have issues with KMS (kernel mode setting), allowing the system to fall back to VESA/EFI framebuffer graphics mode which is slower but more reliable.
2026-07-04 00:00:11 +02:00
nocode 24370461ea feat(sddm): add fws login theme for kde xfce and hyprland
Introduce a new SDDM theme "fws" designed for FWS with support for KDE, XFCE, and Hyprland desktop environments.

The theme is built using pure QtQuick and QtQuick.Controls (Basic style) with no additional dependencies beyond what SDDM already requires. It includes:

- Main.qml: Custom login screen with clock, date, user/password fields, session selector, and power controls
- metadata.desktop: Theme metadata and configuration
- theme.conf: Default configuration with customizable accent color and gradient colors

The design uses a dark theme with accent colors and supports capslock detection and login error handling.
2026-07-04 00:00:05 +02:00
nocode 5d6d1c6db8 feat(waybar): add waybar style configuration for fws theme
Add CSS stylesheet for Waybar that matches the FWS theme, SDDM appearance, and Hyprland borders. Includes styling for workspaces, clock, custom key help button, and system status modules with Catppuccin blue accent color (#89b4fa).
2026-07-03 23:59:58 +02:00
nocode 23df7656e5 feat(waybar): add waybar configuration for hyprland desktop environment
Add waybar panel configuration with modules for workspaces, window title, clock, volume, network, CPU, and memory monitoring. Configured for top panel layout with French localization support.
2026-07-03 23:59:53 +02:00
nocode a28a64310a feat(releng): add fws-hypr-keys script to display hyprland keyboard shortcuts
Add a new Python utility script that displays actual Hyprland keyboard shortcuts from hyprctl binds output. The script:

- Queries hyprctl binds to get loaded key bindings from the compositor
- Formats keybinds with human-readable modifiers and key labels (French localization)
- Displays descriptions from bindd config entries or falls back to dispatcher actions
- Opens results in wofi dmenu for incremental search and filtering
- Handles special keys like mouse buttons, arrow keys, and common special keys
- Deduplicates repeated bindings (e.g., from resize actions)

No additional dependencies required beyond python3 and wofi (already in FWS + Hyprland packages).
2026-07-03 23:59:49 +02:00
nocode 7aa1276e2c feat(anaconda): add fws recap glade ui definition
Add new Glade UI definition file for the FWS recap (summary before installation) spoke. This defines the AnacondaStandaloneWindow layout with a scrollable text view to display installation summary information.
2026-07-03 23:59:44 +02:00
nocode 9df1f444bc feat(fws): add recap spoke for pre-installation summary
Add FWSRecapSpoke, a standalone spoke that displays a comprehensive summary of user configuration choices and detected hardware before installation begins. The spoke intercepts the installation flow between the SummaryHub and ProgressSpoke, allowing users to review and modify their choices before any disk writes occur.

Features:
- Displays user-selected configuration (language, keyboard, timezone, users, partitioning, desktop, hostname)
- Shows detected hardware (virtualization, GPUs, disks, network status)
- Provides installation flow overview and warnings about automatic partitioning
- Implements back navigation to return to the hub for modifications
- Never marks as completed to ensure proper flow control in anaconda's action queue
2026-07-03 23:59:39 +02:00
nocode 7fe54d0791 fix(setup-aur.sh)!: use explicit package list instead of glob for install and publish
Change publish_to_repo() to accept explicit file list via variadic arguments
instead of a directory glob. This prevents re-publishing stale .pkg files
from previous builds that may still exist in BUILD_DIR.

Update build_or_skip() to get the actual list of packages produced by the
current makepkg invocation using --packagelist, filter for .pkg.tar.* files,
verify they exist on disk, and pass only those to pacman -U and publish_to_repo().

This fixes issues where:
- After pkgrel bumps, old .pkg files would cause pacman to receive duplicate
  package names ("repeated target" error)
- Stale packages would be republished over the purge in local-repo
- Optional debug packages would cause errors when not produced

BREAKING CHANGE: publish_to_repo() function signature changed from publish_to_repo(name, directory) to publish_to_repo(name, file1, file2, ...) - callers must pass explicit file paths instead of a directory path
2026-07-03 23:59:32 +02:00
nocode 4249282052 fix(python-meh): fix python 3.11 compatibility by replacing removed gettext.ldgettext
Replace gettext.ldgettext with gettext.dgettext in Anaconda exception handler
since ldgettext was removed in Python 3.11. Without this fix, the exception
handler crashes with AttributeError when trying to display errors, resulting
in raw tracebacks instead of the meh dialog in both GUI and TUI modes.

Also clean up stale __pycache__ directories from previous extractions and
add validation to ensure ldgettext is completely removed.
2026-07-03 23:59:25 +02:00
nocode 5e66f99816 feat(syslinux): add safe graphics boot option with nomodeset kernel parameter
Add a new boot menu entry for FWS without kernel mode setting (KMS) to provide a fallback option for systems where KMS causes issues. This entry uses the nomodeset kernel parameter to force the framebuffer to VESA/EFI mode, which is slower but more reliable for problematic GPU drivers and hypervisors like VMware.
2026-07-03 23:59:20 +02:00
nocode 35f77ba33d chore(releng): add file permissions for fws-hypr-keys script 2026-07-03 23:59:13 +02:00
nocode d8e67fe861 refactor(anaconda): clarify payload mechanism and enhance desktop environment configurations
Update interactive-defaults.ks kickstart file with:

- Clarified documentation on LiveOS payload detection and mounting via /run/rootfsbase bind-mount instead of direct liveimg directive
- Added comprehensive comments explaining why liveimg directive is intentionally omitted and how anaconda --liveinst handles payload creation
- Enhanced XFCE and Hyprland package sets with noto-fonts for proper TTF rendering and additional tools (thunar, hyprlock, grim, slurp, wl-clipboard, playerctl)
- Implemented SDDM theme configuration with FWS theme support and accent colors per desktop environment (teal for XFCE, Catppuccin blue for Hyprland, Breeze blue for KDE)
- Added GDM (GNOME) configuration via dconf with welcome banner, clock enhancements, and numlock settings
- Expanded Waybar configuration installation for Hyprland with config and style files
- Added comprehensive documentation for greeter customization and Hyprland VM compatibility
- Included details about FWS recap spoke (standalone GUI summary screen) replacing zenity-based pre-install approach
2026-07-03 23:59:09 +02:00
nocode 68850f0ed2 feat(hyprland): expand hyprland config with keybindings, animations, and window rules
Add comprehensive keybinding descriptions using bindd/binde for keyboard shortcut display, implement Catppuccin Mocha color scheme with blue gradient borders, enable animations with custom bezier curve, configure touchpad settings, add blur/shadow configuration for VM compatibility, implement scratchpad support, add screenshot and multimedia key bindings with PipeWire/playerctl integration, and define window floating rules.

Keybindings now match i3/sway FWS configuration with French descriptions that automatically appear in fws-hypr-keys shortcut viewer. Configuration includes multi-screen support, workspace navigation (1-10), window resizing, and various utility bindings.
2026-07-03 23:59:00 +02:00
nocode aa9cb13648 chore(baseline): add file permissions for selinux and hardware setup scripts
Add executable permissions for load_policy, setstatus, fws-setup-hardware, and fws-hypr-keys scripts in the baseline profile configuration.
2026-07-03 23:58:53 +02:00
nocode 8b7af60239 chore: remove pycache file 2026-07-03 23:58:48 +02:00
nocode ee0082baa1 feat(xinitrc): add anaconda installer improvements and splash screen
Add several improvements to the liveinst installer workflow:

- Expose archiso squashfs at /run/rootfsbase where Anaconda expects to find
  the Live OS image, fixing SourceSetupError crashes
- Display immediate splash screen with zenity during Anaconda startup (2-4 min
  wait time) to provide user feedback and prevent perceived hangs
- Wrap liveinst with dbus-run-session to provide D-Bus session bus, fixing
  GLib.GError crashes from missing ScreenSaver service
- Add hardware summary log output to debug information collection

These changes address three crash scenarios identified during testing and
improve user experience during installer initialization.
2026-07-03 23:58:28 +02:00
nocode 0b0fcd8ce4 feat(releng): add hardware detection and driver setup script
Add fws-setup-hardware script that automatically detects and installs:
- GPU drivers (NVIDIA, AMD, Intel) based on lspci output
- Hypervisor guest tools (VMware, VirtualBox, QEMU, Hyper-V)
- Audio/mic/headset stack (PipeWire), Bluetooth (BlueZ), containers (Podman)
- Optional desktop stack when non-CLI desktop is selected: Steam, Wine, VR support (Monado/OpenXR), and winboat-bin

The script is integrated into the Anaconda kickstart post-installation phase and is non-blocking to prevent installation failures from missing packages or network issues.

Also update profiledef.sh to set correct permissions (755) for the new script.
2026-06-24 23:32:37 +02:00
nocode 99006b9ede feat(hyprland): add hyprland configuration with keyboard layout injection and vm compatibility
Add Hyprland desktop environment support with:

- New hyprland.conf template with keyboard layout placeholders (__KB_LAYOUT__ and __KB_VARIANT__) that get injected during installation
- Hyprland wrapper script (/usr/local/bin/fws-hyprland) that detects virtual machines and forces software rendering to prevent compositor crashes on virtual GPUs (VMware vmwgfx, VirtualBox)
- Automatic configuration deployment to /etc/skel and existing user home directories
- Refactored keyboard layout detection logic (moved to section 3bis) to support both X11/SDDM and Hyprland configurations
- Added locale generation during installation to ensure proper language support

Keyboard layout is now derived from either kickstart configuration or system locale using langtable, providing consistent keyboard behavior across all desktop environments.
2026-06-24 23:06:20 +02:00
nocode ff98a1e186 fix(anaconda): improve desktop environment setup and system configuration
- Fix XFCE display manager: replace lightdm-gtk-greeter with sddm for consistency
- Add SDDM numlock initialization for KDE, XFCE, and Hyprland
- Configure X11 keyboard layout from kickstart or locale via langtable to ensure correct SDDM login keyboard
- Enable wheel group sudo access and add all human users to wheel group
- Apply FWS branding to os-release, lsb-release, and GRUB configuration
- Remove inappropriate live system motd file
- Fix DNS resolution: configure systemd-resolved stub resolver with NetworkManager integration to prevent "Limited Connectivity" on first boot
- Reformat case statement for better readability
2026-06-24 19:02:31 +02:00
nocode 7daca57714 feat(anaconda): add desktop environment selection spoke for FWS
Implement a new Anaconda spoke that allows users to select their preferred desktop environment (CLI, GNOME, KDE Plasma, Hyprland, or XFCE) during installation.

The spoke:
- Displays radio buttons for five desktop choices with CLI as the default
- Persists the selection to /tmp/fws-desktop for the kickstart %post script
- Supports revisiting the spoke and resuming previous selections
- Includes a GTK UI definition file (glade) with translatable labels
- Integrates as a SoftwareCategory spoke in the Anaconda installer

The implementation leverages Anaconda's runtime spoke discovery mechanism via the airootfs overlay, avoiding the need to rebuild the anaconda package.
2026-06-24 17:58:52 +02:00
nocode d409406fde feat(build-offi): add build date to iso filename and prompt for version/variant on host
Refactor version and variant handling to prompt for metadata on the host before entering container, enabling proper terminal display. Pass FWS_ISO_VERSION and FWS_ISO_TAG via environment variables to container.

Add build date (AAAA.MM.JJ format) to the final ISO filename: fws-<version>-<variant>-<AAAA.MM.JJ>.iso

Implement ask_iso_meta_host() function to validate and collect version (major.minor.build) and variant name inputs before containerization. Use sudo -E to preserve environment variables across privilege escalation.

Update ISO metadata prompt logic to skip if already provided, supporting both container execution and native Arch/WSL environments.
2026-06-23 12:50:45 +02:00
nocode 0de970239f feat(build): refactor build script with interactive version and variant selection
Replace automatic version reading from VERSION file with interactive prompts for version (major.minor.build) and ISO variant name (e.g., cli, gnome, kde).

Changes:
- Remove automatic VERSION file reading; now serves as default with prompt
- Add interactive prompts for version and variant name with validation
- Simplify ISO naming from FWS-v{major}-{minor}-{build}-{date}-x86_64.iso to fws-{version}-{variant}.iso
- Update hostname from 'archiso' to 'fws'
- Add custom FWS logo for fastfetch with color configuration
- Improve error messages and user feedback

This enables flexible build variants and streamlines the build process.
2026-06-23 11:02:41 +02:00
nocode 7d6bba42b2 feat(anaconda): add hostname field to user spoke and fix keyboard layout by territory
Replace keyboard correction logic in kickstart with anaconda package patches:

- Add "Nom de machine" (hostname) field to the User spoke in anaconda GUI
- user.py writes the hostname value to /tmp/fws-hostname for the kickstart %post
- Simplify kickstart to read from /tmp/fws-hostname instead of live hostname
- Fix keyboard layout selection to use full locale (e.g. fr_CH) instead of just language (fr)
  to correctly derive territory-specific keymaps (ch(fr) for Switzerland, not fr for France)
- Remove outdated fix_keyboard() function from kickstart

Also add --network=host flag to podman run in setup-aur.sh to ensure DNS and internet
connectivity through the host stack instead of the bridge (fixes timeout issues on
certain networks with firewalld/NAT).

Bump anaconda pkgrel from 2 to 4.
2026-06-23 10:48:32 +02:00
nocode 1184ba17d9 fix(releng): correct hostname and keyboard mapping in anaconda post-installation
Fix two bugs in the anaconda post-installation script:

1. Hostname handling: Anaconda writes an empty /etc/hostname to the target system. Now we preserve the hostname set during installation from the live environment, falling back to 'fws' if using default values.

2. Keyboard mapping: Anaconda derives keyboard layout from language only, ignoring territory. For locales with territory variants (e.g., fr_CH for Switzerland), we now apply territory-specific keyboard maps to both console (vconsole.conf) and X11 (xorg.conf.d) using langtable for proper mapping.
2026-06-23 08:40:42 +02:00
nocode d25ab9b4da fix(build): add --network=host to podman run commands for dns connectivity
Add --network=host flag to podman run commands in both build.sh and build-offi.sh to fix DNS resolution issues.

The podman bridge network was unable to route to external hosts on certain wifi networks due to firewalld/NAT misconfiguration after network changes, resulting in "core.db: Resolving timed out" errors. By sharing the host's network stack, the container inherits the same network connectivity including DNS resolution via 127.0.0.53/systemd-resolved that is available on the host.
2026-06-23 08:06:53 +02:00
79 changed files with 4620 additions and 128 deletions
+31 -2
View File
@@ -2,15 +2,35 @@
Changelog
#########
[XX] - YYYY-MM-DD
=================
[0.6.0] - 2026-07-07
====================
Added
-----
- **Installateur Anaconda 45 porté sur Arch** (payload LiveOS) : page « Récapitulatif avant installation » avec bouton
Précédent, choix du bureau intégré (GNOME, KDE, XFCE, Hyprland, i3, CLI) avec compatibilité VR affichée, splash de
préparation alimenté en direct par les logs (diagnostic automatique en cas de blocage), garde-fou RAM (< 2 Go),
mécanisme de retry réseau pour l'installation du bureau (``fws-desktop-retry``).
- **Outillage FWS** : ``fws-hello`` (accueil 1er démarrage), ``fws-update`` + indicateur de mises à jour,
``fws-keys`` (raccourcis réels), ``fws-powermenu``, ``fws-about``, ``fws-vr-setup`` (SteamVR/ALVR/Monado),
``fws-proton-ge``, ``fws-setup-hardware`` (drivers GPU/VM/audio/Bluetooth), ``fws-dnd``.
- **Récupération système** (« point de restauration ») : application GTK ``fws-recovery-gui`` + moteur ``fws-recovery``
(GRUB, initramfs, pacman, chroot), accessible depuis le live (entrée de boot), le menu GRUB du système installé
(``fws-recovery.target``) et son menu d'applications (élévation polkit).
- **Swap zram** sur le live et le système installé (``zram-generator``).
- **firewalld** activé par défaut sur le système installé.
- Bureaux Hyprland et i3 à parité visuelle (waybar/polybar, wofi/rofi, Catppuccin Mocha), thème SDDM « fws »,
bannière GDM.
Changed
-------
- **Branding FWS complet** : l'installateur s'annonce « FWS 0.6 » (variables ``ANACONDA_PRODUCT*``), splash FWS au menu
de boot BIOS (l'image Arch d'archiso est remplacée), ``/etc/issue`` sans version de noyau, filtres d'affichage
Fedora/Arch sur le splash et le journal de récupération.
- Toutes les entrées de boot plafonnent les workers udev bloqués (``udev.event_timeout=45``) ; le menu BIOS démarre
explicitement l'entrée FWS (``DEFAULT fws``).
- Use xz compression for the baseline profile's bootstrap tarball so that it has more differences when compared to the
releng profile.
- Match network interfaces by their type, instead of name, in systemd-networkd configuration.
@@ -21,12 +41,21 @@ Deprecated
Fixed
-----
- Watchdog du splash qui affichait son diagnostic PAR-DESSUS l'installateur (il se désarme désormais dès que la GUI
d'anaconda est à l'écran, et mesure l'inactivité en itérations — insensible aux sauts d'horloge des VM).
- Gel de l'installateur sur machines à faible RAM (1 Go) : zram + avertissement préalable.
- Profils ``baseline``/``releng`` synchronisés (la fusion baseline-écrase-releng avait fait perdre ``firewalld``,
le cap udev et les entrées de boot FWS d'ISO précédentes).
- DNS du système installé (NetworkManager + systemd-resolved), hook ``lvm2`` dans l'initramfs, GRUB de secours
``--removable`` en UEFI.
- Use ``systemd-run`` to invoke ``curl`` with a transient service that depends on ``network-online.target`` since no
services are currently pulling in ``network_online.target``.
Removed
-------
- ``configs/baseline/syslinux/syslinux-linux.cfg`` (label ``arch`` stock, orphelin et sans cap udev).
[87] - 2025-10-29
=================
+7 -3
View File
@@ -85,8 +85,12 @@ projet (« inbound = outbound », `GPL-3.0-or-later`), avec attestation d'origin
plus « Arch Linux ») ; `motd` ne renvoie plus vers `archlinux.org`
- [ ] **Dépôt rendu publiquement accessible** (sinon l'offre écrite n'est pas
opposable) — action hors-code
- [ ] Branding Red Hat/Fedora retiré de l'UI Anaconda ; installateur marqué
« modifié » — à faire pendant la migration (config `product.d`)
- [x] Branding Red Hat/Fedora retiré de l'UI Anaconda : nom de produit **FWS**
via les variables `ANACONDA_PRODUCTNAME`/`ANACONDA_PRODUCTVERSION`
(exportées par le `.xinitrc` du live, lues depuis l'os-release FWS —
l'en-tête affiche « FWS 0.6 INSTALLATION ») ; visuel **Arch** du menu de
boot BIOS remplacé par un splash FWS ; le caractère « version modifiée
d'Anaconda » reste documenté ici et dans TRADEMARKS.md
- [ ] Décision tranchée sur `python-iso639` (AGPL-3.0-only) : garder ou remplacer
- [ ] Revue par un conseil FOSS (recommandé avant diffusion commerciale)
@@ -96,4 +100,4 @@ projet (« inbound = outbound », `GPL-3.0-or-later`), avec attestation d'origin
> dépôt** (construits depuis l'AUR) : à corriger à la source AUR, pas ici.
> **Ceci n'est pas un avis juridique.** Base de conformité de bonne foi au
> 2026-06-17.
> 2026-07-07.
+75 -20
View File
@@ -1,6 +1,6 @@
# FWS
**FWS** est une distribution Linux live/installable basée sur **Arch Linux**, livrée sous forme d'ISO bootable BIOS/UEFI. L'installateur graphique est **en cours de migration de Calamares vers Anaconda** (Calamares retiré au commit 05ae9c3, intégration d'Anaconda en cours — voir le chantier dédié).
**FWS** est une distribution Linux live/installable basée sur **Arch Linux**, pensée pour **remplacer Windows** : bureau prêt à l'emploi, jeux (Steam/Proton-GE), réalité virtuelle (SteamVR), compatibilité applicative (winboat/Wine) et outillage de maintenance intégré. L'installateur graphique est **Anaconda** (celui de Fedora/RHEL), **porté sur Arch** : payload LiveOS (copie du live sur le disque), spokes FWS (choix du bureau, récapitulatif avant installation), finalisation pacman/GRUB/mkinitcpio en `%post`.
Le projet est conçu pour être **construit depuis Windows via WSL** (les scripts détectent Git Bash et basculent automatiquement dans WSL), mais fonctionne aussi directement depuis une **Arch Linux native** ou depuis **toute distribution non-Arch (Fedora, Ubuntu…) via une bascule automatique dans un conteneur Arch podman**.
@@ -9,14 +9,40 @@ Le projet est conçu pour être **construit depuis Windows via WSL** (les script
## Aperçu
- **Base** : Arch Linux (rolling release)
- **Installateur** : en migration Calamares → Anaconda (Calamares retiré, Anaconda en préparation ; payload `liveimg` pour installer FWS et non Fedora)
- **Affichage live** : Xorg + Openbox (session graphique conservée pour l'installateur)
- **Bureaux installables** : XFCE, KDE Plasma, GNOME, Hyprland (choix à l'installation)
- **Boot** : BIOS (syslinux) + UEFI (systemd-boot)
- **Installateur** : **Anaconda 45 porté sur Arch** — payload LiveOS (installe FWS, pas Fedora), page « Récapitulatif avant installation » avec bouton Précédent, choix du bureau intégré, **brandé FWS** (en-tête « FWS 0.6 INSTALLATION », via les variables `ANACONDA_PRODUCT*` exportées par le `.xinitrc`)
- **Affichage live** : Xorg + Openbox + splash de préparation qui montre l'activité réelle d'anaconda (dernier log actif + chrono ; si rien ne bouge avant l'apparition de l'installateur, il le dit puis affiche un diagnostic complet — le watchdog se désarme dès que la GUI est à l'écran)
- **Mémoire** : **4 Go recommandés** pour installer (2 Go minimum — swap **zram** intégré au live ET au système installé ; sous 2 Go le live avertit, à 1 Go sans zram l'installateur graphique gèle)
- **Bureaux installables** : GNOME, KDE Plasma, XFCE, **Hyprland** (Wayland — non compatible VR), **i3** (X11 — compatible VR/SteamVR), ou ligne de commande seule
- **Boot** : UEFI (systemd-boot) + BIOS (syslinux, splash **FWS**, `DEFAULT fws`) — entrées *normale*, *safe graphics (nomodeset)*, *Réparer FWS (recovery)* et *speech*
- **Récupération** : accessible depuis le live (entrée de boot), depuis le **menu GRUB du système installé** (« Réparer FWS ») et depuis son **menu d'applications** (« Récupération FWS », élévation polkit)
- **Compression** : squashfs (xz)
---
## Ce qu'il y a dans FWS
### À l'installation
- **Choix du bureau** dans Anaconda, avec compatibilité VR affichée (i3 = SteamVR ✔ ; Hyprland = ✘)
- **Page récapitulative** avant toute écriture disque : config choisie (langue, clavier, fuseau, comptes, partitionnement, bureau, nom de machine) + matériel détecté (GPU, disques, virtualisation, réseau)
- **Automatique en fin d'install** : drivers GPU (NVIDIA/AMD/Intel), outils invité VM (VMware/VirtualBox/QEMU/Hyper-V), PipeWire + pavucontrol, Bluetooth (BlueZ + blueman), luminosité (brightnessctl), **pare-feu firewalld activé**, Steam + Wine + Monado + MangoHud + gamemode, clavier/locale, hostname, sudo pour l'utilisateur créé, branding FWS
### Sur le système installé (outillage `fws-*`)
| Commande | Rôle | Accès |
|---|---|---|
| `fws-hello` | Bienvenue au 1er démarrage (mise à jour, codecs, Proton-GE, VR, raccourcis) | auto au 1er login |
| `fws-update` | Mise à jour complète (pacman + cache + orphelins + flatpak) | bouton 󰚰 de la barre |
| `fws-keys` | Panneau des **raccourcis clavier réels** (lit la config Hyprland/i3) | `Super+K`, bouton ⌨ |
| `fws-powermenu` | Verrouiller / déconnexion / suspendre / redémarrer / éteindre | `Super+X`, bouton 󰐥 |
| `fws-about` | « À propos » : le projet FWS + infos système | menu d'applications |
| `fws-vr-setup` | Drivers/runtimes VR (Vive/Index → SteamVR natif ; Quest → ALVR ; Monado) | `Super+Shift+V` (i3), menu |
| `fws-proton-ge` | Installe/actualise Proton-GE pour Steam (vérif sha512) | fws-hello, terminal |
| `fws-recovery` | **Application GTK de réparation** : GRUB, initramfs, pacman, terminal chroot (moteur bash + repli console) | menu « Récupération FWS » de l'OS installé, entrée GRUB « Réparer FWS », entrée de boot du live |
### Hyprland & i3 : parité visuelle
Même identité Catppuccin Mocha (nuances de bleu) sur les deux : barre flottante arrondie (**waybar** / **polybar**) avec date+heure, indicateur de mises à jour, volume/réseau/CPU/RAM/luminosité/Bluetooth cliquables ; menus assortis (**wofi** / **rofi**) ; mêmes raccourcis (déclarés avec descriptions → `Super+K`) ; **Dolphin** en gestionnaire de fichiers ; thème **SDDM « fws »** maison (pur QtQuick, accent adapté au bureau ; GNOME garde GDM avec bannière FWS).
---
## Environnement requis — INSTALLATION ULTRA PRÉCISE
### Côté Windows (machine hôte)
@@ -146,9 +172,9 @@ Détails utiles :
Depuis **Git Bash sur Windows** (à la racine du repo) :
```bash
# Étape 1 : compilation des paquets AUR (winboat) + deps locales
# Étape 1 : compilation des paquets AUR (winboat) + PKGBUILD locaux
# (anaconda porté sur Arch + ses dépendances python)
# → crée local-repo/ avec les .pkg.tar.zst
# (Calamares retiré ; l'installateur Anaconda est un chantier en cours)
./setup-aur.sh
# Étape 2 : build de l'ISO via mkarchiso
@@ -170,11 +196,11 @@ L'ISO finale est générée dans `./out/FWS-YYYY.MM.DD-x86_64.iso`.
4. **`pacman -Syu`** complet (obligatoire — sinon `makepkg` plante sur les "conflicting files" lors des splits de paquets type `gcc-libs`).
5. **Install des outils de build** : `base-devel git`.
6. **Création d'un utilisateur `fwsbuild`** non-root avec `sudo` NOPASSWD (makepkg refuse de tourner en root).
7. **Clone + build AUR** : `winboat-bin` via `makepkg -s --noconfirm`, plus les PKGBUILD locaux de `pkgbuilds/` (deps Anaconda). Calamares n'est plus compilé.
7. **Clone + build AUR** : `winboat-bin` via `makepkg -s --noconfirm`, plus les PKGBUILD locaux de `pkgbuilds/` (anaconda, anaconda-widgets, pykickstart, blivet, python-meh patché…). Le build est **incrémental** : un paquet déjà présent dans `local-repo/` à la bonne version est sauté.
8. **Création du repo local** : `repo-add local-repo/fws-local.db.tar.gz`.
9. **Cleanup** : suppression de l'utilisateur `fwsbuild`.
**Durée** : quelques minutes (winboat-bin + deps locales ; Calamares et ses ~12 dépendances Qt6 ne sont plus compilés).
**Durée** : quelques minutes en incrémental ; le premier build complet (anaconda + deps) est plus long.
---
@@ -220,11 +246,12 @@ L'ISO finale est générée dans `./out/FWS-YYYY.MM.DD-x86_64.iso`.
## Séquence de boot de l'ISO
1. Démarrage de l'ISO FWS (BIOS ou UEFI)
1. Menu de boot : **installation** (défaut), **safe graphics** (`nomodeset`), **Réparer FWS** (`fws.recovery=1` → app GTK `fws-recovery-gui` via openbox, repli console `fws-recovery`), **speech**. Toutes les entrées plafonnent les workers udev bloqués (`udev.event_timeout=45`)
2. `systemd` → autologin root sur `tty1`
3. `.zprofile``startx`
4. `.xinitrc` → Openbox (session graphique du live)
5. **[Chantier C]** lancement de l'installateur Anaconda — pas encore câblé (Calamares retiré au commit 05ae9c3)
3. `.zprofile` mode recovery si demandé, sinon `startx`
4. `.xinitrc` → Openbox + **garde-fou RAM** (avertissement sous 2 Go) + **splash « Préparation de l'installateur »** (label alimenté en direct par les logs anaconda ; à 4 min sans activité → diagnostic auto `/tmp/fws-stall.txt` ; le tout se désarme dès que la GUI d'anaconda apparaît), bind mount `/run/rootfsbase` (source du payload LiveOS), patch du hub Anaconda, **exports `ANACONDA_PRODUCT*`** (branding FWS), puis `dbus-run-session liveinst`
5. **Anaconda** : accueil → hub (langue, clavier, disque, utilisateur, **bureau FWS**) → « Commencer l'installation » → **page Récapitulatif** (bouton Précédent / Lancer l'installation) → installation → écran de réussite → extinction
6. En cas d'échec : écran de debug avec tous les logs (liveinst, %post chrooté, anaconda)
---
@@ -233,8 +260,18 @@ L'ISO finale est générée dans `./out/FWS-YYYY.MM.DD-x86_64.iso`.
- **Paquets du live** : `configs/releng/packages.x86_64`
- **Paquets baseline** : `configs/baseline/packages.x86_64`
- **Overlay rootfs** : `configs/releng/airootfs/` ou `configs/baseline/airootfs/`
- **Kickstart de l'installateur** : `configs/releng/airootfs/usr/share/anaconda/interactive-defaults.ks` (payload liveimg + %post)
- **Branding ISO** : `configs/releng/profiledef.sh` (`iso_name`, `iso_label`, `iso_publisher`)
- **Kickstart de l'installateur** : `configs/releng/airootfs/usr/share/anaconda/interactive-defaults.ks` (payload LiveOS via `--liveinst` + tout le `%post` : bureaux, drivers, pare-feu, branding)
- **Spokes Anaconda FWS** : `configs/baseline/airootfs/usr/lib/python3.14/site-packages/pyanaconda/ui/gui/spokes/` (`fws_desktop.py`, `fws_recap.py`) + glades dans `usr/share/anaconda/ui/spokes/`
- **Configs bureaux & thèmes** : `configs/releng/airootfs/usr/local/share/fws/` (hyprland, i3, waybar, polybar, wofi, rofi, picom) et `usr/share/sddm/themes/fws/`
- **Outils fws-\*** : `configs/releng/airootfs/usr/local/bin/`
- **Récupération sur l'OS installé** : entrée GRUB générée par `configs/releng/airootfs/etc/grub.d/42_fws-recovery``fws-recovery.target` + `fws-recovery-boot.service` (`etc/systemd/system/`) ; lanceur `usr/local/share/applications/fws-recovery.desktop` + politique polkit `usr/share/polkit-1/actions/org.fws.recovery.policy`
- **Swap zram** (live + installé) : `configs/releng/airootfs/etc/systemd/zram-generator.conf` (paquet `zram-generator`)
- **Branding ISO** : `configs/releng/profiledef.sh` (`iso_name`, `iso_label`, `iso_publisher`) ; **visuel du menu BIOS** : `configs/releng/syslinux/splash.png` (PNG 8-bit) ; **nom/version affichés par l'installateur** : exports `ANACONDA_PRODUCTNAME`/`ANACONDA_PRODUCTVERSION` dans le `.xinitrc` (lus depuis l'os-release du live)
> ⚠ **Règle d'or de la fusion** : au build, `configs/baseline/*` est copié **par-dessus** `configs/releng/*` — tout fichier homonyme de baseline **écrase** celui de releng. Ça concerne notamment `profiledef.sh`, `packages.x86_64`, `pacman.conf`, `efiboot/loader/entries/01-archiso-linux.conf` et `syslinux/syslinux.cfg` : **toute modification d'un de ces fichiers doit être reportée dans les deux profils** (des divergences ont déjà fait perdre silencieusement `firewalld` et le cap `udev.event_timeout` d'une ISO ; un `syslinux-linux.cfg` stock orphelin côté baseline définissait aussi un label `arch` sans le cap — supprimé, et le menu BIOS pointe désormais explicitement sur `DEFAULT fws`). Vérifier avec :
> ```bash
> comm -12 <(cd configs/baseline && find . -type f | sort) <(cd configs/releng && find . -type f | sort)
> ```
---
@@ -247,7 +284,20 @@ L'ISO finale est générée dans `./out/FWS-YYYY.MM.DD-x86_64.iso`.
| `pacman: signature unknown trust` | Keyring périmé → `pacman -Sy --noconfirm archlinux-keyring` puis relancer. |
| `makepkg: conflicting files` | `pacman -Syu` incomplet avant le build. Relancer `setup-aur.sh`. |
| `mkarchiso` plante sur `[fws-local]` | Le repo local n'a pas été généré. Relancer `setup-aur.sh`. |
| Écran noir après login Hyprland | Tu testes dans VirtualBox → pas d'accélération GPU. Utiliser QEMU/KVM avec `virtio-gpu` ou bare-metal. |
| Écran bleu 1-2 min au boot du live | Normal en VM : un worker udev traîne (plafonné à 45 s) — le splash « Préparation de l'installateur » l'indique. Attendre. |
| L'installateur semble figé / fenêtre blanche | Rendu logiciel en VM : la fenêtre Anaconda se peint lentement. Patienter quelques secondes. |
| Session Hyprland/i3 : fenêtres au contenu noir en VM | Compositing sur GPU virtuel — picom est auto-désactivé en VM (config FWS) ; si tu l'as réactivé, `pkill picom`. |
| Hyprland crash au lancement en VM | Géré : le wrapper `fws-hyprland` force le rendu logiciel (`kms_swrast`/llvmpipe) sous hyperviseur. Sur vrai GPU, rendu matériel normal. |
| Steam plante à l'install de SteamVR | En VM : `steam -cef-disable-gpu -cef-disable-gpu-compositing` (et la VR ne peut PAS marcher en VM — GPU/USB inaccessibles). |
| Icône Bluetooth absente / blueman en erreur | Pas d'adaptateur Bluetooth (cas VM) : le module de barre se cache et l'applet ne se lance pas — comportement voulu. |
| Le système installé ne boote plus | Menu GRUB → « **Réparer FWS (récupération du système)** » (boote l'initramfs fallback + l'app de réparation). Si GRUB lui-même est mort : booter l'ISO → entrée « **Réparer FWS (recovery)** ». |
| Réparer depuis le système installé (il boote encore) | Menu d'applications → « **Récupération FWS** » (mot de passe admin demandé une fois — polkit). |
| Réinstallation par-dessus une install existante | Le kickstart nettoie tout (`zerombr` + `clearpart --all`), mais en cas de comportement étrange au 2ᵉ passage, effacer/recréer le disque virtuel. |
| Boot BIOS | Fonctionne (résolu) : syslinux est sous `/boot/syslinux` de l'ISO (pas `/syslinux`), El-Torito BIOS + UEFI présents ; install BIOS complète validée en VM. Le menu BIOS démarre l'entrée FWS par défaut (`DEFAULT fws`). |
| Splash « Préparation de l'installateur » figé | **RAM insuffisante** (reproduit : gel à 1 Go, OK à 2 Go, aucun swap sur le live avant zram). En VM : allouer **4 Go** (le wizard VMware « Other 64-bit » n'en met que 1 !). Le live avertit désormais sous 2 Go, embarque un swap zram, et le splash affiche l'activité réelle d'anaconda (+ diagnostic auto si les logs ne bougent plus). |
| Dialogue « préparation bloquée : diagnostic » PAR-DESSUS l'installateur | Bug corrigé (ISO ≤ 2026-07-07 01:04) : le watchdog du splash se déclenchait alors qu'anaconda était à l'écran (logs silencieux = normal quand il attend l'utilisateur). Il se désarme désormais dès l'apparition de la GUI ; ferme le dialogue et continue, ou rebuild. |
| « Possibly missing firmware for module … » pendant une réparation Recovery | Avertissements `mkinitcpio` **normaux** : firmware de matériel exotique (SCSI/fibre channel…) absent de `linux-firmware`. Sans effet, sauf si tu possèdes ce matériel précis. Le journal Recovery l'explique désormais. |
| Recovery : « tout est grisé » | Corrigé : seul le sélecteur était désactivé (une seule installation détectée) mais toute l'app semblait morte ; il reste actif désormais. Les boutons d'action, eux, ont toujours été fonctionnels. |
| Retours à la ligne CRLF | `build.sh` lance `dos2unix` automatiquement, mais si tu modifies pendant le build, relancer. |
### Logs utiles
@@ -256,11 +306,16 @@ L'ISO finale est générée dans `./out/FWS-YYYY.MM.DD-x86_64.iso`.
# Pendant le build
tail -f /tmp/fws-build/work/iso/arch/pkglist.x86_64.txt
# Dans l'ISO (autologin tty1)
journalctl -b
# Dans le LIVE (Ctrl+Alt+F2, login root) — pendant/après l'installation
journalctl -b # système
cat /tmp/fws-liveinst.log # sortie complète de liveinst/anaconda
cat /tmp/anaconda.log # log principal anaconda
cat /tmp/fws-post-chroot.log # %post chrooté = grub-install/mkinitcpio
# En cas d'échec, l'écran de debug du live affiche déjà tout ça (zenity).
# Hyprland post-install
cat ~/.local/share/hyprland/hyprland.log
# Sur le système installé
cat /var/log/fws-post.log # trace complète du %post d'installation
journalctl -b # premier démarrage
```
---
+26 -4
View File
@@ -47,12 +47,24 @@ Le système live héritait de l'`os-release` du paquet `filesystem` d'Arch et
donc sans conflit, et est retiré du système final par
`zzzz99-remove-custom-hooks-from-airootfs.hook`.
- **`motd`** : ne référence plus `archlinux.org` ; pointe vers le dépôt FWS.
- **Menu de boot BIOS (2026-07-07)** : l'image de fond `syslinux/splash.png`
héritée d'archiso affichait le **logo Arch Linux plein écran** — remplacée
par un visuel FWS original (générique, sans logo tiers).
- **Bannière de login (`/etc/issue`)** : `\S (\l)` — affiche le `PRETTY_NAME`
FWS, sans la version du noyau (dont le suffixe `-archN`).
- **Textes utilisateur** : `fws-about` dit « distribution Linux » ; le splash
de préparation et le journal de l'app Récupération remplacent à l'affichage
les mentions « Arch Linux »/« Fedora » issues des logs d'outils tiers (les
identifiants techniques réels — `archlinux-keyring`, miroirs — restent
intacts : usage nominatif fonctionnel).
> Note technique : `ID=fws` + `ID_LIKE=arch` préserve la détection « famille
> Arch » des outils tiers (et satisfait les checks `^ID(_LIKE)?=.*arch` de
> `build.sh`/`setup-aur.sh`, qui portent de toute façon sur l'hôte de build).
> Si la migration Anaconda révèle un composant testant `ID=arch` en dur, basculer
> transitoirement le hook sur `ID=arch` tout en gardant `NAME`/`PRETTY_NAME` FWS.
> Le suffixe `-archN` de `uname -r` vient du binaire du noyau Arch : usage
> nominatif inévitable sans recompiler le kernel.
---
@@ -60,7 +72,7 @@ Le système live héritait de l'`os-release` du paquet `filesystem` d'Arch et
> **Red Hat®** et **Fedora®** sont des marques déposées de **Red Hat, Inc.**
> L'installateur **Anaconda** est développé par Red Hat. FWS distribue une
> **version MODIFIÉE** d'Anaconda (portée sur Arch, payload `liveimg`), **non
> **version MODIFIÉE** d'Anaconda (portée sur Arch, payload LiveOS), **non
> officielle**, **non fournie ni approuvée** par Red Hat ou le projet Fedora.
> *« This product includes a modified version of Anaconda. Red Hat and Fedora
> are trademarks of Red Hat, Inc. This is an unofficial build, not provided or
@@ -74,9 +86,19 @@ Engagements FWS :
passer une version modifiée pour officielle) ;
- **recommandation béton :** retirer tout **logo / nom de produit Red Hat ou
Fedora** de l'interface de l'installateur si présent, et y afficher
« Installateur FWS (basé sur Anaconda) ». La configuration produit d'Anaconda
se règle via `product.d` / les données de branding — à auditer pendant la
migration.
« Installateur FWS (basé sur Anaconda) ».
### ✅ Remédiation appliquée (2026-07-07 — l'installateur s'annonce « FWS »)
Sans `/.buildstamp`, Anaconda s'annonçait « ANACONDA BLUESKY INSTALLATION » et
ses logs parlent de Fedora. `pyanaconda/core/product.py` donne priorité aux
variables d'environnement : le `.xinitrc` du live exporte
`ANACONDA_PRODUCTNAME`/`ANACONDA_PRODUCTVERSION` (depuis l'os-release FWS) et
`ANACONDA_ISFINAL=true` → l'en-tête et l'accueil affichent **« FWS 0.6 »**. Le
splash de préparation filtre en outre les mentions Fedora des lignes de log
affichées. Les mentions profondes dans les **logs techniques** (fichiers
`/tmp/*.log`, écrans de debug) sont conservées volontairement : identification
loyale du composant amont (version modifiée d'Anaconda), utile au diagnostic.
---
+1 -1
View File
@@ -1 +1 @@
0.5.3
0.6.0
+91 -30
View File
@@ -2,10 +2,13 @@
# ============================================================
# FWS — Build OFFICIEL versionné de l'ISO (archiso)
#
# Identique à build.sh dans sa structure, mais :
# - lit la version dans le fichier VERSION (format Major.Minor.Build)
# - nomme l'ISO FWS-vMAJOR-MINOR-BUILD-DATE-x86_64.iso
# - range l'ISO dans out/vMAJOR/vMAJOR.MINOR/
# IDENTIQUE à build.sh (même bascule conteneur/WSL, --network=host, fusion
# baseline, MÊME injection local-repo → donc même paquet anaconda avec le champ
# Hostname), avec EN PLUS uniquement le RENOMMAGE :
# - demande la version (major.minor.build) + le nom de variante, le fichier
# VERSION servant de défaut,
# - nomme l'ISO fws-<version>-<nom>.iso (ex. fws-1.5.0-cli.iso),
# - range l'ISO dans out/vMAJOR/vMAJOR.MINOR/.
#
# Fonctionne sur :
# - Windows 10 / 11 (Git Bash → bascule auto dans WSL Arch)
@@ -121,6 +124,37 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
exit 1
}
# La version + le nom sont DEMANDÉS sur l'hôte (avant le conteneur) et passés
# via -e, pour que le prompt s'affiche au bon endroit (terminal de l'hôte).
# (Définis FWS_ISO_VERSION / FWS_ISO_TAG ; lus plus bas, prompt sauté si déjà
# fournis — c.-à-d. dans le conteneur.)
ask_iso_meta_host() {
[ -n "$FWS_ISO_VERSION" ] && [ -n "$FWS_ISO_TAG" ] && return 0
local def=""
if [ -f "$(dirname "${BASH_SOURCE[0]}")/VERSION" ]; then
def="$(tr -d ' \t\r\n' < "$(dirname "${BASH_SOURCE[0]}")/VERSION")"
[[ "$def" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || def=""
fi
while :; do
if [ -n "$def" ]; then
read -rp "==> Version (major.minor.build) [Entrée = $def] : " FWS_ISO_VERSION || exit 1
[ -n "$FWS_ISO_VERSION" ] || FWS_ISO_VERSION="$def"
else
read -rp "==> Version (major.minor.build, ex. 1.5.0) : " FWS_ISO_VERSION || exit 1
fi
[[ "$FWS_ISO_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && break
echo -e "\e[31m Format attendu : major.minor.build (ex. 1.5.0)\e[0m" >&2
done
while :; do
read -rp "==> Nom de la variante (ex. cli, gnome, kde) : " FWS_ISO_TAG || exit 1
FWS_ISO_TAG="$(printf '%s' "$FWS_ISO_TAG" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9-')"
[ -n "$FWS_ISO_TAG" ] && break
echo -e "\e[31m Nom requis (lettres, chiffres, tirets)\e[0m" >&2
done
export FWS_ISO_VERSION FWS_ISO_TAG
}
ask_iso_meta_host
# Rootful : préfixe sudo si on n'est pas déjà root.
SUDO=(); [ "$EUID" -eq 0 ] || SUDO=(sudo)
# -it seulement si on est dans un terminal (sinon podman casse en CI).
@@ -138,8 +172,14 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
"${SUDO[@]}" podman pull "$IMAGE"
echo "==> Relance dans le conteneur ($SELF)..."
"${SUDO[@]}" podman run --rm "${TTY[@]}" --privileged \
# --network=host : le réseau bridge de podman ne route plus vers l'extérieur
# sur ce wifi (firewalld/NAT cassé après changement de réseau → « core.db :
# Resolving timed out », même avec --dns). En partageant la pile réseau de
# l'hôte (qui, lui, a internet + DNS via 127.0.0.53/systemd-resolved joignable
# dans son propre netns), le conteneur récupère une connectivité identique.
"${SUDO[@]}" podman run --rm "${TTY[@]}" --network=host --privileged \
-e FWS_IN_CONTAINER=1 \
-e FWS_ISO_VERSION -e FWS_ISO_TAG \
-v "$REPO_DIR":"$REPO_DIR" \
-v fws-pacman-cache:/var/cache/pacman/pkg \
-w "$REPO_DIR" \
@@ -168,7 +208,8 @@ command -v pacman >/dev/null \
# $0 est cité pour gérer les chemins à espaces (ex. "1. FWS").
if [ "$EUID" -ne 0 ]; then
if command -v sudo >/dev/null 2>&1; then
exec sudo bash "$0" "$@"
# -E : conserve FWS_ISO_VERSION / FWS_ISO_TAG (demandés avant) à travers sudo.
exec sudo -E bash "$0" "$@"
else
exec su -c "bash \"$0\"" root
fi
@@ -188,29 +229,48 @@ WORK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "==> Dossier de travail : $WORK_DIR"
# ============================================================
# Lecture et validation de la version (fichier VERSION)
# Version (major.minor.build) + nom de la variante.
# Déjà demandés sur l'hôte (FWS_ISO_VERSION/FWS_ISO_TAG passés via -e) ; sinon
# (exécution native Arch / WSL directe) on les demande ici. Le fichier VERSION
# sert de défaut (Entrée = le reprendre). Produit fws-<version>-<nom>.iso.
# ============================================================
VERSION_FILE="$WORK_DIR/VERSION"
[ -f "$VERSION_FILE" ] \
|| { echo -e "\e[31mFichier VERSION introuvable : $VERSION_FILE\e[0m"; exit 1; }
# Trim espaces / tabulations / CR / LF
VERSION="$(tr -d ' \t\r\n' < "$VERSION_FILE")"
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo -e "\e[31mVERSION invalide : '$VERSION' (attendu Major.Minor.Build, ex. 1.2.3)\e[0m"
exit 1
VERSION="$FWS_ISO_VERSION"
ISO_TAG="$FWS_ISO_TAG"
VERSION_DEFAULT=""
if [ -f "$WORK_DIR/VERSION" ]; then
VERSION_DEFAULT="$(tr -d ' \t\r\n' < "$WORK_DIR/VERSION")"
[[ "$VERSION_DEFAULT" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || VERSION_DEFAULT=""
fi
while [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; do
if [ -n "$VERSION_DEFAULT" ]; then
read -rp "==> Version (major.minor.build) [Entrée = $VERSION_DEFAULT] : " VERSION \
|| { echo -e "\e[31mEntrée requise — lance le script en interactif.\e[0m" >&2; exit 1; }
[ -n "$VERSION" ] || VERSION="$VERSION_DEFAULT"
else
read -rp "==> Version (major.minor.build, ex. 1.5.0) : " VERSION \
|| { echo -e "\e[31mEntrée requise — lance le script en interactif.\e[0m" >&2; exit 1; }
fi
[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] \
|| echo -e "\e[31m Format attendu : major.minor.build (ex. 1.5.0)\e[0m" >&2
done
while [ -z "$ISO_TAG" ]; do
read -rp "==> Nom de la variante (ex. cli, gnome, kde) : " ISO_TAG \
|| { echo -e "\e[31mEntrée requise — lance le script en interactif.\e[0m" >&2; exit 1; }
ISO_TAG="$(printf '%s' "$ISO_TAG" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9-')"
[ -n "$ISO_TAG" ] || echo -e "\e[31m Nom requis (lettres, chiffres, tirets)\e[0m" >&2
done
IFS='.' read -r MAJOR MINOR BUILD <<< "$VERSION"
BUILD_DATE="$(date +%Y.%m.%d)"
ISO_VERSION="v${MAJOR}-${MINOR}-${BUILD}-${BUILD_DATE}"
# iso_version interne (passé à mkarchiso) ; l'ISO finale est renommée plus bas.
ISO_VERSION="${VERSION}-${ISO_TAG}"
OUT_DIR="$WORK_DIR/out/v${MAJOR}/v${MAJOR}.${MINOR}"
ISO_FILE="$OUT_DIR/FWS-${ISO_VERSION}-x86_64.iso"
# Date ajoutée À LA FIN du nom, avant .iso → fws-<version>-<nom>-<AAAA.MM.JJ>.iso
ISO_FILE="$OUT_DIR/fws-${VERSION}-${ISO_TAG}-${BUILD_DATE}.iso"
echo "==> Version officielle : ${MAJOR}.${MINOR}.${BUILD}"
echo "==> Dossier de sortie : $OUT_DIR"
echo "==> ISO finale : FWS-${ISO_VERSION}-x86_64.iso"
echo "==> Version : ${VERSION} · variante : ${ISO_TAG} · date : ${BUILD_DATE}"
echo "==> ISO finale : fws-${VERSION}-${ISO_TAG}-${BUILD_DATE}.iso"
echo "==> Dossier : $OUT_DIR"
# pacman ≥ 7 sandboxe les ops FS via landlock, non supporté par le kernel
# WSL ni (selon le noyau) par le conteneur → désactivation. Idempotent,
@@ -249,8 +309,8 @@ if [ -d "$WORK_DIR/configs/baseline" ]; then
fi
# ── Injection de la version dans profiledef.sh (copie temporaire) ──
# mkarchiso nomme l'ISO ${iso_name}-${iso_version}-${arch}.iso.
# On force iso_version pour obtenir vMAJOR-MINOR-BUILD-DATE.
# mkarchiso nomme l'ISO ${iso_name}-${iso_version}-${arch}.iso ; on force
# iso_version pour obtenir le nom voulu (l'ISO est renommée fws-… plus bas).
if [ -f /tmp/fws-build/releng/profiledef.sh ]; then
sed -i "s|^iso_version=.*|iso_version=\"${ISO_VERSION}\"|" \
/tmp/fws-build/releng/profiledef.sh
@@ -295,11 +355,12 @@ find /tmp/fws-build/releng -type f -exec dos2unix {} + 2>/dev/null
echo "==> Lancement de mkarchiso..."
mkdir -p "$OUT_DIR"
# mkarchiso supprime l'ISO cible avant de la réécrire : si elle est VERROUILLÉE
# par Windows (VM ouverte dessus, ISO montée), ce rm casse tout le build à la
# fin. Parade : staging neuf puis déplacement ; si la cible (cette même
# version) est verrouillée, on bascule sur un nom horodaté. On ne touche
# jamais aux ISO des autres versions.
# mkarchiso fait un « rm » interne de l'ISO cible avant de la ()écrire ; si
# une ISO du même nom est VERROUILLÉE par Windows (VM encore ouverte dessus,
# ISO montée, antivirus), ce rm échoue et tout le build casse à la toute fin.
# Parade : mkarchiso écrit dans un dossier de staging neuf, puis on déplace
# l'ISO. Si la cible (cette même version) est verrouillée, on bascule sur un
# nom horodaté → on ne touche jamais aux ISO des autres versions.
STAGE="$OUT_DIR/.staging-$$"
rm -rf "$STAGE"; mkdir -p "$STAGE"
mkarchiso -v -w /tmp/fws-build/work -o "$STAGE" /tmp/fws-build/releng
@@ -319,4 +380,4 @@ echo "==> Nettoyage..."
rm -rf /tmp/fws-build/work
echo "==> Build officiel terminé ! ISO : $FINAL"
echo -e "\e[32mScript de build officiel (v${MAJOR}.${MINOR}.${BUILD}) terminé avec succès.\e[0m"
echo -e "\e[32mScript de build officiel (fws-${VERSION}-${ISO_TAG}-${BUILD_DATE}) terminé avec succès.\e[0m"
+6 -1
View File
@@ -133,7 +133,12 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
"${SUDO[@]}" podman pull "$IMAGE"
echo "==> Relance dans le conteneur ($SELF)..."
"${SUDO[@]}" podman run --rm "${TTY[@]}" --privileged \
# --network=host : le réseau bridge de podman ne route plus vers l'extérieur
# sur ce wifi (firewalld/NAT cassé après changement de réseau → « core.db :
# Resolving timed out », même avec --dns). En partageant la pile réseau de
# l'hôte (qui, lui, a internet + DNS via 127.0.0.53/systemd-resolved joignable
# dans son propre netns), le conteneur récupère une connectivité identique.
"${SUDO[@]}" podman run --rm "${TTY[@]}" --network=host --privileged \
-e FWS_IN_CONTAINER=1 \
-v "$REPO_DIR":"$REPO_DIR" \
-v fws-pacman-cache:/var/cache/pacman/pkg \
+139
View File
@@ -20,10 +20,146 @@ _OB_PID=$!
# boucle (symptôme « fond bleu qui clignote/reboote »). On NE rend jamais la main
# à startx : on reste sur Openbox pour lire les logs / rebooter proprement.
if command -v liveinst >/dev/null 2>&1; then
# Payload LiveOS : avec --liveinst (passé par liveinst), Anaconda IGNORE le
# payload du kickstart (anaconda.py teste opts.liveinst AVANT le type du
# payload DBus — un « liveimg » dans le ks serait du code mort) et détecte
# l'image live sur des chemins Fedora/dracut : /dev/mapper/live-base,
# /dev/mapper/live-osimg-min ou /run/rootfsbase — tous ABSENTS d'archiso →
# « SourceSetupError: No Live OS image found! » = crash au démarrage
# (reproduit hors VM dans un chroot de l'airootfs). On expose donc le
# squashfs archiso (monté ro sur /run/archiso/airootfs) là où Anaconda le
# cherche : findmnt y résout le loop device → montage → rsync vers la cible.
if [ -d /run/archiso/airootfs ] && [ ! -e /run/rootfsbase ]; then
mkdir -p /run/rootfsbase
mount --bind /run/archiso/airootfs /run/rootfsbase || rmdir /run/rootfsbase
fi
# GARDE-FOU RAM : le live n'a que le zram comme marge — en dessous de
# ~2 Go, Anaconda (GUI Python + squashfs) peut geler la machine en plein
# « Préparation de l'installateur ». On prévient AVANT plutôt que de
# laisser l'utilisateur croire à un bug.
_MEMKB=$(awk '/^MemTotal:/{print $2}' /proc/meminfo 2>/dev/null)
if [ -n "$_MEMKB" ] && [ "$_MEMKB" -lt 2000000 ] && command -v zenity >/dev/null 2>&1; then
zenity --warning --width=460 --title="FWS — mémoire insuffisante ?" \
--text="<b>Cette machine n'a que $(( _MEMKB / 1024 )) Mo de RAM.</b>\n\nL'installateur graphique peut geler en dessous de 2 Go.\nEn VM : augmente la mémoire (4 Go recommandés) puis redémarre.\n\nOK pour tenter quand même." 2>/dev/null
fi
# ÉCRAN D'ATTENTE immédiat : le démarrage d'anaconda peut prendre 2 à
# 4 minutes (observé en VM VMware : un worker udev se bloque au boot et
# n'est tué par udevd qu'après 45 s — udev.event_timeout). Sans feedback,
# l'utilisateur croit à une panne alors que TOUT va bien. Le splash montre
# EN DIRECT ce que fait anaconda : un nourrisseur écrit sur stdin de
# zenity --progress (« # texte » = mise à jour du libellé) le log anaconda
# le plus récemment modifié + son dernier message + un chrono. Si les logs
# ne bougent plus (vrai blocage), le libellé le DIT (au lieu d'une barre
# qui pulse pour rien) et, au-delà de 4 min d'inactivité, on collecte
# journal+dmesg dans /tmp/fws-stall.txt et on l'affiche : l'écran devient
# lui-même le rapport de bug. Le nourrisseur meurt par SIGPIPE quand le
# splash est tué à la sortie de liveinst.
_SPLASH_PID=""
if command -v zenity >/dev/null 2>&1; then
(
_el=0; _quiet=0; _prevstamp=""
while :; do
sleep 3
# GUI d'anaconda à l'écran (marqueur ui.gui) → le splash n'a
# plus de raison d'être et le watchdog DOIT se désarmer : des
# logs silencieux pendant que l'utilisateur remplit les écrans
# sont NORMAUX (bug vécu : le rapport « préparation bloquée »
# s'affichait PAR-DESSUS l'installateur au bout de 4 min).
if grep -q 'ui\.gui' /tmp/anaconda.log 2>/dev/null; then
pkill -f 'zenity --progress' 2>/dev/null
exit 0
fi
# Compteurs en itérations (pas d'heure murale : l'horloge du
# guest SAUTE en VM — resynchro RTC — et gonflerait les âges,
# vu en vrai : « 242 s d'inactivité » 90 s après le boot).
_el=$(( _el + 3 ))
_last=$(ls -t /tmp/storage.log /tmp/anaconda.log /tmp/program.log /tmp/dbus.log 2>/dev/null | head -n1)
if [ -z "$_last" ]; then
printf '# Démarrage d'"'"'Anaconda… (%d min %02d s)\n' $(( _el / 60 )) $(( _el % 60 )) || exit 0
continue
fi
_stamp=$(stat -c %Y "$_last" 2>/dev/null)
if [ "$_stamp" = "$_prevstamp" ]; then
_quiet=$(( _quiet + 3 ))
else
_quiet=0; _prevstamp="$_stamp"
fi
# Rebranding d'affichage : anaconda (installateur Fedora) parle
# de Fedora/Arch dans ses logs — on ne montre pas ça à l'écran.
_msg=$(tail -n1 "$_last" 2>/dev/null | tr -d '\r' \
| sed -E 's/Fedora( Linux)?/FWS/g; s/Arch Linux/FWS/g; s/\<Arch\>/FWS/g' \
| cut -c1-90)
if [ "$_quiet" -ge 240 ] && [ ! -f /tmp/fws-stall.txt ]; then
{
echo "=== FWS : préparation bloquée (aucune activité log depuis ${_quiet}s) ==="
echo "--- dernier log actif : $_last"; tail -n 25 "$_last" 2>/dev/null
echo "--- udev (workers bloqués ?)"; udevadm settle --timeout=0 2>&1
echo "--- journal (fin)"; journalctl -b --no-pager 2>/dev/null | tail -n 60
echo "--- dmesg (fin)"; dmesg 2>/dev/null | tail -n 40
} > /tmp/fws-stall.txt 2>&1
zenity --text-info --title="FWS — préparation bloquée : diagnostic" \
--filename=/tmp/fws-stall.txt --width=1000 --height=650 2>/dev/null &
fi
if [ "$_quiet" -ge 60 ]; then
printf '# ⚠ Aucune activité depuis %d s (%s) — dernier message : %s\n' "$_quiet" "$(basename "$_last")" "$_msg" || exit 0
else
printf '# Préparation (%d min %02d s) — %s : %s\n' $(( _el / 60 )) $(( _el % 60 )) "$(basename "$_last")" "$_msg" || exit 0
fi
done
) | zenity --progress --pulsate --no-cancel \
--title="FWS" \
--text="<b>Préparation de l'installateur FWS…</b>\n\nAnalyse du matériel et des disques en cours.\nEncore une à deux minutes, c'est normal." \
--width=560 2>/dev/null &
_SPLASH_PID=$!
fi
# Hub Résumé SANS le libellé « We won't touch your disks… » : notre glade
# modifié NE PEUT PAS être livré directement à son chemin final —
# mkarchiso pose l'overlay airootfs AVANT pacstrap, et pacman refuse
# alors d'installer anaconda par-dessus (« exists in filesystem »). On le
# livre donc sous /usr/local/share/fws et on ÉCRASE ici, au boot du live
# (l'overlay du live est inscriptible), le fichier du paquet.
if [ -f /usr/local/share/fws/anaconda-summary-hub.glade ]; then
cp -f /usr/local/share/fws/anaconda-summary-hub.glade \
/usr/share/anaconda/ui/hubs/summary.glade 2>/dev/null || true
fi
# BRANDING : sans /.buildstamp, anaconda s'appelle « anaconda bluesky »
# (défauts de pyanaconda/core/product.py) et ses logs parlent de Fedora.
# Les variables d'environnement ANACONDA_* ont priorité → l'en-tête
# devient « FWS 1.0 INSTALLATION » et le log « anaconda … for FWS started ».
# (Les mentions Fedora PROFONDES dans les logs — GeoIP, profils — restent :
# ce sont des chaînes internes de l'installateur, sans effet sur l'install.)
export ANACONDA_PRODUCTNAME="$(. /etc/os-release 2>/dev/null; echo "${NAME:-FWS}")"
export ANACONDA_PRODUCTVERSION="$(. /etc/os-release 2>/dev/null; echo "${VERSION_ID:-0.6}")"
export ANACONDA_ISFINAL=true
LIVEINST_LOG=/tmp/fws-liveinst.log
# Bus D-Bus de SESSION : au démarrage (--liveinst), inhibit_screensaver()
# de pyanaconda se connecte au bus de session. S'il n'y en a AUCUN (notre
# startx root minimal n'en garantit pas), Gio lève une GLib.GError que
# pyanaconda n'attrape PAS (il n'attrape que DBusError) → crash immédiat
# (2e crash reproduit hors VM). dbus-run-session fournit un bus de session
# pour toute la durée de l'installateur ; avec un bus présent, l'absence du
# service ScreenSaver est attrapée (simple warning dans anaconda.log).
if command -v dbus-run-session >/dev/null 2>&1 && [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
dbus-run-session -- liveinst >"$LIVEINST_LOG" 2>&1
else
liveinst >"$LIVEINST_LOG" 2>&1
fi
rc=$?
# liveinst terminé (install finie ou échec) : le splash n'a plus de raison
# d'être — kill de zenity ; le nourrisseur meurt de SIGPIPE à son
# prochain printf. Idem pour un éventuel dialogue de diagnostic de stall.
if [ -n "$_SPLASH_PID" ]; then
pkill -P "$_SPLASH_PID" 2>/dev/null
kill "$_SPLASH_PID" 2>/dev/null
fi
pkill -f "fws-stall.txt" 2>/dev/null
# ATTENTION : liveinst NE propage PAS le code de sortie d'anaconda — il
# enchaîne son propre nettoyage et sort souvent 0 même quand anaconda a
# planté. On ne se fie donc PAS à $rc : en phase debug on regroupe TOUS les
@@ -66,6 +202,9 @@ if command -v liveinst >/dev/null 2>&1; then
echo
echo "########## %post --nochroot (/tmp/fws-post-nochroot.log) ##########"
cat /tmp/fws-post-nochroot.log 2>/dev/null || echo "(absent)"
echo
echo "########## Récapitulatif (page GUI fws_recap, avant install) ##########"
cat /tmp/fws-hw-summary.txt 2>/dev/null || echo "(absent — page récap pas atteinte)"
for f in /tmp/anaconda.log /tmp/syslog /tmp/program.log \
/tmp/storage.log /tmp/dbus.log /tmp/packaging.log /tmp/X.log; do
echo
+14 -1
View File
@@ -1,5 +1,18 @@
# Démarrage automatique de Xorg uniquement dans le live FWS.
# Démarrage automatique dans le live FWS uniquement.
# Sur le système installé, /run/archiso n'existe pas, donc rien ne se lance.
# Mode RÉPARATION (entrée de boot « Réparer FWS », paramètre fws.recovery=1) :
# on démarre X + l'application GTK (fws-recovery-session lance openbox puis
# fws-recovery-gui, qui pilote le moteur fws-recovery). Si X échoue à
# démarrer, startx rend la main → repli sur l'assistant console fws-recovery.
# (Sur le SYSTÈME INSTALLÉ, l'équivalent est fws-recovery-boot.service via
# l'entrée GRUB « Réparer FWS » — pas ce fichier.)
if [[ -d /run/archiso && "$(tty)" == "/dev/tty1" ]] \
&& grep -qw 'fws.recovery=1' /proc/cmdline 2>/dev/null; then
startx /usr/local/bin/fws-recovery-session -- vt1
exec fws-recovery
fi
if [[ -d /run/archiso && "$(tty)" == "/dev/tty1" && -z "$DISPLAY" ]]; then
exec startx -- vt1
fi
@@ -0,0 +1,106 @@
# FWS — Spoke « Desktop Environment » : nouvel onglet de choix du bureau.
#
# POURQUOI ICI (airootfs, pas dans le paquet anaconda) : Anaconda découvre les
# spokes au RUNTIME via collect()/collect_spokes (os.listdir du dossier des
# spokes puis import de chaque .py). Déposer ce fichier dans le dossier des
# spokes via l'overlay airootfs suffit donc à le faire apparaître — SANS rebuild
# du paquet anaconda (juste ./build.sh). Le glade associé est dans
# /usr/share/anaconda/ui/spokes/fws_desktop.glade (aussi via l'airootfs).
#
# ⚠ Le chemin /usr/lib/python3.14/... est couplé à la version de Python d'Arch
# (3.14 au moment du build d'anaconda). À mettre à jour si Python change de
# version majeure (en même temps que le rebuild d'anaconda de toute façon).
#
# FONCTION : afficher 5 choix (CLI/GNOME/KDE/Hyprland/XFCE) et écrire la clé
# choisie dans /tmp/fws-desktop. Le %post du kickstart lit ce fichier, fait
# « pacman -S » du bureau + d'un navigateur (Firefox), et active le display
# manager. « cli » (défaut) = aucun bureau, rien à installer.
from pyanaconda.core.i18n import CN_, _
from pyanaconda.ui.categories.software import SoftwareCategory
from pyanaconda.ui.gui.spokes import NormalSpoke
__all__ = ["FWSDesktopSpoke"]
# clé interne (écrite dans /tmp/fws-desktop) → libellé affiché dans le statut.
_CHOICES = [
("cli", _("Command line only (no desktop)")),
("gnome", "GNOME"),
("kde", "KDE Plasma"),
("hyprland", "Hyprland — Wayland (non compatible VR)"),
("i3", "i3 — X11 (compatible VR : SteamVR)"),
("xfce", "XFCE"),
]
_DESKTOP_FILE = "/tmp/fws-desktop"
class FWSDesktopSpoke(NormalSpoke):
"""Choix de l'environnement de bureau FWS (écrit /tmp/fws-desktop)."""
builderObjects = ["fwsDesktopWindow"]
mainWidgetName = "fwsDesktopWindow"
uiFile = "spokes/fws_desktop.glade"
category = SoftwareCategory
icon = "preferences-desktop-display-symbolic"
title = CN_("GUI|Spoke", "_Desktop Environment")
@staticmethod
def get_screen_id():
"""Identifiant unique de cet écran."""
return "fws-desktop-selection"
def __init__(self, *args):
NormalSpoke.__init__(self, *args)
self._choice = "cli"
self._radios = {}
def initialize(self):
NormalSpoke.initialize(self)
for key, _label in _CHOICES:
self._radios[key] = self.builder.get_object("radio_" + key)
# Reprend un éventuel choix déjà écrit (revisite du spoke).
try:
with open(_DESKTOP_FILE) as fh:
val = fh.read().strip()
if val in self._radios:
self._choice = val
except OSError:
pass
def refresh(self):
# Coche le bouton correspondant au choix courant.
rb = self._radios.get(self._choice) or self._radios.get("cli")
if rb is not None:
rb.set_active(True)
def apply(self):
# Relit le bouton actif et persiste le choix pour le %post du kickstart.
for key, rb in self._radios.items():
if rb is not None and rb.get_active():
self._choice = key
break
try:
with open(_DESKTOP_FILE, "w") as fh:
fh.write(self._choice)
except OSError:
pass
@property
def status(self):
for key, label in _CHOICES:
if key == self._choice:
return label
return _("Command line only (no desktop)")
@property
def completed(self):
# « cli » est un défaut valide → le spoke n'est jamais bloquant.
return True
@property
def mandatory(self):
return False
@property
def ready(self):
return True
@@ -0,0 +1,348 @@
# FWS — Page « Récapitulatif » : écran À PART ENTIÈRE avant l'installation.
#
# POURQUOI ICI (airootfs, pas dans le paquet anaconda) : même mécanisme que la
# spoke « Desktop Environment » (fws_desktop.py) — Anaconda découvre les spokes
# au RUNTIME (collect) dans ce dossier → l'overlay airootfs suffit, AUCUN
# rebuild du paquet anaconda. Le glade associé est dans
# /usr/share/anaconda/ui/spokes/fws_recap.glade (aussi via l'airootfs).
#
# PLACEMENT DANS LE FLUX : standalone spoke « postForHub = SummaryHub » avec
# priority = -10. L'ordre des écrans est pre(SummaryHub) → SummaryHub →
# post(SummaryHub) trié par priority croissante (UserInterface.
# _orderActionClasses). ProgressSpoke — l'écran qui DÉMARRE l'installation
# dans son refresh() — est aussi post(SummaryHub), priority 0 par défaut →
# cette page s'intercale ENTRE les deux : l'utilisateur clique « Commencer
# l'installation » sur le hub, voit CE récapitulatif (le disque n'a encore
# subi AUCUNE écriture), et l'installation ne démarre qu'à son clic
# « Lancer l'installation ». Le bouton « Précédent » ramène au hub pour
# modifier les choix (cf. _on_back_clicked : la file d'actions du GUI est
# consommée à chaque écran, on y réinsère le hub).
#
# CONTENU : configuration choisie par l'utilisateur (via les modules D-Bus
# d'Anaconda : langue, clavier, fuseau, comptes, partitionnement + les
# fichiers /tmp des spokes FWS : bureau, hostname) ET matériel détecté
# (virtualisation, GPU, disques, réseau) — mêmes sources que ce que le
# %post du kickstart et fws-setup-hardware appliqueront.
#
# ⚠ « completed » doit rester False : _instantiateAction SAUTE les standalone
# spokes complétées (mécanisme prévu pour les installs kickstart).
# ⚠ Chemin couplé à la version Python d'Arch (3.14) — comme fws_desktop.py —
# et le bouton « Précédent » s'appuie sur les internes du GUI d'anaconda
# 45.8 (_actions/_currentAction) : à re-vérifier à chaque bump d'anaconda.
import inspect
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from pyanaconda.anaconda_loggers import get_module_logger
from pyanaconda.core import util
from pyanaconda.modules.common.constants.objects import DISK_SELECTION
from pyanaconda.modules.common.constants.services import (
LOCALIZATION,
STORAGE,
TIMEZONE,
USERS,
)
from pyanaconda.modules.common.structures.user import UserData
from pyanaconda.ui.gui import GraphicalUserInterface
from pyanaconda.ui.gui.hubs.summary import SummaryHub
from pyanaconda.ui.gui.spokes import StandaloneSpoke
log = get_module_logger(__name__)
__all__ = ["FWSRecapSpoke"]
# Doit rester aligné sur le %post du kickstart (étape « Bureau choisi ») et
# sur les libellés de la spoke fws_desktop.
_DESKTOP_LABELS = {
"cli": "ligne de commande uniquement (aucun bureau)",
"gnome": "GNOME (+ Firefox, GDM)",
"kde": "KDE Plasma (+ Firefox, SDDM)",
"hyprland": "Hyprland — Wayland, non compatible VR (+ Firefox, SDDM)",
"i3": "i3 — X11, compatible VR/SteamVR (+ Firefox, SDDM)",
"xfce": "XFCE (+ Firefox, SDDM)",
}
_PARTITIONING_LABELS = {
"AUTOMATIC": "automatique (recommandé)",
"INTERACTIVE": "personnalisé (assistant)",
"CUSTOM": "personnalisé",
"BLIVET": "avancé (blivet-gui)",
"MANUAL": "points de montage manuels",
}
def _safe(getter, default):
"""Valeur d'un getter D-Bus/système, ou défaut : le récap ne doit JAMAIS
faire tomber l'installateur, quel que soit le module indisponible (d'
le except large : dasbus lève des types variés selon la panne)."""
try:
return getter()
except Exception: # pylint: disable=broad-except
return default
def _capture(*argv):
"""stdout d'une commande, chaîne vide si absente ou en échec."""
try:
return util.execWithCapture(argv[0], list(argv[1:])) or ""
except (OSError, RuntimeError):
return ""
def _read_flag(path):
try:
with open(path) as fh:
return fh.read().strip()
except OSError:
return ""
def _config_lines():
"""Section « configuration choisie » : interroge les modules D-Bus
d'Anaconda (remplis par les spokes) + les fichiers /tmp des spokes FWS."""
lines = []
lang = _safe(lambda: LOCALIZATION.get_proxy().Language, "")
if lang:
lines.append(f" • Langue : {lang}")
layouts = _safe(lambda: list(LOCALIZATION.get_proxy().XLayouts), [])
if layouts:
lines.append(f" • Clavier (X11) : {', '.join(layouts)}")
tz = _safe(lambda: TIMEZONE.get_proxy().Timezone, "")
if tz:
ntp = _safe(lambda: TIMEZONE.get_proxy().NTPEnabled, None)
suffix = {True: " — synchronisation NTP activée",
False: " — synchronisation NTP désactivée"}.get(ntp, "")
lines.append(f" • Fuseau horaire : {tz}{suffix}")
users = _safe(
lambda: UserData.from_structure_list(USERS.get_proxy().Users), [])
if users:
names = ", ".join(u.name for u in users if u.name)
lines.append(f" • Utilisateur(s) : {names} (sudo via wheel)")
else:
lines.append(" • Utilisateur(s) : AUCUN — pensez-y avant de lancer !")
root_locked = _safe(lambda: USERS.get_proxy().IsRootAccountLocked, None)
root_pw = _safe(lambda: USERS.get_proxy().IsRootPasswordSet, None)
if root_locked:
root_txt = "verrouillé (recommandé)"
elif root_pw:
root_txt = "actif, mot de passe défini"
elif root_pw is not None:
# Même règle que l'étape « Sécurité » du %post : un root resté sans
# mot de passe sera verrouillé en fin d'installation.
root_txt = "sans mot de passe → sera VERROUILLÉ en fin d'install"
else:
root_txt = ""
if root_txt:
lines.append(f" • Compte root : {root_txt}")
# Hostname : écrit par le spoke Utilisateur (patch user.py du paquet
# anaconda). Même nettoyage / même défaut que le %post --nochroot.
hn = "".join(c for c in _read_flag("/tmp/fws-hostname")
if c.isalnum() or c in "._-") or "fws"
lines.append(f" • Nom de machine : {hn}")
# Bureau : écrit par fws_desktop.apply() (sortie de la spoke). Absent =
# spoke jamais visitée = « cli », exactement le défaut du %post.
desk = _read_flag("/tmp/fws-desktop") or "cli"
lines.append(f" • Bureau : {_DESKTOP_LABELS.get(desk, desk)}")
method = _safe(
lambda: STORAGE.get_proxy(
STORAGE.get_proxy().AppliedPartitioning).PartitioningMethod, "")
if method:
lines.append(" • Partitionnement : "
f"{_PARTITIONING_LABELS.get(method, method)}")
return lines, desk, method
def _build_summary():
config_lines, desk, method = _config_lines()
config_text = "\n".join(config_lines) \
or " (configuration non disponible)"
# Disques ciblés par l'installation (spoke « Destination »).
selected = set(_safe(
lambda: list(STORAGE.get_proxy(DISK_SELECTION).SelectedDisks), []))
disks = []
for line in _capture("lsblk", "-dno", "NAME,SIZE,MODEL,TYPE").splitlines():
fields = line.split()
if fields and fields[-1] == "disk":
mark = " ← CIBLE de l'installation" if fields[0] in selected else ""
disks.append("" + " ".join(fields[:-1]) + mark)
disk_text = "\n".join(disks) or " (aucun disque détecté)"
if method == "AUTOMATIC":
disk_warn = (
" ⚠ Rien n'a encore été écrit. Le partitionnement automatique\n"
" EFFACERA ENTIÈREMENT le(s) disque(s) cible(s) au lancement.")
else:
disk_warn = (
" ⚠ Rien n'a encore été écrit. Au lancement, ce sont vos choix\n"
" de partitionnement faits dans l'installateur qui s'appliqueront.")
virt = (_capture("systemd-detect-virt").strip().splitlines() or ["none"])[0]
if virt in ("", "none"):
virt = "aucune (machine physique)"
gpus = []
for line in _capture("lspci").splitlines():
low = line.lower()
if ("vga" in low or "3d" in low or "display" in low) and ":" in line:
gpus.append("" + line.split(":", 2)[-1].strip())
gpu_text = "\n".join(gpus) or " (aucune carte graphique détectée)"
try:
net_ok = util.execWithRedirect("ip", ["route", "get", "1.1.1.1"]) == 0
except (OSError, RuntimeError):
net_ok = False
if net_ok:
net = "disponible ✔"
else:
net = ("INDISPONIBLE ✖ — bureau, drivers et stack FWS ne\n"
" s'installeront PAS (rattrapable au 1er boot)")
extras = ("Steam, Wine, VR (Monado/OpenXR), winboat-bin"
if desk != "cli" else "rien de plus (choix CLI)")
return f"""\
══════════════════════════════════════════════════════════
FWS — RÉCAPITULATIF AVANT INSTALLATION
══════════════════════════════════════════════════════════
CONFIGURATION CHOISIE
{config_text}
DISQUES
{disk_warn}
{disk_text}
MATÉRIEL DÉTECTÉ
• Virtualisation : {virt}
• Réseau : {net}
Cartes graphiques (drivers installés automatiquement en fin d'install) :
{gpu_text}
DÉROULEMENT DE L'INSTALLATION
1. Partitionnement, puis copie du système live FWS (payload LiveOS)
2. Noyau + initramfs régénérés (mkinitcpio), GRUB installé
3. Bureau choisi + navigateur installés (réseau requis)
4. Matériel : drivers GPU, outils VM, son, Bluetooth, Podman
+ si bureau choisi : {extras}
« Précédent » pour revenir modifier vos choix —
« Lancer l'installation » pour démarrer réellement l'installation.
"""
class FWSRecapSpoke(StandaloneSpoke):
"""Récapitulatif des choix, affiché entre le hub et l'installation."""
builderObjects = ["fwsRecapWindow"]
mainWidgetName = "fwsRecapWindow"
uiFile = "spokes/fws_recap.glade"
postForHub = SummaryHub
priority = -10
@staticmethod
def get_screen_id():
"""Identifiant unique de cet écran."""
return "fws-recap"
@property
def completed(self):
# False, sinon _instantiateAction saute l'écran (cf. en-tête).
return False
def apply(self):
# Rien à persister : cette page ne fait que montrer l'état.
pass
def initialize(self):
StandaloneSpoke.initialize(self)
# Le bouton « continuer » de CETTE page est le vrai déclencheur :
# l'écran suivant (ProgressSpoke) démarre la file d'installation.
self.window.get_continue_button().set_label("_Lancer l'installation")
# Capture pour « Précédent » : initialize() est appelé DEPUIS
# GraphicalUserInterface._on_continue_clicked (clic « Commencer
# l'installation » du hub), AVANT la bascule de _currentAction → à
# cet instant ui._currentAction est ENCORE l'instance du hub Résumé,
# et le « self » de l'appelant est l'UI. Aucune API publique ne les
# expose ; on remonte la pile d'appels (déterministe — pas de scan
# mémoire, pas de variable globale).
self._gui = None
self._hub = None
frame = inspect.currentframe()
try:
frame = frame.f_back
while frame is not None:
candidate = frame.f_locals.get("self")
if isinstance(candidate, GraphicalUserInterface):
self._gui = candidate
current = getattr(candidate, "_currentAction", None)
if isinstance(current, SummaryHub):
self._hub = current
break
frame = frame.f_back
finally:
del frame
# Bouton « Précédent », placé avec les boutons de navigation
# (à côté de « Quitter » — leur parent est la GtkButtonBox du bas).
quit_button = self.window.get_quit_button()
button_box = quit_button.get_parent()
self._back_button = Gtk.Button.new_with_mnemonic("_Précédent")
self._back_button.connect("clicked", self._on_back_clicked)
button_box.add(self._back_button)
button_box.reorder_child(self._back_button, 0)
if self._gui is None or self._hub is None:
# Références introuvables (flux inattendu) : bouton grisé plutôt
# qu'une navigation cassée — l'install reste possible.
log.warning("FWS recap: hub/UI introuvables, « Précédent » désactivé.")
self._back_button.set_sensitive(False)
self._back_button.show()
def _on_back_clicked(self, _button):
ui = self._gui
hub = self._hub
if ui is None or hub is None:
return
log.info("FWS recap: retour au hub Résumé pour modification.")
# La file d'actions du GUI est CONSOMMÉE (pop(0)) à chaque écran :
# ici _actions = [FWSRecapSpoke, ProgressSpoke] et le hub en est déjà
# sorti. On le réinsère en tête pour que le prochain « Commencer
# l'installation » ré-enchaîne hub → NOUVELLE page récap (résumé
# recalculé avec les choix modifiés) → installation.
if not ui._actions or ui._actions[0] is not SummaryHub:
ui._actions.insert(0, SummaryHub)
# _on_continue_clicked a désactivé le « continuer » du hub en le
# quittant : on le réarme, sinon « Commencer l'installation » est mort.
hub.window.set_may_continue(True)
# Même séquence que la transition officielle (_on_continue_clicked).
self.exited.emit(self)
hub.refresh()
ui.mainWindow.setCurrentAction(hub)
hub.entered.emit(hub)
ui._currentAction = hub
def refresh(self):
StandaloneSpoke.refresh(self)
text = _build_summary()
self.builder.get_object("recapView").get_buffer().set_text(text)
# Copie pour le harnais de debug du .xinitrc (fws-debug.txt).
try:
with open("/tmp/fws-hw-summary.txt", "w") as fh:
fh.write(text)
except OSError:
pass
@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
<!-- FWS : copie de summary.glade d'anaconda 45.8 SANS le libellé
« We won't touch your disks… » du bas du hub. Livré ICI (chemin neutre)
car mkarchiso pose l'airootfs AVANT pacstrap : au chemin du paquet,
pacman refuserait d'installer anaconda (« exists in filesystem »).
C'est le .xinitrc du live qui le copie sur
/usr/share/anaconda/ui/hubs/summary.glade au boot. -->
<requires lib="gtk+" version="3.0"/>
<requires lib="AnacondaWidgets" version="3.0"/>
<object class="AnacondaHubWindow" id="summaryWindow">
<property name="can_focus">False</property>
<property name="window_name" translatable="yes">INSTALLATION SUMMARY</property>
<property name="quit_button">quitButton</property>
<property name="continue_button">continueButton</property>
<child internal-child="main_box">
<object class="GtkBox" id="AnacondaHubWindow-main_box1">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child internal-child="nav_box">
<object class="GtkEventBox" id="AnacondaHubWindow-nav_box1">
<property name="can_focus">False</property>
<child internal-child="nav_area">
<object class="GtkGrid" id="AnacondaHubWindow-nav_area1">
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child internal-child="alignment">
<object class="GtkAlignment" id="AnacondaHubWindow-alignment1">
<property name="can_focus">False</property>
<property name="yalign">0</property>
<property name="left_padding">12</property>
<property name="right_padding">6</property>
<child internal-child="action_area">
<object class="GtkBox" id="summaryWindow-actionArea">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child internal-child="scrolled_window">
<object class="GtkScrolledWindow" id="summaryWindow-scroll">
<property name="can_focus">True</property>
<property name="is_focus">True</property>
<property name="hscrollbar_policy">never</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="buttonbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">6</property>
<property name="margin_right">6</property>
<property name="spacing">12</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="quitButton">
<property name="label" translatable="yes" context="GUI|Summary">_Quit</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="hexpand">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="continueButton">
<property name="label" translatable="yes" context="GUI|Summary">_Begin Installation</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<style>
<class name="suggested-action"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<child internal-child="accessible">
<object class="AtkObject" id="summaryWindow-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">INSTALLATION SUMMARY</property>
</object>
</child>
</object>
</interface>
@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- FWS — onglet « Desktop Environment » (choix du bureau à installer). -->
<interface>
<requires lib="gtk+" version="3.6"/>
<requires lib="AnacondaWidgets" version="1.0"/>
<object class="AnacondaSpokeWindow" id="fwsDesktopWindow">
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="window-name" translatable="yes">DESKTOP ENVIRONMENT</property>
<signal name="button-clicked" handler="on_back_clicked" swapped="no"/>
<child internal-child="main_box">
<object class="GtkBox" id="AnacondaSpokeWindow-main_box1">
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child internal-child="nav_box">
<object class="GtkEventBox" id="AnacondaSpokeWindow-nav_box1">
<property name="can-focus">False</property>
<child internal-child="nav_area">
<object class="GtkGrid" id="AnacondaSpokeWindow-nav_area1">
<property name="can-focus">False</property>
<property name="margin-left">6</property>
<property name="margin-right">6</property>
<property name="margin-top">6</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child internal-child="alignment">
<object class="GtkAlignment" id="AnacondaSpokeWindow-alignment1">
<property name="can-focus">False</property>
<property name="margin-top">12</property>
<property name="yalign">0</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="bottom-padding">48</property>
<property name="left-padding">24</property>
<property name="right-padding">24</property>
<child internal-child="action_area">
<object class="GtkBox" id="AnacondaSpokeWindow-action_area1">
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">8</property>
<child>
<object class="GtkLabel" id="fws_desktop_desc">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="xalign">0</property>
<property name="margin-bottom">12</property>
<property name="wrap">True</property>
<property name="max-width-chars">60</property>
<property name="label" translatable="yes">Choose the desktop environment to install. A web browser (Firefox) is installed automatically with any desktop.</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="radio_cli">
<property name="label" translatable="yes">Command line only (no desktop)</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="active">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="radio_gnome">
<property name="label" translatable="yes">GNOME</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="draw-indicator">True</property>
<property name="group">radio_cli</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="radio_kde">
<property name="label" translatable="yes">KDE Plasma</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="draw-indicator">True</property>
<property name="group">radio_cli</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="radio_hyprland">
<property name="label" translatable="yes">Hyprland — Wayland (NOT VR compatible)</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="draw-indicator">True</property>
<property name="group">radio_cli</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="radio_i3">
<property name="label" translatable="yes">i3 — X11 (VR compatible: SteamVR, HTC Vive, Valve Index…)</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="draw-indicator">True</property>
<property name="group">radio_cli</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="radio_xfce">
<property name="label" translatable="yes">XFCE</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="draw-indicator">True</property>
<property name="group">radio_cli</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">6</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child internal-child="accessible">
<object class="AtkObject" id="fwsDesktopWindow-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">DESKTOP ENVIRONMENT</property>
</object>
</child>
</object>
</interface>
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- FWS — page « Récapitulatif avant installation » (cf. fws_recap.py).
Squelette calqué sur installation_progress.glade : AnacondaStandaloneWindow
avec ses enfants internes (nav_box/nav_area, alignment/action_area). -->
<interface>
<requires lib="gtk+" version="3.0"/>
<requires lib="AnacondaWidgets" version="3.0"/>
<object class="AnacondaStandaloneWindow" id="fwsRecapWindow">
<property name="can_focus">False</property>
<property name="window_name" translatable="yes">RÉCAPITULATIF AVANT INSTALLATION</property>
<child internal-child="main_box">
<object class="GtkBox" id="AnacondaStandaloneWindow-main_box1">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child internal-child="nav_box">
<object class="GtkEventBox" id="AnacondaStandaloneWindow-nav_box1">
<property name="can_focus">False</property>
<child internal-child="nav_area">
<object class="GtkGrid" id="AnacondaStandaloneWindow-nav_area1">
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child internal-child="alignment">
<object class="GtkAlignment" id="AnacondaStandaloneWindow-alignment1">
<property name="can_focus">False</property>
<property name="yalign">0</property>
<property name="left_padding">12</property>
<property name="right_padding">6</property>
<child internal-child="action_area">
<object class="GtkBox" id="fwsRecapWindow-actionArea">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkScrolledWindow" id="recapScroll">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTextView" id="recapView">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<property name="cursor_visible">False</property>
<property name="monospace">True</property>
<property name="left_margin">12</property>
<property name="right_margin">12</property>
<property name="top_margin">12</property>
<property name="bottom_margin">12</property>
<property name="wrap_mode">word-char</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
@@ -1,4 +1,5 @@
title Arch Linux (%ARCH%, UEFI)
title FWS install medium (%ARCH%, UEFI)
sort-key 01
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID%
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% udev.event_timeout=45
+4
View File
@@ -62,6 +62,7 @@ zsh
grml-zsh-config
bash
zenity
zram-generator
nano
vim
less
@@ -79,6 +80,7 @@ pacman-contrib
dmidecode
usbutils
pciutils
firewalld
fastfetch
htop
@@ -103,6 +105,8 @@ open-vm-tools
# --- FWS (fws-local) ---
winboat-bin
# Gestionnaire D&D 5e (dés, personnages, grimoire, sessions, combat).
fws-dnd
# --- Installateur Anaconda (fws-local) ---
# 'anaconda' tire automatiquement anaconda-widgets + toutes ses dépendances
-5
View File
@@ -1,8 +1,3 @@
[fws-local]
SigLevel = Optional TrustAll
Server = file:///mnt/c/Users/Murasaki/Documents/FWS/fws/local-repo
#
# /etc/pacman.conf
#
+15
View File
@@ -26,4 +26,19 @@ file_permissions=(
["/usr/local/bin/choose-mirror"]="0:0:755"
["/usr/local/bin/Installation_guide"]="0:0:755"
["/usr/local/bin/livecd-sound"]="0:0:755"
["/usr/bin/load_policy"]="0:0:755"
["/usr/bin/setstatus"]="0:0:755"
["/usr/local/bin/fws-setup-hardware"]="0:0:755"
["/usr/local/bin/fws-keys"]="0:0:755"
["/usr/local/bin/fws-vr-setup"]="0:0:755"
["/usr/local/bin/fws-hello"]="0:0:755"
["/usr/local/bin/fws-update"]="0:0:755"
["/usr/local/bin/fws-updates-check"]="0:0:755"
["/usr/local/bin/fws-proton-ge"]="0:0:755"
["/usr/local/bin/fws-recovery"]="0:0:755"
["/usr/local/bin/fws-recovery-session"]="0:0:755"
["/usr/local/bin/fws-recovery-gui"]="0:0:755"
["/usr/local/bin/fws-powermenu"]="0:0:755"
["/usr/local/bin/fws-about"]="0:0:755"
["/etc/grub.d/42_fws-recovery"]="0:0:755"
)
@@ -1,5 +0,0 @@
LABEL arch
MENU LABEL Arch Linux (%ARCH%, BIOS)
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID%
+9 -7
View File
@@ -1,9 +1,11 @@
SERIAL 0 115200
UI menu.c32
MENU TITLE Arch Linux
MENU CLEAR
DEFAULT select
DEFAULT arch
TIMEOUT 30
LABEL select
COM32 whichsys.c32
APPEND -pxe- pxe -sys- sys -iso- sys
INCLUDE syslinux-linux.cfg
LABEL pxe
CONFIG archiso_pxe.cfg
LABEL sys
CONFIG archiso_sys.cfg
+40
View File
@@ -0,0 +1,40 @@
#!/bin/sh
# ============================================================================
# FWS — entrée « Réparer FWS » dans le menu GRUB du SYSTÈME INSTALLÉ
# (l'équivalent de l'environnement de récupération de Windows).
#
# Ce script est exécuté par grub-mkconfig (à l'installation via le %post du
# kickstart, puis à chaque fws-recovery « Réinstaller GRUB »). Il démarre le
# MÊME système sur fws-recovery.target (+ marqueur fws.recovery=1) :
# fws-recovery-boot.service prend alors tty1 et lance l'application de
# réparation (GUI via startx, repli console).
#
# Sur le live, ce fichier est inerte (grub-mkconfig n'y tourne jamais).
# ============================================================================
set -e
. /usr/share/grub/grub-mkconfig_lib
kernel=/boot/vmlinuz-linux
# L'initramfs FALLBACK (sans autodetect) : c'est l'entrée de secours, elle
# doit démarrer même si le matériel a changé depuis le dernier mkinitcpio.
initrd=/boot/initramfs-linux-fallback.img
[ -f "$initrd" ] || initrd=/boot/initramfs-linux.img
[ -f "$kernel" ] && [ -f "$initrd" ] || exit 0
# Même logique racine que 10_linux : UUID sauf refus explicite.
if [ -n "${GRUB_DEVICE_UUID:-}" ] && [ "x${GRUB_DISABLE_LINUX_UUID:-}" != xtrue ]; then
rootarg="root=UUID=$GRUB_DEVICE_UUID"
else
rootarg="root=$GRUB_DEVICE"
fi
rel_kernel="$(make_system_path_relative_to_its_root "$kernel")"
rel_initrd="$(make_system_path_relative_to_its_root "$initrd")"
cat <<EOF
menuentry 'Réparer FWS (récupération du système)' --class recovery {
$(prepare_grub_to_access_device "${GRUB_DEVICE_BOOT}" | grub_add_tab)
linux $rel_kernel $rootarg rw ${GRUB_CMDLINE_LINUX:-} systemd.unit=fws-recovery.target fws.recovery=1
initrd $rel_initrd
}
EOF
+1 -1
View File
@@ -1 +1 @@
archiso
fws
+2
View File
@@ -0,0 +1,2 @@
\S (\l)
@@ -5,5 +5,14 @@
# On utilise donc un autre nom.
_fastfetch_rc=$?
if [ "${_fastfetch_rc}" -eq 0 ]; then
fastfetch
# Logo FWS maison (4 carrés + Tux) au lieu du logo Arch détecté par défaut.
fastfetch \
--logo-type file \
--logo /usr/local/share/fws/logo.txt \
--logo-color-1 '38;2;30;160;100' \
--logo-color-2 '38;2;45;125;210' \
--logo-color-3 '38;2;240;180;40' \
--logo-color-4 '38;2;25;85;145' \
--logo-color-5 '38;2;28;28;34' \
--logo-color-6 '38;2;245;165;35'
fi
@@ -1 +0,0 @@
/usr/lib/systemd/system/systemd-timesyncd.service
@@ -0,0 +1 @@
/usr/lib/systemd/system/systemd-timesyncd.service
@@ -0,0 +1,24 @@
[Unit]
Description=FWS - Reparation du systeme (session de secours sur tty1)
# Demarre UNIQUEMENT via fws-recovery.target (entree GRUB « Reparer FWS » du
# systeme installe : systemd.unit=fws-recovery.target). PAS de [Install] :
# enrole dans aucun boot normal, donc son Conflicts=getty@tty1 ne peut jamais
# casser le login tty1 d'un boot ordinaire.
# Sur le live, c'est /root/.zprofile (autologin tty1) qui gere la reparation.
ConditionKernelCommandLine=fws.recovery=1
ConditionPathExists=!/run/archiso
Conflicts=getty@tty1.service display-manager.service
After=systemd-user-sessions.service getty@tty1.service display-manager.service
[Service]
Type=idle
# GUI via startx (openbox + fws-recovery-gui) ; si X ne demarre pas (pilote
# video casse — un cas de reparation typique), repli sur l'assistant CONSOLE.
ExecStart=/bin/sh -c '/usr/bin/startx /usr/local/bin/fws-recovery-session -- vt1 || exec /usr/local/bin/fws-recovery'
StandardInput=tty
StandardOutput=tty
StandardError=tty
TTYPath=/dev/tty1
TTYReset=yes
TTYVHangup=yes
Restart=no
@@ -0,0 +1,10 @@
[Unit]
Description=FWS - Cible de reparation du systeme
# Demandee par l'entree GRUB « Reparer FWS » du systeme installe
# (kernel : systemd.unit=fws-recovery.target fws.recovery=1).
# Un multi-user complet (reseau pour pacman -Syu) + la session de reparation ;
# le Conflicts du service libere tty1 (son start est REQUIS ici, il gagne
# contre le Wants de getty@tty1).
Requires=multi-user.target fws-recovery-boot.service
After=multi-user.target fws-recovery-boot.service
AllowIsolate=yes
@@ -1 +0,0 @@
/usr/lib/systemd/system/systemd-time-wait-sync.service
@@ -0,0 +1 @@
/usr/lib/systemd/system/systemd-time-wait-sync.service
@@ -1 +0,0 @@
/usr/lib/systemd/system/systemd-timesyncd.service
@@ -0,0 +1 @@
/usr/lib/systemd/system/systemd-timesyncd.service
@@ -0,0 +1,12 @@
# FWS — swap compressé en RAM (zram), activé automatiquement par
# zram-generator (paquet dans packages.x86_64, aucun service à activer).
#
# Le LIVE n'a AUCUN swap : sur une machine/VM à 2 Go, Anaconda + le rootfs
# squashfs saturent la mémoire → livelock, écran figé pendant « Préparation
# de l'installateur ». Le zram (compression ~3:1 sur les tas Python) donne
# la marge nécessaire. Copié tel quel sur le SYSTÈME INSTALLÉ par le payload
# LiveOS → FWS garde ce filet de sécurité après installation (esprit
# « remplaçant de Windows » sur des machines modestes).
[zram0]
zram-size = min(ram, 4096)
compression-algorithm = zstd
@@ -0,0 +1,9 @@
[Desktop Entry]
Type=Application
Name=Bienvenue sur FWS
Comment=Fenêtre de bienvenue au premier démarrage
Exec=fws-hello --autostart
Icon=help-about
Terminal=false
Categories=System;
X-GNOME-Autostart-enabled=true
+185
View File
@@ -0,0 +1,185 @@
#!/usr/bin/env python3
# FWS — fenêtre « À propos » : deux onglets, « Le projet FWS » (ce qu'est FWS,
# version, liens) et « Système » (matériel + logiciel détectés). Remplace le
# « About Xfce » de libxfce4ui par une page de marque FWS. GTK3 (python-gobject
# installé avec le bureau).
import os
import re
import subprocess
import gi
gi.require_version("Gtk", "3.0")
gi.require_version("Gdk", "3.0") # sinon gi charge Gdk 4.0 (conflit avec Gtk3)
from gi.repository import Gdk, GLib, Gtk # noqa: E402
GLib.set_prgname("fws-about")
def _osrelease():
data = {}
for path in ("/etc/os-release", "/usr/lib/os-release"):
try:
with open(path) as fh:
for line in fh:
m = re.match(r'^([A-Z_]+)=(.*)$', line.strip())
if m:
data[m.group(1)] = m.group(2).strip('"')
break
except OSError:
continue
return data
def _cmd(*argv):
try:
return subprocess.check_output(argv, text=True,
stderr=subprocess.DEVNULL).strip()
except (OSError, subprocess.CalledProcessError):
return ""
def _cpu():
try:
with open("/proc/cpuinfo") as fh:
for line in fh:
if line.startswith("model name"):
return line.split(":", 1)[1].strip()
except OSError:
pass
return "inconnu"
def _mem():
try:
with open("/proc/meminfo") as fh:
for line in fh:
if line.startswith("MemTotal"):
kib = int(line.split()[1])
return f"{kib / 1048576:.1f} Gio"
except OSError:
pass
return "inconnu"
def _gpus():
out = []
for line in _cmd("lspci").splitlines():
low = line.lower()
if "vga" in low or "3d" in low or "display" in low:
out.append(line.split(":", 2)[-1].strip())
return out or ["inconnu"]
def _desktop():
if os.environ.get("HYPRLAND_INSTANCE_SIGNATURE"):
return "Hyprland"
env = os.environ.get("XDG_CURRENT_DESKTOP") \
or os.environ.get("DESKTOP_SESSION") or ""
return env or "inconnu"
def _session_type():
t = os.environ.get("XDG_SESSION_TYPE", "")
return {"wayland": "Wayland", "x11": "X11"}.get(t, t or "inconnu")
def _disks():
out = []
for line in _cmd("lsblk", "-dno", "NAME,SIZE,MODEL,TYPE").splitlines():
f = line.split()
if f and f[-1] == "disk":
out.append(" ".join(f[:-1]))
return out or ["inconnu"]
def _grid(rows):
grid = Gtk.Grid(column_spacing=18, row_spacing=8, margin=18)
for i, (key, val) in enumerate(rows):
k = Gtk.Label(xalign=1)
k.set_markup(f"<b>{GLib.markup_escape_text(key)}</b>")
k.get_style_context().add_class("dim-label")
v = Gtk.Label(label=val, xalign=0, selectable=True)
v.set_line_wrap(True)
grid.attach(k, 0, i, 1, 1)
grid.attach(v, 1, i, 1, 1)
return grid
def _project_tab(osr):
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10, margin=20)
title = Gtk.Label()
name = osr.get("PRETTY_NAME") or osr.get("NAME") or "FWS Linux"
title.set_markup(f"<span size='xx-large' weight='bold'>{name}</span>")
box.pack_start(title, False, False, 0)
tag = Gtk.Label(label="Une distribution Linux pensée pour remplacer "
"Windows : bureau prêt à l'emploi, jeux, VR et "
"compatibilité applicative.")
tag.set_line_wrap(True)
tag.set_max_width_chars(52)
box.pack_start(tag, False, False, 0)
feats = Gtk.Label()
feats.set_markup(
"<b>Inclus :</b> installateur graphique · plusieurs bureaux "
"(GNOME, KDE, Hyprland, i3, XFCE) · Steam &amp; Proton-GE · "
"réalité virtuelle (SteamVR) · winboat (applications Windows) · "
"pare-feu · réparation système intégrée.")
feats.set_line_wrap(True)
feats.set_max_width_chars(52)
feats.set_xalign(0)
box.pack_start(feats, False, False, 6)
home = osr.get("HOME_URL") or "https://github.com/You-re-like-Windows-a-bitch/fws"
link = Gtk.LinkButton.new_with_label(home, "Site du projet")
box.pack_start(link, False, False, 0)
return box
def _system_tab(osr):
rows = [
("Système", osr.get("PRETTY_NAME", "FWS Linux")),
("Build", osr.get("BUILD_ID", "rolling")),
("Noyau", _cmd("uname", "-r") or "inconnu"),
("Bureau", _desktop()),
("Affichage", _session_type()),
("Nom de machine", _cmd("hostname") or os.uname().nodename),
("Processeur", _cpu()),
("Mémoire", _mem()),
("Carte(s) graphique(s)", "\n".join(_gpus())),
("Disque(s)", "\n".join(_disks())),
]
scroll = Gtk.ScrolledWindow()
scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
scroll.add(_grid(rows))
return scroll
class AboutWindow(Gtk.Window):
def __init__(self):
super().__init__(title="À propos de FWS")
self.set_default_size(520, 460)
self.set_position(Gtk.WindowPosition.CENTER)
self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
osr = _osrelease()
notebook = Gtk.Notebook()
notebook.append_page(_project_tab(osr), Gtk.Label(label="Le projet FWS"))
notebook.append_page(_system_tab(osr), Gtk.Label(label="Système"))
self.add(notebook)
self.connect("destroy", Gtk.main_quit)
def main():
if not Gtk.init_check()[0]:
return 1
win = AboutWindow()
win.show_all()
Gtk.main()
return 0
if __name__ == "__main__":
raise SystemExit(main())
+158
View File
@@ -0,0 +1,158 @@
#!/usr/bin/env python3
# FWS — fenêtre de bienvenue au premier démarrage (pendant post-install de la
# page récap de l'installateur). Boutons : mise à jour, codecs, VR, raccourcis,
# Proton-GE. GTK3 : présent sur TOUS les bureaux FWS (Firefox le tire) ;
# python-gobject est installé avec le bureau par le kickstart.
#
# Lancement : /etc/xdg/autostart (KDE/GNOME/XFCE) et exec des configs
# Hyprland/i3, toujours avec --autostart : on se retire sans bruit si
# l'utilisateur a coché « ne plus afficher » (~/.config/fws/hello-done).
import os
import shutil
import subprocess
import sys
import gi
gi.require_version("Gtk", "3.0")
gi.require_version("Gdk", "3.0") # sinon gi charge Gdk 4.0 (conflit avec Gtk3)
from gi.repository import Gdk, GLib, Gtk # noqa: E402
# WM_CLASS déterministe : les règles i3/Hyprland « fenêtre flottante »
# ciblent cette classe.
GLib.set_prgname("fws-hello")
_FLAG = os.path.expanduser("~/.config/fws/hello-done")
_CODECS = ("gst-plugins-base gst-plugins-good gst-plugins-bad "
"gst-plugins-ugly gst-libav ffmpeg mpv")
# (libellé, description, commande, en_terminal)
_ACTIONS = [
("Mettre à jour le système",
"pacman -Syu + nettoyage du cache",
"fws-update", True),
("Installer les codecs multimédia",
"lecture vidéo/audio (GStreamer, ffmpeg, mpv)",
f"sudo pacman -S --needed {_CODECS}", True),
("Proton-GE pour Steam",
"meilleure compatibilité des jeux Windows",
"fws-proton-ge", True),
("Configurer la VR",
"drivers casque : HTC Vive, Index, Quest…",
"fws-vr-setup", True),
]
_TERMINALS = ["kitty", "konsole", "gnome-terminal", "xfce4-terminal", "xterm"]
def _terminal():
for term in _TERMINALS:
if shutil.which(term):
return term
return None
def _run(command, in_terminal):
if in_terminal:
term = _terminal()
if term is None:
return
if term == "gnome-terminal":
argv = [term, "--", "sh", "-c", command]
else:
argv = [term, "-e", "sh", "-c", command]
else:
argv = ["sh", "-c", command]
subprocess.Popen(argv) # noqa: S603 — commandes internes FWS
class HelloWindow(Gtk.Window):
def __init__(self):
super().__init__(title="Bienvenue sur FWS")
self.set_default_size(460, -1)
self.set_position(Gtk.WindowPosition.CENTER)
self.set_border_width(18)
# DIALOGUE : les tuileurs (i3/Hyprland) laissent les dialogues en
# flottant → la fenêtre garde sa taille naturelle au lieu d'être
# étirée sur tout l'espace de travail (layout illisible sinon).
self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
self.set_resizable(False)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
self.add(box)
title = Gtk.Label()
title.set_markup("<span size='x-large' weight='bold'>Bienvenue sur FWS !</span>")
box.pack_start(title, False, False, 0)
subtitle = Gtk.Label(
label="Ton système est prêt. Quelques réglages utiles pour bien démarrer :")
subtitle.set_line_wrap(True)
subtitle.set_xalign(0)
box.pack_start(subtitle, False, False, 0)
actions = list(_ACTIONS)
# Raccourcis clavier : uniquement sur les sessions qui ont fws-keys
# (Hyprland/i3) — les autres bureaux ont leurs propres réglages.
session = os.environ.get("XDG_CURRENT_DESKTOP", "").lower()
if os.environ.get("HYPRLAND_INSTANCE_SIGNATURE") or "i3" in session:
actions.append(("Voir les raccourcis clavier",
"aussi disponible à tout moment : Super+K",
"fws-keys", False))
for label, desc, command, in_term in actions:
button = Gtk.Button()
inner = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
lab = Gtk.Label()
lab.set_markup(f"<b>{label}</b>")
lab.set_xalign(0)
sub = Gtk.Label(label=desc)
sub.set_xalign(0)
sub.get_style_context().add_class("dim-label")
inner.pack_start(lab, False, False, 0)
inner.pack_start(sub, False, False, 0)
button.add(inner)
button.connect("clicked",
lambda _b, c=command, t=in_term: _run(c, t))
box.pack_start(button, False, False, 0)
self._again = Gtk.CheckButton(label="Afficher cette fenêtre au prochain démarrage")
self._again.set_active(False)
box.pack_start(self._again, False, False, 6)
close = Gtk.Button(label="Fermer")
close.connect("clicked", lambda _b: self.close())
box.pack_start(close, False, False, 0)
self.connect("destroy", self._on_destroy)
def _on_destroy(self, _win):
if not self._again.get_active():
os.makedirs(os.path.dirname(_FLAG), exist_ok=True)
with open(_FLAG, "w") as fh:
fh.write("ok\n")
else:
try:
os.unlink(_FLAG)
except OSError:
pass
Gtk.main_quit()
def main():
if "--autostart" in sys.argv and os.path.exists(_FLAG):
return 0
# Pas d'affichage joignable (lancement hors session graphique) : on se
# retire sans traceback plutôt que de crasher.
if not Gtk.init_check()[0]:
return 1
win = HelloWindow()
win.show_all()
Gtk.main()
return 0
if __name__ == "__main__":
sys.exit(main())
+129
View File
@@ -0,0 +1,129 @@
#!/usr/bin/env python3
# FWS — affiche les raccourcis clavier RÉELS de la session (bouton ⌨ de la
# barre ou Super+K). Universel :
# • Hyprland : « hyprctl binds -j » = les binds effectivement chargés
# (descriptions des bindd) — affichage wofi (Wayland).
# • i3 : parse ~/.config/i3/config (bindsym, variables résolues)
# — affichage rofi (X11).
# Aucune dépendance hors python3 + wofi/rofi (déjà installés avec le bureau).
import json
import os
import re
import subprocess
import sys
_WOFI_STYLE = "/usr/local/share/fws/wofi-keys.css"
# Bits de modmask Hyprland (masques X11) dans l'ordre d'affichage voulu.
_MODS = [(64, "SUPER"), (4, "CTRL"), (8, "ALT"), (1, "SHIFT")]
_KEY_LABELS = {
"mouse:272": "clic gauche",
"mouse:273": "clic droit",
"mouse:274": "clic molette",
"mouse_down": "molette bas",
"mouse_up": "molette haut",
"button4": "molette haut",
"button5": "molette bas",
"return": "Entrée",
"space": "Espace",
"escape": "Échap",
"tab": "Tab",
"left": "←",
"right": "→",
"up": "↑",
"down": "↓",
"print": "Impr écran",
}
def _label(key):
return _KEY_LABELS.get(key.lower(), key)
# --- Hyprland : binds réels via hyprctl ------------------------------------
def hyprland_lines():
raw = subprocess.check_output(["hyprctl", "binds", "-j"], text=True)
lines, seen = [], set()
for bind in json.loads(raw):
parts = [name for bit, name in _MODS if bind.get("modmask", 0) & bit]
key = bind.get("key") or (f"code {bind['keycode']}" if bind.get("keycode") else "")
parts.append(_label(key))
combo = " + ".join(p for p in parts if p)
if not combo or combo in seen:
continue
seen.add(combo)
desc = (bind.get("description") or "").strip() \
or f"{bind.get('dispatcher', '?')} {(bind.get('arg') or '').strip()}".strip()
lines.append(f"{combo:<24} {desc}")
return lines
# --- i3 : parse de la config (variables résolues) ----------------------------
def i3_lines():
path = os.path.expanduser("~/.config/i3/config")
variables = {}
lines, seen = [], set()
with open(path, encoding="utf-8", errors="replace") as fh:
content = fh.readlines()
for line in content:
m = re.match(r"\s*set\s+(\$\S+)\s+(.+)$", line)
if m:
variables[m.group(1)] = m.group(2).strip()
def resolve(text):
# $var les plus longs d'abord ($filemanager avant $file…).
for var in sorted(variables, key=len, reverse=True):
text = text.replace(var, variables[var])
return text
for line in content:
m = re.match(r"\s*bindsym\s+(?:--\S+\s+)*(\S+)\s+(.+)$", line)
if not m:
continue
combo_raw, action = m.groups()
combo_raw = resolve(combo_raw).replace("Mod4", "Super").replace("Mod1", "Alt")
combo = " + ".join(_label(k) for k in combo_raw.split("+"))
if combo in seen:
continue
seen.add(combo)
action = resolve(action)
action = re.sub(r"^exec\s+(--no-startup-id\s+)?", "", action).strip().strip('"')
lines.append(f"{combo:<24} {action}")
return lines
def show(lines):
if not lines:
lines = ["(aucun raccourci trouvé)"]
text = "\n".join(lines)
if os.environ.get("HYPRLAND_INSTANCE_SIGNATURE"):
cmd = ["wofi", "--dmenu", "--prompt", "Raccourcis clavier — taper pour filtrer",
"--width", "820", "--height", "600", "--insensitive",
"--cache-file", "/dev/null"]
if os.path.exists(_WOFI_STYLE):
cmd += ["--style", _WOFI_STYLE]
else:
# X11/i3 : rofi en dmenu, style assorti (mono pour l'alignement).
cmd = ["rofi", "-dmenu", "-i", "-p", "Raccourcis clavier",
"-no-custom",
"-theme-str", 'window { width: 880px; } listview { lines: 18; }',
"-theme-str", '* { font: "Noto Sans Mono 11"; }']
subprocess.run(cmd, input=text, text=True, check=False)
def main():
try:
if os.environ.get("HYPRLAND_INSTANCE_SIGNATURE"):
lines = hyprland_lines()
else:
lines = i3_lines()
except Exception as exc: # noqa: BLE001 — best effort, jamais de crash muet
subprocess.run(["notify-send", "Raccourcis FWS", f"Erreur : {exc}"],
check=False)
return 1
show(lines)
return 0
if __name__ == "__main__":
sys.exit(main())
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
# FWS — menu d'alimentation (verrouiller / déconnexion / suspendre /
# redémarrer / éteindre). Bouton de la barre ou Super+X. Universel :
# • Hyprland (Wayland) → wofi, verrouillage hyprlock, sortie hyprctl.
# • i3 (X11) → rofi, verrouillage i3lock, sortie i3-msg exit.
# Aucune dépendance hors python3 + wofi/rofi (déjà là avec le bureau).
import os
import subprocess
import sys
_WOFI_STYLE = "/usr/local/share/fws/wofi-style.css"
_HYPR = bool(os.environ.get("HYPRLAND_INSTANCE_SIGNATURE"))
# (libellé affiché, commande shell). L'icône Nerd Font aide au repérage.
if _HYPR:
_lock = "hyprlock"
_logout = "hyprctl dispatch exit"
else:
_lock = "i3lock -c 1e1e2e"
_logout = "i3-msg exit"
_ITEMS = [
("󰌾 Verrouiller", _lock),
("󰗽 Déconnexion", _logout),
("󰤄 Suspendre", "systemctl suspend"),
("󰜉 Redémarrer", "systemctl reboot"),
("󰐥 Éteindre", "systemctl poweroff"),
]
def _menu(entries):
text = "\n".join(entries)
if _HYPR:
cmd = ["wofi", "--dmenu", "--prompt", "Alimentation",
"--width", "300", "--height", "260", "--cache-file", "/dev/null"]
if os.path.exists(_WOFI_STYLE):
cmd += ["--style", _WOFI_STYLE]
else:
cmd = ["rofi", "-dmenu", "-i", "-p", "Alimentation", "-no-custom",
"-theme-str", "window { width: 300px; } listview { lines: 5; }"]
out = subprocess.run(cmd, input=text, text=True,
capture_output=True, check=False)
return out.stdout.strip()
def main():
choice = _menu([label for label, _ in _ITEMS])
if not choice:
return 0
for label, command in _ITEMS:
if label == choice:
subprocess.Popen(["sh", "-c", command]) # noqa: S603,S607
break
return 0
if __name__ == "__main__":
sys.exit(main())
+46
View File
@@ -0,0 +1,46 @@
#!/bin/bash
# FWS — installe/actualise Proton-GE (GloriousEggroll) pour Steam :
# meilleure compatibilité des jeux Windows que le Proton officiel.
# S'exécute en UTILISATEUR (installe dans ~/.steam/root/compatibilitytools.d).
set -u
say() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; }
fail() { printf '\033[1;31m ✗ %s\033[0m\n' "$*"; read -rp "Entrée pour fermer. " _; exit 1; }
[ "$(id -u)" -eq 0 ] && fail "à lancer en utilisateur normal (pas root) : l'outil s'installe dans ton Steam."
API="https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest"
DEST="$HOME/.steam/root/compatibilitytools.d"
say "Recherche de la dernière version de Proton-GE…"
JSON="$(curl -fsSL "$API")" || fail "GitHub inaccessible (réseau ?)"
URL="$(printf '%s' "$JSON" | grep -o '"browser_download_url": *"[^"]*\.tar\.gz"' | cut -d'"' -f4 | head -1)"
SUM_URL="$(printf '%s' "$JSON" | grep -o '"browser_download_url": *"[^"]*\.sha512sum"' | cut -d'"' -f4 | head -1)"
[ -n "$URL" ] || fail "archive introuvable dans la release GitHub"
NAME="$(basename "$URL" .tar.gz)"
if [ -d "$DEST/$NAME" ]; then
say "$NAME est déjà installé ✔"
read -rp "Entrée pour fermer. " _; exit 0
fi
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
say "Téléchargement de $NAME (~450 Mo)…"
curl -fL --progress-bar -o "$TMP/$NAME.tar.gz" "$URL" || fail "téléchargement échoué"
if [ -n "$SUM_URL" ]; then
say "Vérification de l'intégrité (sha512)…"
curl -fsSL -o "$TMP/sum" "$SUM_URL" \
&& (cd "$TMP" && sed "s|[^ ]*\.tar\.gz|$NAME.tar.gz|" sum | sha512sum -c -) \
|| fail "somme de contrôle invalide — archive corrompue ?"
fi
say "Installation dans $DEST…"
mkdir -p "$DEST"
tar -xzf "$TMP/$NAME.tar.gz" -C "$DEST" || fail "extraction échouée"
say "$NAME installé ✔"
echo " Dans Steam : Paramètres → Compatibilité → activer, puis choisir"
echo " « $NAME » (ou par jeu : Propriétés → Compatibilité)."
echo " Redémarre Steam pour qu'il apparaisse."
read -rp "Entrée pour fermer. " _
+220
View File
@@ -0,0 +1,220 @@
#!/bin/bash
# ============================================================================
# FWS — moteur de réparation d'une installation existante depuis le live
# (« point de restauration à la Windows »).
#
# L'interface GRAPHIQUE est fws-recovery-gui (GTK3 python — le live embarque
# python-gobject/gtk3 via Anaconda) ; elle pilote CE script en sous-commandes.
# Sans argument : assistant CONSOLE (repli sans X, ou usage à la main).
#
# Sous-commandes (pour la GUI / scripts) :
# scan → une ligne « périphérique|nom » par install FWS
# repair <action> <dev> → monte, répare, démonte ; sortie en continu
# action ∈ grub|initramfs|pacman|pacman-update|all
# shell <dev> → terminal DANS le système (xterm si DISPLAY)
#
# arch-chroot (arch-install-scripts, présent sur le live) gère les binds.
#
# Fonctionne depuis le LIVE (autre disque) ET depuis le SYSTÈME INSTALLÉ
# lui-même (menu d'applications / entrée GRUB « Réparer FWS ») : si la cible
# est la racine en cours d'exécution, on opère directement, sans mount/chroot.
# ============================================================================
set -u
MNT=/mnt/fws-recovery
ROOTDEV=""
DO_UPDATE=""
SELF="" # cible = système courant
ROOTSRC="$(findmnt -no SOURCE / 2>/dev/null)" # sur le live : overlay → jamais un lsblk
say() { printf '\n==> %s\n' "$*"; }
warn() { printf ' ⚠ %s\n' "$*"; }
[ "$(id -u)" -eq 0 ] || { echo "root requis"; exit 1; }
cleanup() { [ -n "$MNT" ] && [ "$MNT" != "/" ] && umount -R "$MNT" 2>/dev/null; }
# Exécute DANS la cible : chroot si elle est montée, directement si c'est nous.
in_target() {
if [ -n "$SELF" ]; then "$@"; else arch-chroot "$MNT" "$@"; fi
}
# ── Moteur ───────────────────────────────────────────────────────────────────
scan_installs() {
vgscan --mknodes >/dev/null 2>&1
vgchange -ay >/dev/null 2>&1
udevadm settle --timeout=10 >/dev/null 2>&1
# Le système en cours d'exécution d'abord (app lancée depuis l'OS installé).
if [ ! -d /run/archiso ] && grep -qiE '^(ID|NAME)=.*fws' /etc/os-release 2>/dev/null; then
printf '%s|%s (ce système)\n' "$ROOTSRC" \
"$(. /etc/os-release 2>/dev/null; echo "${PRETTY_NAME:-FWS}")"
fi
mkdir -p "$MNT"
local dev name
while read -r dev; do
[ -b "$dev" ] || continue
[ "$dev" = "$ROOTSRC" ] && continue # déjà listé ci-dessus
mount -o ro "$dev" "$MNT" 2>/dev/null || continue
if [ -f "$MNT/etc/os-release" ] && [ -d "$MNT/boot" ] \
&& grep -qiE '^(ID|NAME)=.*fws' "$MNT/etc/os-release" 2>/dev/null; then
name="$(. "$MNT/etc/os-release" 2>/dev/null; echo "${PRETTY_NAME:-FWS}")"
printf '%s|%s\n' "$dev" "$name"
fi
umount "$MNT" 2>/dev/null
done < <(lsblk -rpno NAME,TYPE | awk '$2=="part"||$2=="lvm"{print $1}')
}
# Monte la cible + /boot + ESP d'après son fstab.
# Cas particulier : la cible EST la racine en cours d'exécution (app lancée
# depuis l'OS installé, ou entrée GRUB « Réparer FWS ») → tout est déjà là,
# on opère en place (MNT="" : les chemins "$MNT/…" pointent sur la vraie racine).
mount_target() {
ROOTDEV="$1"
if [ "$ROOTDEV" = "$ROOTSRC" ] && [ ! -d /run/archiso ]; then
SELF=1; MNT=""
echo " cible = système en cours d'exécution (pas de montage)"
return 0
fi
mkdir -p "$MNT"
mount "$ROOTDEV" "$MNT" || return 1
local mp src
for mp in /boot /boot/efi; do
src="$(awk -v mp="$mp" '$2==mp && $1!~/^#/{print $1; exit}' "$MNT/etc/fstab" 2>/dev/null)"
[ -n "$src" ] || continue
mount "$(findfs "$src" 2>/dev/null || echo "$src")" "$MNT$mp" 2>/dev/null \
&& echo " $mp monté" || warn "$mp non monté (on continue)"
done
return 0
}
fix_grub() {
say "Réinstallation de GRUB…"
if [ -d /sys/firmware/efi ]; then
in_target grub-install --target=x86_64-efi \
--efi-directory=/boot/efi --bootloader-id=FWS --recheck || true
in_target grub-install --target=x86_64-efi \
--efi-directory=/boot/efi --bootloader-id=FWS --removable --recheck
else
local disk
disk="$(lsblk -spno NAME,TYPE "$ROOTDEV" 2>/dev/null \
| awk '$2=="disk"{print $1; exit}' | grep -oE '/dev/[a-z0-9]+')"
[ -n "$disk" ] || { warn "disque BIOS introuvable"; return 1; }
in_target grub-install --target=i386-pc --recheck "$disk"
fi
in_target grub-mkconfig -o /boot/grub/grub.cfg
}
fix_initramfs() {
say "Régénération de l'initramfs (mkinitcpio -P)…"
echo " NB : les avertissements « Possibly missing firmware for module … » sont"
echo " NORMAUX sur Arch : firmware de matériel exotique (cartes SCSI/fibre…)"
echo " non fourni par linux-firmware. Sans effet, sauf si tu AS ce matériel."
in_target mkinitcpio -P
}
fix_pacman() {
say "Réparation de pacman…"
rm -f "$MNT/var/lib/pacman/db.lck" && echo " verrou db.lck retiré (s'il existait)"
in_target pacman-key --init
in_target pacman-key --populate archlinux
if [ -n "$DO_UPDATE" ]; then
say "Mise à jour complète (pacman -Syu)…"
[ -z "$SELF" ] && cp -f /etc/resolv.conf "$MNT/etc/resolv.conf" 2>/dev/null
in_target pacman -Syu --noconfirm
fi
}
fix_all() { fix_initramfs && fix_grub; }
# ── Sous-commandes (pilotées par fws-recovery-gui) ───────────────────────────
case "${1:-}" in
scan)
scan_installs
exit 0 ;;
repair)
action="${2:?action manquante}"; dev="${3:?périphérique manquant}"
trap cleanup EXIT
say "Montage de $dev…"
mount_target "$dev" || { warn "montage impossible"; exit 1; }
rc=0
case "$action" in
grub) fix_grub || rc=$? ;;
initramfs) fix_initramfs || rc=$? ;;
pacman) fix_pacman || rc=$? ;;
pacman-update) DO_UPDATE=1; fix_pacman || rc=$? ;;
all) fix_all || rc=$? ;;
*) warn "action inconnue : $action"; rc=2 ;;
esac
cleanup; trap - EXIT
[ "$rc" -eq 0 ] && say "Terminé ✔" || say "Échec (code $rc)"
exit "$rc" ;;
shell)
dev="${2:?périphérique manquant}"
trap cleanup EXIT
mount_target "$dev" || { warn "montage impossible"; exit 1; }
if [ -z "$SELF" ] && [ -n "${DISPLAY:-}" ] && command -v xterm >/dev/null 2>&1; then
xterm -title "FWS — terminal dans le système (chroot)" \
-e arch-chroot "$MNT" /bin/bash
else
in_target /bin/bash
fi
cleanup; trap - EXIT
exit 0 ;;
""|tui) : ;; # assistant console ci-dessous
*) echo "usage : fws-recovery [scan|repair <action> <dev>|shell <dev>]"; exit 2 ;;
esac
# ── Assistant CONSOLE (repli sans X / usage manuel) ──────────────────────────
trap cleanup EXIT
say "Recherche des installations FWS…"
mapfile -t CANDIDATES < <(scan_installs)
if [ "${#CANDIDATES[@]}" -eq 0 ]; then
warn "Aucune installation FWS trouvée. Disques visibles :"
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
read -rp "Entrée pour ouvrir un shell (exit pour redémarrer). " _
bash; exit 0
fi
echo; i=1
for c in "${CANDIDATES[@]}"; do echo " $i) ${c%%|*} — ${c##*|}"; i=$((i+1)); done
read -rp "Installation à réparer [1] : " pick; pick="${pick:-1}"
dev="${CANDIDATES[$((pick-1))]%%|*}"
[ -n "$dev" ] || exit 1
say "Montage de $dev…"
mount_target "$dev" || { warn "montage impossible"; exit 1; }
while :; do
cat <<'MENU'
╔════════════════════════════════════════════════════════╗
║ FWS — Réparation du système ║
╠════════════════════════════════════════════════════════╣
║ 1) Réinstaller GRUB (le système ne démarre plus) ║
║ 2) Régénérer l'initramfs (mkinitcpio -P) ║
║ 3) Réparer pacman (verrou, clés, mise à jour) ║
║ 4) Tout réparer (initramfs + GRUB) ║
║ 5) Ouvrir un shell DANS le système (chroot) ║
║ q) Quitter et redémarrer ║
╚════════════════════════════════════════════════════════╝
MENU
read -rp " Choix : " choice
case "$choice" in
1) fix_grub ;;
2) fix_initramfs ;;
3) DO_UPDATE=""
read -rp " Mise à jour complète aussi (réseau) ? [o/N] " _u
case "$_u" in o|O|oui) DO_UPDATE=1 ;; esac
fix_pacman ;;
4) fix_all ;;
5) say "Shell chroot — « exit » pour revenir au menu."
in_target /bin/bash ;;
q|Q) break ;;
*) warn "choix inconnu" ;;
esac
done
cleanup; trap - EXIT
say "Réparation terminée."
read -rp "Redémarrer maintenant ? [O/n] " _r
case "$_r" in n|N) bash ;; *) systemctl reboot ;; esac
+269
View File
@@ -0,0 +1,269 @@
#!/usr/bin/env python3
# FWS — application GRAPHIQUE de réparation du système (mode « Réparer FWS »
# du live). GTK3 : le live embarque python-gobject/gtk3 via Anaconda.
#
# Pilote le MOTEUR fws-recovery en sous-commandes :
# fws-recovery scan → détecte les installations FWS
# fws-recovery repair <action> <dev> → répare (sortie streamée ici)
# fws-recovery shell <dev> → terminal chroot (xterm)
#
# Deux contextes de lancement :
# • mode « Réparer FWS » (live OU entrée GRUB de l'OS installé) : session
# root via fws-recovery-session → le moteur est appelé directement ;
# • menu d'applications de l'OS installé : lancée en UTILISATEUR → chaque
# appel du moteur passe par pkexec (politique org.fws.recovery,
# auth_admin_keep : un seul mot de passe pour toute la session).
import os
import shutil
import subprocess
import sys
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import GLib, Gtk, Pango # noqa: E402
GLib.set_prgname("fws-recovery-gui")
_ENGINE = "/usr/local/bin/fws-recovery"
def _engine_cmd(args):
"""Commande moteur, élevée via pkexec si on n'est pas root."""
cmd = [_ENGINE] + args
if os.geteuid() != 0:
cmd = ["pkexec"] + cmd
return cmd
_ACTIONS = [
("grub", "Réinstaller GRUB",
"le système ne démarre plus / « Operating System not found »"),
("initramfs", "Régénérer l'initramfs",
"kernel panic au démarrage, module manquant (mkinitcpio -P)"),
("pacman", "Réparer pacman",
"verrou bloqué, clés de signature cassées"),
("pacman-update", "Réparer pacman + mise à jour complète",
"idem, puis pacman -Syu (réseau requis)"),
("all", "Tout réparer",
"initramfs puis GRUB — le réflexe « ça ne boote plus »"),
]
class RecoveryWindow(Gtk.Window):
def __init__(self):
super().__init__(title="FWS — Réparation du système")
self.set_icon_name("fws-recovery")
self.set_default_size(720, 560)
self.set_position(Gtk.WindowPosition.CENTER)
self.set_border_width(18)
self.connect("destroy", Gtk.main_quit)
self._proc = None
self._installs = [] # [(dev, nom), …]
self._action_buttons = []
root = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
self.add(root)
title = Gtk.Label()
title.set_markup("<span size='x-large' weight='bold'>Réparation du système FWS</span>")
root.pack_start(title, False, False, 0)
self._status = Gtk.Label(label="Recherche des installations FWS sur les disques…")
self._status.set_xalign(0)
root.pack_start(self._status, False, False, 0)
# Sélecteur d'installation (rempli après le scan).
picker_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
picker_row.pack_start(Gtk.Label(label="Système à réparer :"), False, False, 0)
self._picker = Gtk.ComboBoxText()
picker_row.pack_start(self._picker, True, True, 0)
root.pack_start(picker_row, False, False, 0)
# Boutons d'action (style fws-hello : titre + description).
for key, label, desc in _ACTIONS:
button = Gtk.Button()
inner = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
lab = Gtk.Label()
lab.set_markup(f"<b>{label}</b>")
lab.set_xalign(0)
sub = Gtk.Label(label=desc)
sub.set_xalign(0)
sub.get_style_context().add_class("dim-label")
inner.pack_start(lab, False, False, 0)
inner.pack_start(sub, False, False, 0)
button.add(inner)
button.connect("clicked", self._on_action, key)
root.pack_start(button, False, False, 0)
self._action_buttons.append(button)
shell_btn = Gtk.Button(label="Ouvrir un terminal dans le système (chroot)")
shell_btn.connect("clicked", self._on_shell)
root.pack_start(shell_btn, False, False, 0)
self._action_buttons.append(shell_btn)
# Journal (sortie du moteur, en continu).
frame = Gtk.Frame(label="Journal")
scroll = Gtk.ScrolledWindow()
scroll.set_min_content_height(150)
self._log = Gtk.TextView()
self._log.set_editable(False)
self._log.set_cursor_visible(False)
self._log.set_monospace(True)
self._log.modify_font(Pango.FontDescription("Monospace 9"))
scroll.add(self._log)
frame.add(scroll)
root.pack_start(frame, True, True, 0)
# Pied : redémarrer / éteindre.
footer = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
reboot = Gtk.Button(label="Redémarrer")
reboot.connect("clicked", lambda _b: self._power("reboot"))
poweroff = Gtk.Button(label="Éteindre")
poweroff.connect("clicked", lambda _b: self._power("poweroff"))
footer.pack_end(reboot, False, False, 0)
footer.pack_end(poweroff, False, False, 0)
root.pack_start(footer, False, False, 0)
self._set_busy(True)
GLib.idle_add(self._start_scan)
# ── Utilitaires ─────────────────────────────────────────────────────────
def _append(self, text):
# Rebranding d'affichage : les outils sous-jacents parlent
# d'« Arch Linux » — pas le journal FWS. (Les identifiants techniques
# comme archlinux-keyring restent tels quels : ce sont de vrais noms
# de fichiers/paquets, les masquer fausserait le diagnostic.)
text = text.replace("Arch Linux", "FWS").replace("Fedora", "FWS")
buf = self._log.get_buffer()
buf.insert(buf.get_end_iter(), text)
mark = buf.create_mark(None, buf.get_end_iter(), False)
self._log.scroll_mark_onscreen(mark)
buf.delete_mark(mark)
def _set_busy(self, busy):
for b in self._action_buttons:
b.set_sensitive(not busy and bool(self._installs))
# Sensible même avec UNE seule install : un combo grisé donne
# l'impression que TOUTE l'app est désactivée (retour utilisateur).
self._picker.set_sensitive(not busy and bool(self._installs))
def _selected_dev(self):
idx = self._picker.get_active()
if 0 <= idx < len(self._installs):
return self._installs[idx][0]
return None
def _power(self, what):
subprocess.run(["systemctl", what], check=False)
# ── Scan des installations ──────────────────────────────────────────────
def _start_scan(self):
self._run_engine(["scan"], self._scan_done, quiet=True)
return False
def _scan_done(self, rc, output):
self._installs = []
for line in output.splitlines():
if "|" in line:
dev, _, name = line.partition("|")
self._installs.append((dev.strip(), name.strip()))
self._picker.append_text(f"{name.strip()} — {dev.strip()}")
if self._installs:
self._picker.set_active(0)
n = len(self._installs)
self._status.set_markup(
f"<b>{n}</b> installation{'s' if n > 1 else ''} FWS détectée{'s' if n > 1 else ''}."
" Choisis une réparation :")
elif rc in (126, 127):
# pkexec : authentification annulée ou refusée.
self._status.set_markup(
"<b>Authentification annulée.</b> Ferme puis relance"
" l'application pour réessayer.")
else:
self._status.set_markup(
"<b>Aucune installation FWS détectée.</b> Vérifie les disques"
" (terminal ci-dessous) ou redémarre.")
self._append("Aucune installation FWS trouvée.\n\n"
+ subprocess.run(["lsblk", "-o", "NAME,SIZE,TYPE,FSTYPE"],
capture_output=True, text=True,
check=False).stdout)
self._set_busy(False)
# Le terminal reste utile même sans installation détectée (diagnostic).
self._action_buttons[-1].set_sensitive(True)
# ── Exécution du moteur avec sortie en continu ──────────────────────────
def _run_engine(self, args, done_cb, quiet=False):
self._set_busy(True)
self._proc = subprocess.Popen(
_engine_cmd(args), stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, text=True, bufsize=1)
chunks = []
def on_io(source, _cond):
line = source.readline()
if line:
chunks.append(line)
if not quiet:
self._append(line)
return True
rc = self._proc.wait()
done_cb(rc, "".join(chunks))
return False
GLib.io_add_watch(self._proc.stdout,
GLib.IO_IN | GLib.IO_HUP, on_io)
def _on_action(self, _button, key):
dev = self._selected_dev()
if not dev:
return
label = next(lab for k, lab, _d in _ACTIONS if k == key)
self._status.set_markup(f"<b>{label}</b> sur {dev} — en cours…")
self._append(f"\n───── {label} ({dev}) ─────\n")
self._run_engine(["repair", key, dev], self._action_done)
def _action_done(self, rc, _output):
if rc == 0:
self._status.set_markup("<b>Terminé ✔</b> — autre réparation, ou redémarre.")
elif rc in (126, 127):
self._status.set_markup(
"<b>Authentification annulée</b> — rien n'a été modifié.")
else:
self._status.set_markup(
f"<b>Échec (code {rc})</b> — détail dans le journal ci-dessous.")
self._set_busy(False)
def _on_shell(self, _button):
dev = self._selected_dev()
if dev and os.geteuid() == 0:
subprocess.Popen([_ENGINE, "shell", dev])
elif dev:
# pkexec purge DISPLAY → le moteur ouvre le chroot DANS cet xterm
# (lancé en utilisateur, donc visible sur X11 comme Wayland).
subprocess.Popen(["xterm", "-title",
"FWS — terminal dans le système (chroot)",
"-e", "pkexec", _ENGINE, "shell", dev])
elif shutil.which("xterm"):
subprocess.Popen(["xterm", "-title", "FWS — terminal"])
def main():
if not Gtk.init_check()[0]:
# Pas d'affichage : le .zprofile retombera sur l'assistant console.
return 1
if os.geteuid() != 0 and not shutil.which("pkexec"):
dlg = Gtk.MessageDialog(message_type=Gtk.MessageType.ERROR,
buttons=Gtk.ButtonsType.CLOSE,
text="pkexec introuvable : lance fws-recovery-gui en root.")
dlg.run()
return 1
win = RecoveryWindow()
win.show_all()
Gtk.main()
return 0
if __name__ == "__main__":
sys.exit(main())
@@ -0,0 +1,8 @@
#!/bin/sh
# FWS — session X du mode réparation. Lancé par startx (cf. /root/.zprofile,
# entrée de boot « Réparer FWS »). Openbox minimal + l'application GTK
# fws-recovery-gui. Si la GUI meurt, openbox reste pour un xterm de secours.
xsetroot -solid "#1a1a2e" 2>/dev/null
openbox &
fws-recovery-gui
exec openbox
+84
View File
@@ -0,0 +1,84 @@
#!/bin/bash
# ============================================================================
# FWS — détection matérielle + drivers + stack « remplacement de Windows ».
#
# Appelé par le %post du kickstart : fws-setup-hardware <bureau>
# (<bureau> = cli|gnome|kde|xfce|hyprland). Réseau requis ; CHAQUE étape est
# NON bloquante (pas de set -e) : un paquet manquant ou pas de réseau ne casse
# pas l'installation.
#
# Installe :
# - les drivers GPU détectés via lspci (Intel / AMD / NVIDIA) + Mesa/Vulkan,
# - les outils invité selon l'hyperviseur (VMware/VirtualBox/QEMU/Hyper-V),
# - le son/micro/casque (PipeWire), le Bluetooth (BlueZ), les conteneurs
# (Podman),
# - et — si un bureau est choisi — la stack « comme Windows » : Steam, Wine,
# VR (Monado/OpenXR → SteamVR pour HTC Vive / Valve Index ; Oculus n'a pas
# de support Linux officiel), winboat-bin (apps Windows).
# ============================================================================
DESK="${1:-cli}"
log() { echo "[FWS-hw] $*"; }
# ── 1) Drivers GPU (détection lspci) ─────────────────────────────────────────
DRIVERS="mesa"
GPU="$(lspci 2>/dev/null | grep -iE 'VGA compatible controller|3D controller|Display controller')"
case "$GPU" in
*NVIDIA*|*GeForce*) DRIVERS="$DRIVERS nvidia-dkms nvidia-utils linux-headers dkms" ;;
*AMD*|*ATI*|*Radeon*) DRIVERS="$DRIVERS vulkan-radeon libva-mesa-driver" ;;
*Intel*) DRIVERS="$DRIVERS vulkan-intel intel-media-driver" ;;
esac
# ── 2) Outils invité selon l'hyperviseur ─────────────────────────────────────
VMSVC=""
case "$(systemd-detect-virt 2>/dev/null)" in
vmware) DRIVERS="$DRIVERS open-vm-tools"; VMSVC="vmtoolsd vmware-vmblock-fuse" ;;
oracle) DRIVERS="$DRIVERS virtualbox-guest-utils"; VMSVC="vboxservice" ;;
qemu|kvm) DRIVERS="$DRIVERS qemu-guest-agent spice-vdagent"; VMSVC="qemu-guest-agent spice-vdagentd" ;;
microsoft) DRIVERS="$DRIVERS hyperv"; VMSVC="hv_fcopy_daemon hv_kvp_daemon hv_vss_daemon" ;;
esac
# ── 3) Stack de base : son / micro / casque, Bluetooth, luminosité, conteneurs
# brightnessctl : réglage de luminosité sans root (via logind) — consommé par
# les touches XF86MonBrightness* et la molette du module backlight de waybar.
BASE="pipewire pipewire-alsa pipewire-pulse pipewire-jack wireplumber \
pavucontrol bluez bluez-utils brightnessctl podman"
# Gestionnaire Bluetooth graphique : KDE et GNOME ont le leur ; blueman pour
# les bureaux qui n'en fournissent pas (tray + assistant d'appairage).
case "$DESK" in
hyprland|xfce|i3) BASE="$BASE blueman" ;;
esac
# ── 4) Stack desktop : jeux, Windows-compat, VR (si un bureau est choisi) ────
DESKTOP=""
if [ "$DESK" != "cli" ]; then
# multilib requis (Steam / Wine 32 bits).
grep -qE '^\[multilib\]' /etc/pacman.conf \
|| printf '\n[multilib]\nInclude = /etc/pacman.d/mirrorlist\n' >> /etc/pacman.conf
# mangohud : overlay FPS/perfs en jeu (mangohud %command% dans Steam).
DESKTOP="steam wine winetricks wine-mono wine-gecko \
lib32-mesa lib32-pipewire monado gamemode \
mangohud lib32-mangohud \
noto-fonts ttf-liberation"
fi
# ── Sync + filtre aux paquets RÉELLEMENT existants ───────────────────────────
# (un nom de paquet manquant/renommé ne doit pas faire échouer tout le reste).
pacman -Sy --noconfirm || log "synchro pacman échouée (réseau ?)"
VALID=""
for p in $DRIVERS $BASE $DESKTOP; do
pacman -Si "$p" >/dev/null 2>&1 && VALID="$VALID $p"
done
if [ -n "$VALID" ]; then
log "installation :$VALID"
pacman -S --noconfirm --needed $VALID || log "certains paquets non installés"
fi
# NB : winboat-bin (AUR) n'est PAS installé ici (fws-local n'existe pas sur la
# cible) : il est intégré à l'image FWS via packages.x86_64 et recopié par le
# payload LiveOS d'Anaconda → déjà présent sur le système installé.
# ── Services matériels ───────────────────────────────────────────────────────
[ -n "$VMSVC" ] && systemctl enable $VMSVC 2>/dev/null
systemctl enable bluetooth.service 2>/dev/null
systemctl enable podman.socket 2>/dev/null
exit 0
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
# FWS — mise à jour du système en une commande (bouton de la barre, fws-hello,
# ou terminal). pacman -Syu + nettoyage du cache + rapport des orphelins.
set -u
say() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; }
if [ "$(id -u)" -ne 0 ]; then
exec sudo "$0" "$@"
fi
say "Mise à jour du système (pacman -Syu)…"
pacman -Syu || { echo "Échec de la mise à jour (réseau ?)"; read -rp "Entrée pour fermer. " _; exit 1; }
if command -v paccache >/dev/null 2>&1; then
say "Nettoyage du cache des paquets (2 dernières versions gardées)…"
paccache -rk2
fi
ORPHANS="$(pacman -Qtdq 2>/dev/null)"
if [ -n "$ORPHANS" ]; then
say "Paquets orphelins (plus requis par rien) :"
echo "$ORPHANS"
read -rp "Les supprimer ? [o/N] " _rm
case "$_rm" in o|O|oui) echo "$ORPHANS" | pacman -Rns --noconfirm - ;; esac
fi
if command -v flatpak >/dev/null 2>&1; then
say "Mise à jour des Flatpaks…"
flatpak update -y || true
fi
say "Système à jour ✔"
read -rp "Entrée pour fermer. " _
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
# FWS — indicateur « mises à jour disponibles » pour waybar/polybar.
# checkupdates (pacman-contrib) : lit les dépôts SANS toucher la base pacman.
# Sortie vide = rien à faire → le module de la barre se cache.
command -v checkupdates >/dev/null 2>&1 || exit 0
n="$(checkupdates 2>/dev/null | wc -l)"
[ "$n" -gt 0 ] && printf '󰚰 %s\n' "$n"
exit 0
+115
View File
@@ -0,0 +1,115 @@
#!/bin/bash
# ============================================================================
# FWS — installation des drivers/runtimes VR selon le casque.
#
# À lancer depuis un terminal (Super+Shift+V sous i3, ou l'entrée
# « Installation VR (FWS) » du menu d'applications). Réservé aux sessions
# X11 (i3) : SteamVR ne fonctionne PAS sous Wayland/Hyprland.
#
# Réalité du support Linux (résumé honnête) :
# • HTC Vive / Vive Pro / Valve Index (stations lighthouse) → NATIF via
# SteamVR. C'est le chemin le plus fiable.
# • Meta/Oculus Quest 1-3, Pico → PAS de support officiel ; streaming
# Wi-Fi via ALVR (+ SteamVR). ALVR n'est pas dans les dépôts Arch :
# le script installe la base et donne la marche à suivre.
# • Oculus Rift CV1/S (filaire) → pas de support utilisable.
# • Monado : runtime OpenXR libre (déjà proposé par fws-setup-hardware),
# utile pour les applis OpenXR natives sans SteamVR.
# ============================================================================
set -u
say() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; }
warn() { printf '\033[1;33m ⚠ %s\033[0m\n' "$*"; }
if [ "$(id -u)" -ne 0 ]; then
exec sudo "$0" "$@"
fi
# VM = cul-de-sac VR : pas d'accès direct au GPU ni à l'USB/DisplayPort du
# casque, et Steam (steamwebhelper) est INSTABLE en rendu logiciel (llvmpipe).
# On prévient au lieu de laisser l'utilisateur croire à une panne de FWS.
if systemd-detect-virt --quiet 2>/dev/null; then
warn "Machine VIRTUELLE détectée ($(systemd-detect-virt 2>/dev/null))."
warn "La VR ne fonctionnera PAS ici : casque et GPU inaccessibles depuis une VM."
warn "Steam lui-même peut planter en rendu logiciel (steamwebhelper)."
echo " → Fais cette installation sur la machine RÉELLE avec le casque branché."
read -rp " Continuer quand même (tests uniquement) ? [o/N] " _ok
case "$_ok" in o|O|oui) ;; *) exit 0 ;; esac
fi
# lib32 Vulkan selon le GPU (indispensable à SteamVR, qui est 32/64 bits).
GPU="$(lspci 2>/dev/null | grep -iE 'VGA compatible controller|3D controller|Display controller')"
LIB32="lib32-mesa"
case "$GPU" in
*NVIDIA*|*GeForce*) LIB32="$LIB32 lib32-nvidia-utils" ;;
*AMD*|*ATI*|*Radeon*) LIB32="$LIB32 lib32-vulkan-radeon" ;;
*Intel*) LIB32="$LIB32 lib32-vulkan-intel" ;;
esac
install_pkgs() {
# multilib requis pour Steam/lib32.
grep -qE '^\[multilib\]' /etc/pacman.conf \
|| printf '\n[multilib]\nInclude = /etc/pacman.d/mirrorlist\n' >> /etc/pacman.conf
pacman -Sy --noconfirm || { warn "synchro pacman échouée (réseau ?)"; return 1; }
local valid="" p
for p in "$@"; do
pacman -Si "$p" >/dev/null 2>&1 && valid="$valid $p" || warn "paquet inconnu ignoré : $p"
done
# shellcheck disable=SC2086
pacman -S --noconfirm --needed $valid
}
steamvr_base() {
say "Installation de la base SteamVR (Steam + Vulkan 32 bits : $LIB32)…"
# ttf-liberation : polices attendues par l'interface Steam (évite des
# rendus cassés/plantages cosmétiques sur install minimale).
# shellcheck disable=SC2086
install_pkgs steam $LIB32 ttf-liberation || return 1
say "Base installée. Étapes suivantes :"
echo " 1. Lance Steam, connecte-toi, puis installe « SteamVR » (Bibliothèque → Outils)."
echo " 2. Branche le casque et les stations, puis lance SteamVR."
echo " 3. Premier lancement : SteamVR peut demander à ajuster les règles udev — accepte."
say "Si Steam PLANTE (fenêtre qui se ferme, souvent steamwebhelper) :"
echo " • Vérifie l'espace disque (SteamVR ≈ 5 Go) : df -h ~"
echo " • GPU faible / VM / rendu logiciel — lance :"
echo " steam -cef-disable-gpu -cef-disable-gpu-compositing"
echo " • Pour voir l'erreur exacte : lance « steam » depuis un terminal,"
echo " ou regarde ~/.steam/steam/logs/ (console_log.txt, bootstrap_log.txt)."
}
case "${1:-menu}" in
menu)
cat <<'MENU'
╔══════════════════════════════════════════════════════════╗
║ FWS — Installation VR (session X11 requise) ║
╠══════════════════════════════════════════════════════════╣
║ 1) HTC Vive / Vive Pro / Valve Index (natif, SteamVR) ║
║ 2) Meta Quest / Pico (streaming ALVR) ║
║ 3) Monado (runtime OpenXR libre, sans SteamVR) ║
║ q) Quitter ║
╚══════════════════════════════════════════════════════════╝
MENU
read -rp " Choix : " choice
exec "$0" "$choice"
;;
1|vive|index)
steamvr_base || exit 1
;;
2|quest|pico)
steamvr_base || exit 1
say "Streaming Quest/Pico : il faut AUSSI ALVR (pas dans les dépôts Arch) :"
echo " 1. Télécharge le .tar.gz « ALVR Launcher » : https://github.com/alvr-org/ALVR/releases"
echo " 2. Sur le casque, installe le client ALVR (App Lab / SideQuest)."
echo " 3. Lance ALVR sur le PC (même Wi-Fi 5 GHz que le casque) — il pilote SteamVR."
;;
3|monado)
say "Installation de Monado (OpenXR) + outils…"
install_pkgs monado || exit 1
say "Monado installé. Les applis OpenXR le trouvent via /usr/share/openxr/."
;;
q|Q) exit 0 ;;
*) warn "choix inconnu : ${1}"; exec "$0" menu ;;
esac
echo
read -rp "Terminé — appuie sur Entrée pour fermer. " _
@@ -0,0 +1,20 @@
[Desktop Entry]
# FWS — lanceur de l'application de réparation dans l'OS INSTALLÉ (copié tel
# quel du live). L'app tourne en utilisateur ; chaque appel du moteur passe
# par pkexec (cf. org.fws.recovery.policy).
Type=Application
Name=Récupération FWS
Name[en]=FWS Recovery
GenericName=Réparation du système
GenericName[en]=System repair
Comment=Réparer GRUB, l'initramfs ou pacman — le point de restauration FWS
Comment[en]=Repair GRUB, the initramfs or pacman — the FWS restore point
Exec=/usr/local/bin/fws-recovery-gui
Icon=fws-recovery
# Associe la fenêtre (WM_CLASS via GLib.set_prgname) à ce lanceur → bonne
# icône dans la barre de titre / barre des tâches (sinon icône générique).
StartupWMClass=fws-recovery-gui
Terminal=false
Categories=System;
Keywords=recovery;réparation;restauration;grub;boot;secours;dépannage;
Keywords[en]=recovery;repair;restore;grub;boot;rescue;
@@ -0,0 +1,9 @@
[Desktop Entry]
# FWS — masque « À propos de Xfce » du menu d'applications. Le binaire
# xfce4-about vient de libxfce4ui (dépendance de Thunar) : il n'implique
# AUCUNE session XFCE. /usr/local/share étant prioritaire sur /usr/share
# dans XDG_DATA_DIRS, cette entrée NoDisplay masque celle du paquet partout.
Type=Application
Name=About Xfce
Exec=xfce4-about
NoDisplay=true
@@ -0,0 +1,180 @@
# FWS — configuration Hyprland par défaut.
# Le clavier (__KB_LAYOUT__ / __KB_VARIANT__) est injecté à l'installation,
# depuis le choix fait dans Anaconda. Édite ce fichier librement.
#
# Raccourcis : transposition de la config i3/sway FWS (mêmes touches).
# Couleurs : nuances de bleu (palette Catppuccin Mocha).
#
# Les raccourcis sont déclarés avec « bindd » (bind + description) : la
# description alimente l'écran « Raccourcis clavier » (bouton ⌨ de la barre
# ou SUPER+K → fws-keys, qui lit les binds RÉELS via hyprctl). Ajoute
# tes propres bindd : ils apparaîtront automatiquement dans la liste.
# ⚠ Pas de virgule dans les descriptions (séparateur de champs Hyprland).
monitor = , preferred, auto, 1
input {
kb_layout = __KB_LAYOUT__
kb_variant = __KB_VARIANT__
follow_mouse = 1
numlock_by_default = true
touchpad {
natural_scroll = true
tap-to-click = true
}
}
general {
gaps_in = 4
gaps_out = 8
border_size = 2
# Nuances de bleu : bleu → sapphire en dégradé sur la fenêtre active.
col.active_border = rgb(89b4fa) rgb(74c7ec) 45deg
col.inactive_border = rgb(313244)
layout = dwindle
}
decoration {
rounding = 8
# Flou et ombres COUPÉS : rendu logiciel (llvmpipe) en VM — cf. le
# wrapper fws-hyprland écrit par l'installateur. Sur un vrai GPU tu
# peux les réactiver (enabled = true).
blur {
enabled = false
}
shadow {
enabled = false
}
}
animations {
enabled = true
bezier = fws, 0.22, 1.0, 0.36, 1.0
animation = windows, 1, 3, fws
animation = workspaces, 1, 3, fws
animation = fade, 1, 2, default
}
misc {
disable_hyprland_logo = true
disable_splash_rendering = true # pas de « Thanks xxx! » en bas d'écran
force_default_wallpaper = 0
background_color = 0x1a1a2e # bleu nuit FWS (aucun fond requis)
}
# Démarrage de session : barre d'état, notifications, réseau dans le tray,
# agent d'authentification GUI (indispensable pour les apps qui demandent
# les droits admin : gestionnaire de disques, blueman…).
exec-once = waybar
exec-once = mako
exec-once = nm-applet
# Applet Bluetooth : seulement si un adaptateur existe (en VM il n'y en a
# pas → blueman afficherait une erreur « Connection to BlueZ failed »).
exec-once = sh -c '[ -n "$(ls -A /sys/class/bluetooth 2>/dev/null)" ] && exec blueman-applet'
exec-once = /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
# Fenêtre de bienvenue (1er démarrage seulement — se retire ensuite).
exec-once = fws-hello --autostart
$mod = SUPER
# --- Applications ---------------------------------------------------------
bindd = $mod, Return, Ouvrir un terminal (kitty), exec, kitty
bindd = $mod SHIFT, Return, Terminal flottant, exec, kitty --class floating-term
bindd = $mod, R, Menu des applications, exec, wofi --show drun
bindd = $mod, E, Gestionnaire de fichiers, exec, dolphin
bindd = $mod, B, Ouvrir Firefox, exec, firefox
bindd = $mod, K, Afficher les raccourcis clavier, exec, fws-keys
bindd = $mod, X, Menu d'alimentation (arrêt/redémarrage…), exec, fws-powermenu
# --- Fenêtres ---------------------------------------------------------------
bindd = $mod, Q, Fermer la fenêtre, killactive,
bindd = $mod, V, Fenêtre flottante ou ancrée, togglefloating,
bindd = $mod, F, Plein écran, fullscreen,
bindd = $mod, J, Basculer la découpe horizontale/verticale, layoutmsg, togglesplit
bindd = $mod, L, Verrouiller l'écran, exec, hyprlock
bindd = $mod SHIFT, E, Quitter la session (retour à SDDM), exit,
bindd = $mod SHIFT, C, Recharger la configuration Hyprland, exec, hyprctl reload
# --- Focus (flèches) ---------------------------------------------------------
bindd = $mod, left, Focus à gauche, movefocus, l
bindd = $mod, right, Focus à droite, movefocus, r
bindd = $mod, up, Focus en haut, movefocus, u
bindd = $mod, down, Focus en bas, movefocus, d
# --- Déplacer la fenêtre (Maj + flèches) ---------------------------------------
bindd = $mod SHIFT, left, Déplacer la fenêtre à gauche, movewindow, l
bindd = $mod SHIFT, right, Déplacer la fenêtre à droite, movewindow, r
bindd = $mod SHIFT, up, Déplacer la fenêtre en haut, movewindow, u
bindd = $mod SHIFT, down, Déplacer la fenêtre en bas, movewindow, d
# --- Redimensionner (Ctrl + flèches, maintien possible) ------------------------
binde = $mod CTRL, left, resizeactive, -30 0
binde = $mod CTRL, right, resizeactive, 30 0
binde = $mod CTRL, up, resizeactive, 0 -30
binde = $mod CTRL, down, resizeactive, 0 30
# --- Espaces de travail 1-10 (0 = espace 10, comme i3) --------------------------
bindd = $mod, 1, Aller à l'espace 1, workspace, 1
bindd = $mod, 2, Aller à l'espace 2, workspace, 2
bindd = $mod, 3, Aller à l'espace 3, workspace, 3
bindd = $mod, 4, Aller à l'espace 4, workspace, 4
bindd = $mod, 5, Aller à l'espace 5, workspace, 5
bindd = $mod, 6, Aller à l'espace 6, workspace, 6
bindd = $mod, 7, Aller à l'espace 7, workspace, 7
bindd = $mod, 8, Aller à l'espace 8, workspace, 8
bindd = $mod, 9, Aller à l'espace 9, workspace, 9
bindd = $mod, 0, Aller à l'espace 10, workspace, 10
bindd = $mod SHIFT, 1, Envoyer la fenêtre vers l'espace 1, movetoworkspace, 1
bindd = $mod SHIFT, 2, Envoyer la fenêtre vers l'espace 2, movetoworkspace, 2
bindd = $mod SHIFT, 3, Envoyer la fenêtre vers l'espace 3, movetoworkspace, 3
bindd = $mod SHIFT, 4, Envoyer la fenêtre vers l'espace 4, movetoworkspace, 4
bindd = $mod SHIFT, 5, Envoyer la fenêtre vers l'espace 5, movetoworkspace, 5
bindd = $mod SHIFT, 6, Envoyer la fenêtre vers l'espace 6, movetoworkspace, 6
bindd = $mod SHIFT, 7, Envoyer la fenêtre vers l'espace 7, movetoworkspace, 7
bindd = $mod SHIFT, 8, Envoyer la fenêtre vers l'espace 8, movetoworkspace, 8
bindd = $mod SHIFT, 9, Envoyer la fenêtre vers l'espace 9, movetoworkspace, 9
bindd = $mod SHIFT, 0, Envoyer la fenêtre vers l'espace 10, movetoworkspace, 10
# --- Multi-écrans (comme i3 : Super+Ctrl+1/2 = écran gauche/droit) -------------
bindd = $mod CTRL, 1, Envoyer la fenêtre vers l'écran de gauche, movewindow, mon:l
bindd = $mod CTRL, 2, Envoyer la fenêtre vers l'écran de droite, movewindow, mon:r
# --- Molette = changer d'espace de travail --------------------------------------
bindd = $mod, mouse_up, Espace de travail précédent, workspace, e-1
bindd = $mod, mouse_down, Espace de travail suivant, workspace, e+1
# --- Scratchpad (espace spécial) -------------------------------------------------
bindd = $mod, S, Afficher le scratchpad, togglespecialworkspace, magic
bindd = $mod SHIFT, S, Envoyer la fenêtre au scratchpad, movetoworkspace, special:magic
# --- Captures d'écran (grim/slurp → presse-papier ou ~/Images) -------------------
bindd = , Print, Capture plein écran vers le presse-papier, exec, grim - | wl-copy
bindd = $mod, Print, Capture d'une zone vers le presse-papier, exec, grim -g "$(slurp)" - | wl-copy
bindd = $mod SHIFT, Print, Capture d'une zone vers ~/Images, exec, sh -c 'mkdir -p ~/Images && grim -g "$(slurp)" ~/Images/capture_$(date +%Y-%m-%d_%H-%M-%S).png'
# --- Multimédia (touches XF86 : volume via PipeWire, lecture via playerctl) ------
bindel = , XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+
bindel = , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
bindl = , XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
bindl = , XF86AudioPlay, exec, playerctl play-pause
bindl = , XF86AudioNext, exec, playerctl next
bindl = , XF86AudioPrev, exec, playerctl previous
# --- Luminosité de l'écran (portables : touches Fn) ------------------------------
bindel = , XF86MonBrightnessUp, exec, brightnessctl set +5%
bindel = , XF86MonBrightnessDown, exec, brightnessctl set 5%-
# --- Souris : déplacer / redimensionner les fenêtres -----------------------------
bindm = $mod, mouse:272, movewindow
bindm = $mod, mouse:273, resizewindow
# --- Règles de fenêtres (comme i3) ------------------------------------------------
# Syntaxe Hyprland ≥ 0.55 : champs « <règle> <valeur> » + cibles « match:… »
# (validé contre le binaire avec --verify-config).
windowrule = float on, match:class ^(floating-term)$
windowrule = size 900 550, match:class ^(floating-term)$
windowrule = float on, match:class ^([Pp]avucontrol|org.pulseaudio.pavucontrol)$
windowrule = float on, match:class ^(.?[Bb]lueman-manager.*)$
windowrule = float on, match:class ^([Ff]ws-hello)$
windowrule = float on, match:class ^([Ff]ws-about)$
windowrule = float on, match:title ^(Picture-in-Picture)$
@@ -0,0 +1,160 @@
# FWS — configuration i3 par défaut (session X11, COMPATIBLE VR).
# SteamVR fonctionne en X11 : c'est le bureau à choisir pour un casque VR
# (HTC Vive, Valve Index…). Lance « fws-vr-setup » (Super+Shift+V, ou menu)
# pour installer les drivers/runtimes selon ton casque.
# Le clavier X11 est configuré système via /etc/X11/xorg.conf.d/00-keyboard.conf
# (écrit par l'installateur selon ton choix Anaconda) : rien à régler ici.
set $mod Mod4
set $term kitty
set $filemanager dolphin
set $menu rofi -show drun -show-icons
set $browser firefox
# Couleurs — nuances de bleu (Catppuccin Mocha, comme SDDM/waybar FWS)
set $base #1e1e2e
set $surface0 #313244
set $text #cdd6f4
set $blue #89b4fa
set $sapphire #74c7ec
set $red #f38ba8
# classe bordure fond texte indicateur child_border
client.focused $blue $blue $base $sapphire $blue
client.focused_inactive $surface0 $surface0 $text $surface0 $surface0
client.unfocused $surface0 $surface0 $text $surface0 $surface0
client.urgent $red $red $base $red $red
client.background $base
font pango:Noto Sans 10
default_border pixel 2
gaps inner 4
gaps outer 4
##################
## APPLICATIONS ##
##################
bindsym $mod+Return exec $term
bindsym $mod+Shift+Return exec kitty --class floating-term
bindsym $mod+r exec $menu
bindsym $mod+e exec $filemanager
bindsym $mod+b exec $browser
# VR : installation des drivers/runtimes casque (HTC Vive, Index, Quest…)
bindsym $mod+Shift+v exec kitty -e fws-vr-setup
# Panneau des raccourcis clavier (aussi via le bouton ⌌ de la barre)
bindsym $mod+k exec fws-keys
# Menu d'alimentation (verrouiller/déconnexion/redémarrer/éteindre)
bindsym $mod+x exec fws-powermenu
##############
## FENETRES ##
##############
bindsym $mod+q kill
bindsym $mod+v floating toggle
bindsym $mod+f fullscreen toggle
bindsym $mod+j split toggle
bindsym $mod+l exec i3lock -c 1e1e2e
# Quitter i3 → retour à SDDM
bindsym $mod+Shift+e exec "i3-nagbar -t warning -m 'Quitter i3 et revenir à SDDM ?' -B 'Oui, quitter' 'i3-msg exit'"
bindsym $mod+Shift+c reload
# Focus (flèches)
bindsym $mod+Left focus left
bindsym $mod+Right focus right
bindsym $mod+Up focus up
bindsym $mod+Down focus down
# Déplacer la fenêtre
bindsym $mod+Shift+Left move left
bindsym $mod+Shift+Right move right
bindsym $mod+Shift+Up move up
bindsym $mod+Shift+Down move down
# Redimensionner (Ctrl + flèches)
bindsym $mod+Ctrl+Left resize shrink width 30 px
bindsym $mod+Ctrl+Right resize grow width 30 px
bindsym $mod+Ctrl+Up resize shrink height 30 px
bindsym $mod+Ctrl+Down resize grow height 30 px
################
## WORKSPACES ##
################
bindsym $mod+1 workspace number 1
bindsym $mod+2 workspace number 2
bindsym $mod+3 workspace number 3
bindsym $mod+4 workspace number 4
bindsym $mod+5 workspace number 5
bindsym $mod+6 workspace number 6
bindsym $mod+7 workspace number 7
bindsym $mod+8 workspace number 8
bindsym $mod+9 workspace number 9
bindsym $mod+0 workspace number 10
bindsym $mod+Shift+1 move container to workspace number 1
bindsym $mod+Shift+2 move container to workspace number 2
bindsym $mod+Shift+3 move container to workspace number 3
bindsym $mod+Shift+4 move container to workspace number 4
bindsym $mod+Shift+5 move container to workspace number 5
bindsym $mod+Shift+6 move container to workspace number 6
bindsym $mod+Shift+7 move container to workspace number 7
bindsym $mod+Shift+8 move container to workspace number 8
bindsym $mod+Shift+9 move container to workspace number 9
bindsym $mod+Shift+0 move container to workspace number 10
# Multi-écrans
bindsym $mod+Ctrl+1 move container to output left
bindsym $mod+Ctrl+2 move container to output right
# Molette = changer de workspace
bindsym $mod+button4 workspace prev
bindsym $mod+button5 workspace next
# Scratchpad
bindsym $mod+s scratchpad show
bindsym $mod+Shift+s move scratchpad
##############
## CAPTURES ##
##############
bindsym Print exec --no-startup-id "maim | xclip -selection clipboard -t image/png"
bindsym $mod+Print exec --no-startup-id "maim -s | xclip -selection clipboard -t image/png"
bindsym $mod+Shift+Print exec --no-startup-id "mkdir -p ~/Images && maim -s ~/Images/capture_$(date +%Y-%m-%d_%H-%M-%S).png"
################
## MULTIMEDIA ##
################
bindsym XF86AudioRaiseVolume exec --no-startup-id wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+
bindsym XF86AudioLowerVolume exec --no-startup-id wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
bindsym XF86AudioMute exec --no-startup-id wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
bindsym XF86AudioPlay exec --no-startup-id playerctl play-pause
bindsym XF86AudioNext exec --no-startup-id playerctl next
bindsym XF86AudioPrev exec --no-startup-id playerctl previous
bindsym XF86MonBrightnessUp exec --no-startup-id brightnessctl set +5%
bindsym XF86MonBrightnessDown exec --no-startup-id brightnessctl set 5%-
#####################
## REGLES FENETRES ##
#####################
for_window [class="floating-term"] floating enable, resize set 900 550
for_window [class="(?i)fws-hello"] floating enable
for_window [class="(?i)fws-about"] floating enable
for_window [class="(?i)pavucontrol"] floating enable, resize set 700 500
for_window [class="(?i)blueman-manager"] floating enable
for_window [title="Picture-in-Picture"] floating enable
###########
## BARRE ##
###########
# Polybar (config ~/.config/polybar/config.ini) : clone visuel de la waybar
# Hyprland FWS — pilules, icônes, horloge+date au centre, bouton Raccourcis.
# exec_always + pkill : la barre survit proprement aux reload ($mod+Shift+c).
exec_always --no-startup-id sh -c 'pkill -x polybar; sleep 0.3; exec polybar -q fws'
###############
## AUTOSTART ##
###############
# picom : coins arrondis (parité visuelle avec Hyprland) — config FWS légère.
# PAS en machine virtuelle : le compositing sur GPU virtuel (vmwgfx/llvmpipe)
# rate le repaint des fenêtres GTK → contenus noirs/artefacts. Les coins
# arrondis sont sacrifiés en VM ; sur matériel réel picom se lance normalement.
exec --no-startup-id sh -c 'systemd-detect-virt --quiet || exec picom --config ~/.config/picom.conf'
exec --no-startup-id dunst
exec --no-startup-id nm-applet
exec --no-startup-id /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
exec --no-startup-id sh -c '[ -n "$(ls -A /sys/class/bluetooth 2>/dev/null)" ] && exec blueman-applet'
# Fenêtre de bienvenue (1er démarrage seulement — se retire ensuite).
exec --no-startup-id fws-hello --autostart
@@ -0,0 +1,9 @@
$1████████ $2██$5████$2██
$1████████ $2█$5██$2██$5██$2█
$1████████ $2█$5██████$2█
$1████████ $2██$6█$2██$6█$2██
$3████████ $4████████
$3████████ $4████████
$3████████ $4████████
$3████████ $4████████
@@ -3,7 +3,7 @@ PRETTY_NAME="FWS Linux"
ID=fws
ID_LIKE=arch
BUILD_ID=rolling
VERSION_ID="1.0.0"
VERSION_ID="0.6.0"
ANSI_COLOR="38;2;23;147;209"
HOME_URL="https://github.com/You-re-like-Windows-a-bitch/fws"
SUPPORT_URL="https://github.com/You-re-like-Windows-a-bitch/fws/issues"
@@ -0,0 +1,19 @@
# FWS — picom minimal (session i3) : coins arrondis comme sous Hyprland.
# xrender : fiable partout, y compris en VM (rendu logiciel) ; pas de flou ni
# d'ombres (parité avec la config Hyprland FWS, et léger pour la VR).
backend = "xrender";
vsync = true;
corner-radius = 8;
rounded-corners-exclude = [
"window_type = 'dock'", # polybar gère son propre arrondi
"class_g = 'i3-frame'"
];
shadow = false;
fading = true;
fade-in-step = 0.08;
fade-out-step = 0.08;
# VR / jeux plein écran : ne pas composer par-dessus (latence).
unredir-if-possible = true;
@@ -0,0 +1,174 @@
; FWS — polybar (session i3) : clone visuel de la waybar Hyprland.
; Barre flottante arrondie, modules en « pilules » Catppuccin Mocha,
; icônes Nerd Font (ttf-nerd-fonts-symbols). Horloge + date au centre,
; bouton « ⌌ Raccourcis » (fws-keys), volume/réseau/CPU/RAM/tray à droite.
[colors]
base = #E01e1e2e
surface0 = #313244
text = #cdd6f4
subtext = #a6adc8
blue = #89b4fa
sapphire = #74c7ec
teal = #94e2d5
mauve = #cba6f7
maroon = #eba0ac
yellow = #f9e2af
red = #f38ba8
darkbase = #1e1e2e
[bar/fws]
monitor = ${env:MONITOR:}
width = 100%:-20
offset-x = 10
offset-y = 6
height = 36
radius = 14
background = ${colors.base}
foreground = ${colors.text}
border-size = 1
border-color = #5989b4fa
padding-left = 1
padding-right = 1
module-margin = 1
line-size = 0
font-0 = Noto Sans:size=10;2
font-1 = Symbols Nerd Font:size=12;3
font-2 = Noto Sans:weight=bold:size=10;2
modules-left = i3 xwindow
modules-center = date
modules-right = updates keyhelp backlight pulseaudio network cpu memory tray power
enable-ipc = true
wm-restack = i3
[module/i3]
type = internal/i3
pin-workspaces = true
show-urgent = true
strip-wsnumbers = false
format = <label-state> <label-mode>
label-focused = %name%
label-focused-background = ${colors.blue}
label-focused-foreground = ${colors.darkbase}
label-focused-padding = 2
label-unfocused = %name%
label-unfocused-background = ${colors.surface0}
label-unfocused-foreground = ${colors.subtext}
label-unfocused-padding = 2
label-visible = %name%
label-visible-background = ${colors.surface0}
label-visible-padding = 2
label-urgent = %name%
label-urgent-background = ${colors.red}
label-urgent-foreground = ${colors.darkbase}
label-urgent-padding = 2
[module/xwindow]
type = internal/xwindow
label = %title:0:45:…%
label-foreground = ${colors.subtext}
format-padding = 1
[module/date]
type = internal/date
interval = 1
date = %A %d %B %Y
time = %H:%M
label = 󰥔 %time% %date%
label-font = 3
format-background = ${colors.surface0}
format-padding = 3
[module/updates]
type = custom/script
exec = fws-updates-check
interval = 1800
click-left = kitty -e fws-update
format-background = ${colors.surface0}
format-foreground = ${colors.yellow}
format-padding = 2
[module/keyhelp]
type = custom/script
exec = echo "󰌌 Raccourcis"
interval = 3600
click-left = fws-keys
format-background = ${colors.blue}
format-foreground = ${colors.darkbase}
format-padding = 2
label-font = 3
[module/backlight]
type = internal/backlight
enable-scroll = true
format = <label>
label = 󰃟 %percentage%%
format-background = ${colors.surface0}
format-foreground = ${colors.yellow}
format-padding = 2
[module/pulseaudio]
type = internal/pulseaudio
use-ui-max = false
click-right = pavucontrol
format-volume = <label-volume>
label-volume = 󰕾 %percentage%%
format-volume-background = ${colors.surface0}
format-volume-foreground = ${colors.blue}
format-volume-padding = 2
label-muted = 󰝟 muet
format-muted-background = ${colors.surface0}
format-muted-foreground = ${colors.subtext}
format-muted-padding = 2
[module/network]
type = internal/network
interface-type = wired
interval = 5
format-connected = <label-connected>
label-connected = 󰈀 %local_ip%
format-connected-background = ${colors.surface0}
format-connected-foreground = ${colors.teal}
format-connected-padding = 2
format-disconnected = <label-disconnected>
label-disconnected = 󰤭 hors ligne
format-disconnected-background = ${colors.surface0}
format-disconnected-foreground = ${colors.red}
format-disconnected-padding = 2
[module/cpu]
type = internal/cpu
interval = 3
label = %percentage%%
format-background = ${colors.surface0}
format-foreground = ${colors.mauve}
format-padding = 2
[module/memory]
type = internal/memory
interval = 3
label = %percentage_used%%
format-background = ${colors.surface0}
format-foreground = ${colors.maroon}
format-padding = 2
[module/power]
type = custom/text
content = 󰐥
content-foreground = ${colors.red}
content-background = ${colors.surface0}
content-padding = 2
content-font = 2
click-left = fws-powermenu
[module/tray]
type = internal/tray
format = <tray>
format-background = ${colors.surface0}
format-padding = 1
tray-spacing = 8px
tray-size = 16px
[settings]
screenchange-reload = true
pseudo-transparency = false
@@ -0,0 +1,75 @@
/* FWS — rofi (menu d'applications i3, Super+R) : même look que wofi sous
Hyprland — fond sombre, bordure et sélection bleues, icônes. */
configuration {
show-icons: true;
display-drun: "Applications";
}
* {
bg: #1e1e2eF5;
surface: #313244;
text: #cdd6f4;
subtext: #a6adc8;
blue: #89b4fa;
darkbase: #1e1e2e;
font: "Noto Sans 12";
background-color: transparent;
text-color: @text;
}
window {
background-color: @bg;
border: 2px;
border-color: @blue;
border-radius: 14px;
width: 560px;
}
mainbox {
padding: 8px;
}
inputbar {
background-color: @surface;
border-radius: 10px;
margin: 4px;
padding: 8px 12px;
children: [ prompt, entry ];
}
prompt {
text-color: @blue;
margin: 0 8px 0 0;
}
entry {
placeholder: "Rechercher…";
placeholder-color: @subtext;
}
listview {
margin: 4px;
lines: 8;
scrollbar: false;
}
element {
padding: 8px 10px;
border-radius: 10px;
spacing: 8px;
}
element selected.normal {
background-color: @blue;
text-color: @darkbase;
}
element-icon {
size: 24px;
}
element-text {
text-color: inherit;
vertical-align: 0.5;
}
@@ -0,0 +1,103 @@
{
"layer": "top",
"position": "top",
"height": 36,
"spacing": 2,
"margin-top": 6,
"margin-left": 10,
"margin-right": 10,
"modules-left": ["hyprland/workspaces", "hyprland/window"],
"modules-center": ["clock"],
"modules-right": ["custom/updates", "custom/keyhelp", "backlight", "pulseaudio", "bluetooth", "network", "cpu", "memory", "battery", "tray", "custom/power"],
"custom/power": {
"format": "󰐥",
"tooltip-format": "Alimentation (SUPER+X)",
"on-click": "fws-powermenu"
},
"custom/updates": {
"exec": "fws-updates-check",
"interval": 1800,
"tooltip-format": "Mises à jour disponibles — clic pour installer",
"on-click": "kitty -e fws-update"
},
"hyprland/workspaces": {
"format": "{id}",
"on-click": "activate",
"on-scroll-up": "hyprctl dispatch workspace e-1",
"on-scroll-down": "hyprctl dispatch workspace e+1"
},
"hyprland/window": {
"max-length": 45,
"separate-outputs": true
},
"clock": {
"format": "󰥔 {:%H:%M %A %d %B %Y}",
"format-alt": "󰥔 {:%H:%M}",
"tooltip-format": "<tt>{calendar}</tt>",
"calendar": {
"format": {
"today": "<b><span color='#89b4fa'>{}</span></b>"
}
}
},
"custom/keyhelp": {
"format": "󰌌 Raccourcis",
"tooltip-format": "Raccourcis clavier (SUPER+K)",
"on-click": "fws-keys"
},
"backlight": {
"format": "{icon} {percent}%",
"format-icons": ["󰃞", "󰃟", "󰃠"],
"on-scroll-up": "brightnessctl set +5%",
"on-scroll-down": "brightnessctl set 5%-",
"tooltip-format": "Luminosité {percent}% (molette pour régler)"
},
"pulseaudio": {
"format": "{icon} {volume}%",
"format-muted": "󰝟 muet",
"format-icons": { "default": ["󰕿", "󰖀", "󰕾"] },
"on-click": "pavucontrol",
"on-click-right": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
"on-scroll-up": "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 2%+",
"on-scroll-down": "wpctl set-volume @DEFAULT_AUDIO_SINK@ 2%-",
"tooltip-format": "{desc} — clic : mixeur, clic droit : muet"
},
"bluetooth": {
"format": "󰂯",
"format-disabled": "󰂲",
"format-connected": "󰂱 {num_connections}",
"format-no-controller": "",
"on-click": "blueman-manager",
"tooltip-format": "{controller_alias}\t{controller_address}",
"tooltip-format-connected": "{device_enumerate}"
},
"network": {
"format-wifi": "󰤨 {essid}",
"format-ethernet": "󰈀 {ipaddr}",
"format-disconnected": "󰤭 hors ligne",
"tooltip-format": "{ifname} — {ipaddr}\n⇡ {bandwidthUpBytes} ⇣ {bandwidthDownBytes}"
},
"cpu": {
"format": " {usage}%",
"interval": 3
},
"memory": {
"format": " {percentage}%",
"interval": 3,
"tooltip-format": "{used:0.1f} / {total:0.1f} Gio"
},
"battery": {
"states": { "warning": 25, "critical": 10 },
"format": "{icon} {capacity}%",
"format-charging": "󰂄 {capacity}%",
"format-icons": ["󰁺", "󰁼", "󰁾", "󰂀", "󰁹"],
"tooltip-format": "{timeTo}"
},
"tray": {
"spacing": 8,
"icon-size": 16
}
}
@@ -0,0 +1,112 @@
/* FWS — style Waybar v2 (Catppuccin Mocha, nuances de bleu).
Barre flottante arrondie, modules en « pilules », icônes Nerd Font
(paquet ttf-nerd-fonts-symbols, installé avec le bureau Hyprland). */
* {
font-family: "Noto Sans", "Symbols Nerd Font", sans-serif;
font-size: 13px;
min-height: 0;
}
window#waybar {
background: rgba(30, 30, 46, 0.88); /* base #1e1e2e */
color: #cdd6f4; /* text */
border: 1px solid rgba(137, 180, 250, 0.35);
border-radius: 14px;
}
tooltip {
background: #1e1e2e;
border: 1px solid #89b4fa;
border-radius: 10px;
}
tooltip label { color: #cdd6f4; }
/* --- Espaces de travail --------------------------------------------------- */
#workspaces {
background: #313244; /* surface0 */
margin: 5px 4px;
border-radius: 10px;
padding: 0 4px;
}
#workspaces button {
color: #a6adc8; /* subtext0 */
padding: 0 8px;
margin: 3px 2px;
border: none;
border-radius: 8px;
background: transparent;
transition: all 0.2s ease;
}
#workspaces button.active {
color: #1e1e2e;
background: #89b4fa; /* bleu */
font-weight: bold;
}
#workspaces button:hover {
color: #cdd6f4;
background: #45475a;
box-shadow: none;
}
#window {
color: #a6adc8;
margin-left: 10px;
font-style: italic;
}
/* --- Horloge (centre) ------------------------------------------------------ */
#clock {
color: #cdd6f4;
font-weight: bold;
background: #313244;
padding: 0 16px;
margin: 5px 0;
border-radius: 10px;
}
/* --- Modules de droite : pilules ------------------------------------------- */
#custom-keyhelp,
#backlight,
#pulseaudio,
#bluetooth,
#network,
#cpu,
#memory,
#battery,
#tray {
background: #313244;
padding: 0 12px;
margin: 5px 3px;
border-radius: 10px;
color: #cdd6f4;
}
#custom-keyhelp {
background: #89b4fa; /* bouton d'appel : bleu plein */
color: #1e1e2e;
font-weight: bold;
}
#custom-keyhelp:hover { background: #b4d0fb; }
#custom-power {
color: #f38ba8; /* rouge doux (Catppuccin) */
}
#custom-power:hover { color: #ff6b81; }
#backlight { color: #f9e2af; } /* jaune doux */
#pulseaudio { color: #89b4fa; }
#pulseaudio.muted { color: #6c7086; }
#bluetooth { color: #74c7ec; } /* sapphire */
#bluetooth.disabled { color: #6c7086; }
#network { color: #94e2d5; } /* teal */
#network.disconnected { color: #f38ba8; }
#cpu { color: #cba6f7; } /* mauve */
#memory { color: #eba0ac; } /* maroon */
#battery.warning { color: #f9e2af; }
#battery.critical { color: #f38ba8; }
#battery.charging { color: #a6e3a1; }
@@ -0,0 +1,7 @@
# FWS — configuration wofi (menu d'applications).
insensitive=true
allow_images=true
image_size=24
prompt=Applications
width=560
height=440
@@ -0,0 +1,40 @@
/* FWS — style du panneau « Raccourcis clavier » (fws-keys).
Même palette que le menu wofi, mais police MONOSPACE : les colonnes
combinaison/description sont alignées par des espaces. */
window {
background-color: rgba(30, 30, 46, 0.97);
border: 2px solid #89b4fa;
border-radius: 14px;
font-family: "Noto Sans Mono", monospace;
font-size: 13px;
}
#input {
background-color: #313244;
color: #cdd6f4;
border: none;
border-radius: 10px;
margin: 12px;
padding: 8px 12px;
font-family: "Noto Sans", sans-serif;
}
#input image { color: #89b4fa; }
#inner-box {
margin: 0 8px 8px 8px;
}
#entry {
padding: 5px 10px;
border-radius: 8px;
color: #cdd6f4;
}
#entry:selected {
background-color: #313244;
color: #89b4fa;
}
#text { color: inherit; }
@@ -0,0 +1,42 @@
/* FWS — style wofi (menu d'applications Super+R). Catppuccin Mocha, bleu. */
window {
background-color: rgba(30, 30, 46, 0.96); /* base #1e1e2e */
border: 2px solid #89b4fa;
border-radius: 14px;
font-family: "Noto Sans", sans-serif;
font-size: 14px;
}
#input {
background-color: #313244; /* surface0 */
color: #cdd6f4;
border: none;
border-radius: 10px;
margin: 12px;
padding: 8px 12px;
}
#input image { color: #89b4fa; }
#inner-box {
margin: 0 8px 8px 8px;
}
#entry {
padding: 8px 10px;
border-radius: 10px;
color: #cdd6f4;
}
#entry:selected {
background-color: #89b4fa;
color: #1e1e2e;
}
#entry:selected #text { color: #1e1e2e; }
#text {
margin-left: 8px;
color: inherit;
}
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- FWS — icône « Récupération FWS » : croix de secours, bleus FWS. -->
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
<rect x="3" y="3" width="58" height="58" rx="13" fill="#1e1e2e"/>
<rect x="3" y="3" width="58" height="58" rx="13" fill="none" stroke="#89b4fa" stroke-width="3"/>
<path d="M25 13h14v12h12v14H39v12H25V39H13V25h12z" fill="#89b4fa"/>
<path d="M28 16h8v12h12v8H36v12h-8V36H16v-8h12z" fill="#74c7ec"/>
</svg>

After

Width:  |  Height:  |  Size: 515 B

@@ -5,33 +5,36 @@
# lancée en mode interactif (chemin fixe codé en dur dans Anaconda :
# /usr/share/anaconda/interactive-defaults.ks). Aucun inst.ks= n'est requis.
#
# RÔLE : empêcher Anaconda d'installer Fedora.
# RÔLE : finaliser une install « copie du live » (surtout pas Fedora).
# ----------------------------------------------------------------------------
# Sans payload défini, Anaconda utilise DNF → des RPM depuis les dépôts
# Fedora = il installe Fedora. Anaconda n'a AUCUN backend pacman. La seule
# façon de poser FWS/Arch est le payload « liveimg » : il recopie une image
# de système de fichiers (ici le squashfs du live = FWS) sur le disque, puis
# on finalise nous-mêmes (grub, mkinitcpio, bureau) en %post. C'est le modèle
# « Sabayon » : le système installé EST une copie du live.
# Anaconda n'a AUCUN backend pacman ; son payload par défaut (DNF) poserait
# des RPM Fedora. Ici le payload est LiveOS, imposé par « anaconda --liveinst »
# (cf. bloc « Source d'installation » ci-dessous) : il recopie le squashfs du
# live (= FWS) sur le disque, puis on finalise nous-mêmes (grub, mkinitcpio,
# bureau) en %post. C'est le modèle « Sabayon » : le système installé EST une
# copie du live.
#
# L'utilisateur garde la main dans le GUI sur tout ce qui N'EST PAS fixé ici :
# partitionnement, fuseau, clavier, langue, création d'utilisateur, mot de
# passe root, etc.
# ============================================================================
# --- Source d'installation : l'image du live (FWS), surtout pas Fedora -------
# Squashfs archiso sur le média booté :
# /run/archiso/bootmnt/<install_dir>/<arch>/airootfs.sfs
# install_dir=fws, arch=x86_64, airootfs_image_type=squashfs (cf. profiledef.sh).
# Anaconda loop-monte ce .sfs et rsync son contenu vers la cible.
# --- Source d'installation : le payload LiveOS (surtout pas Fedora/DNF) ------
# AUCUNE directive de payload ici, et c'est VOULU. liveinst lance
# « anaconda --liveinst » et anaconda.py teste opts.liveinst AVANT le type du
# payload DBus → il crée TOUJOURS un payload LiveOS et IGNORE tout payload du
# kickstart (un « liveimg » ici serait du code mort — vérifié dans
# pyanaconda/anaconda.py, propriété payload, et reproduit hors VM).
#
# ⚠ À VALIDER sur ISO réelle : certaines versions d'Anaconda attendent une
# structure « LiveOS/rootfs.img » à l'intérieur du squashfs (format Fedora
# Live) et non un squashfs « plat » comme celui d'archiso. Si l'install
# échoue à monter la source, basculer sur un tarball de rootfs dédié :
# liveimg --url=file:///run/archiso/bootmnt/fws/x86_64/fws-rootfs.tar.gz
# (un build-rootfs.sh produirait alors ce tar via pacstrap).
liveimg --url=file:///run/archiso/bootmnt/fws/x86_64/airootfs.sfs
# Le payload LiveOS détecte l'image live via /dev/mapper/live-base ou
# /run/rootfsbase (chemins Fedora/dracut, absents d'archiso → c'était le crash
# « SourceSetupError: No Live OS image found! » au démarrage). Le .xinitrc du
# live bind-monte donc /run/archiso/airootfs (squashfs FWS, ro) sur
# /run/rootfsbase AVANT de lancer liveinst : Anaconda monte le loop device et
# rsync son contenu vers la cible = le système installé EST une copie du live,
# puis on finalise en %post (grub, mkinitcpio, bureau, matériel).
# DNF/libdnf5 n'est jamais importé : le module DNF n'est chargé QUE si le
# payload actif est DNF (import lazy dans modules/payloads/payload/factory.py).
# --- Disque : repartir d'un disque PROPRE, même s'il a déjà servi ------------
# Sans ça, une réinstallation sur un disque déjà partitionné (typiquement une
@@ -69,9 +72,21 @@ clearpart --all --initlabel --disklabel=msdos
# partitionnement (auto ou manuel) car la plateforme l'impose.
bootloader --disabled
# Pas de section %packages : le payload liveimg n'installe aucun paquet
# Pas de section %packages : le payload LiveOS n'installe aucun paquet
# (il copie une image). Toute installation de paquets se fait en %post.
# --- Récapitulatif avant installation : PAGE GUI dédiée (pas de %pre-install) -
# L'écran « RÉCAPITULATIF AVANT INSTALLATION » est une standalone spoke
# Anaconda (overlay airootfs : pyanaconda/ui/gui/spokes/fws_recap.py,
# postForHub=SummaryHub, priority -10) intercalée ENTRE le hub et l'écran de
# progression : l'utilisateur clique « Commencer l'installation », voit le
# récap (bureau choisi, hostname, disques, GPU, réseau — AUCUNE écriture
# disque à ce stade), et l'installation ne démarre qu'à son clic « Lancer
# l'installation ». L'ancien récap zenity en %pre-install a été abandonné :
# le module scripts tourne avec LC_ALL=C et un env X incertain (zenity 4
# refuse les libellés UTF-8 en locale C → rc=255 pris pour « Annuler » →
# poweroff en plein install au 1er test réel).
# ============================================================================
# %post --nochroot — s'exécute dans le LIVE, la cible montée sous /mnt/sysroot
@@ -89,6 +104,17 @@ SYSROOT=/mnt/sysroot
# DNS pour que « pacman -S <bureau> » fonctionne dans le chroot.
cp -f /etc/resolv.conf "$SYSROOT/etc/resolv.conf" 2>/dev/null || true
# --- Hostname : valeur saisie dans le champ « Nom de machine » du spoke User --
# Anaconda ne capte PAS le hostname du spoke Réseau (→ /etc/hostname vide). On a
# donc ajouté un champ Hostname au spoke Utilisateur (cf. patch du paquet
# anaconda) : user.py écrit la valeur dans /tmp/fws-hostname (live). On la
# nettoie (caractères de hostname valides) et on l'écrit dans la cible ; défaut
# « fws » si vide/absent → plus jamais de hostname vide ni « archiso ».
HN=""
[ -f /tmp/fws-hostname ] && HN="$(tr -cd 'a-zA-Z0-9._-' < /tmp/fws-hostname)"
[ -n "$HN" ] || HN="fws"
echo "$HN" > "$SYSROOT/etc/hostname"
# Choix du bureau (écrit par l'addon « Choix du bureau », à venir ; valeur lue
# par le %post chrooté ci-dessous). « cli » par défaut si absent.
if [ -f /tmp/fws-desktop ]; then
@@ -194,6 +220,44 @@ else
echo "[FWS] AVERTISSEMENT : grub-install a échoué — bootloader à refaire à la main"
fi
# --- 3bis) Clavier (X11) + LOCALE --------------------------------------------
# CLAVIER : Anaconda passe par localed et n'écrit pas fiablement la disposition X
# sur la CIBLE → SDDM / sessions X en QWERTY US. On CALCULE la disposition CHOISIE
# (kickstart généré, sinon dérivée de la locale via langtable) en XL/XV, réutilisés
# plus bas (00-keyboard.conf pour SDDM/X ET la config Hyprland qui a son propre
# kb_layout). LOCALE : le locale choisi (ex. fr_CH.UTF-8) doit être GÉNÉRÉ —
# Anaconda vient de Fedora (locales pré-générées) et ne lance pas locale-gen →
# sans ça LANG casse et tout retombe en C.
XL=""; XV=""
XKB="$(sed -n "s/.*--xlayouts=//p" /root/anaconda-ks.cfg 2>/dev/null | head -1)"
XKB="${XKB#[\'\"]}"; XKB="${XKB%%[\'\"]*}"; XKB="${XKB%%,*}" # 1re dispo, ex. « ch (fr) »
if [ -z "$XKB" ]; then
_loc="$(. /etc/locale.conf 2>/dev/null; printf '%s' "${LANG%%.*}")" # fr_CH
_lng="${_loc%%_*}"; _terr="${_loc#*_}"; [ "$_terr" = "$_loc" ] && _terr=""
[ -n "$_terr" ] && XKB="$(python3 -c "import langtable; k=langtable.list_keyboards(languageId='$_lng', territoryId='$_terr'); print(k[0] if k else '')" 2>/dev/null)"
fi
if [ -n "$XKB" ]; then
XL="${XKB%%(*}"; XL="${XL%% *}" # ch
case "$XKB" in *'('*')') XV="${XKB#*(}"; XV="${XV%)}" ;; *) XV="" ;; esac # fr
install -d /etc/X11/xorg.conf.d
cat > /etc/X11/xorg.conf.d/00-keyboard.conf <<KBEOF
Section "InputClass"
Identifier "system-keyboard"
MatchIsKeyboard "on"
Option "XkbLayout" "$XL"
Option "XkbVariant" "$XV"
EndSection
KBEOF
fi
FWSLANG="$(. /etc/locale.conf 2>/dev/null; printf '%s' "${LANG:-}")"
if [ -n "$FWSLANG" ]; then
_cs="${FWSLANG##*.}"; [ "$_cs" = "$FWSLANG" ] && _cs="UTF-8"
grep -qE "^[[:space:]]*${FWSLANG}[[:space:]]+${_cs}\b" /etc/locale.gen 2>/dev/null \
|| printf '%s %s\n' "$FWSLANG" "$_cs" >> /etc/locale.gen
locale-gen || echo "[FWS] locale-gen a échoué pour $FWSLANG"
fi
# --- 4) Bureau choisi : pacman -S du DE (non bloquant si pas de réseau) -------
DESK="cli"
[ -f /tmp/fws-desktop ] && DESK="$(tr -d '[:space:]' < /tmp/fws-desktop)"
@@ -201,21 +265,311 @@ DM=""
case "$DESK" in
kde) PKGS="plasma-meta sddm konsole dolphin"; DM="sddm" ;;
gnome) PKGS="gnome gdm"; DM="gdm" ;;
xfce) PKGS="xfce4 xfce4-goodies lightdm lightdm-gtk-greeter"; DM="lightdm" ;;
hyprland) PKGS="hyprland kitty wofi waybar sddm"; DM="sddm" ;;
# noto-fonts : KDE l'a via plasma-meta et GNOME a cantarell, mais XFCE et
# Hyprland ne garantissent AUCUNE police TTF → greeter SDDM/apps illisibles.
# Hyprland : la stack des raccourcis FWS (mêmes touches que la config
# i3/sway maison) — dolphin (Super+E), hyprlock (Super+L), grim+slurp+
# wl-clipboard (captures), playerctl (touches multimédia) — plus la
# plomberie de bureau : mako+libnotify (notifications), polkit-gnome
# (agent d'authentification GUI), network-manager-applet (réseau dans le
# tray), xdg-desktop-portal-hyprland (partage d'écran/portails), gvfs
# (montage USB via udisks2), ttf-nerd-fonts-symbols (icônes waybar).
# Son et Bluetooth GARANTIS avec le bureau : la stack PipeWire + le
# mixeur (pavucontrol) + bluez/blueman partent dans la MÊME transaction
# pacman que le DE — si le bureau s'installe, ses gestionnaires aussi
# (fws-setup-hardware les re-propose ensuite en --needed : sans effet
# s'ils sont déjà là, filet de secours sinon).
xfce) PKGS="xfce4 xfce4-goodies sddm noto-fonts pipewire pipewire-pulse wireplumber pavucontrol"; DM="sddm" ;;
hyprland) PKGS="hyprland kitty wofi waybar sddm noto-fonts dolphin kio-extras udisks2 hyprlock grim slurp wl-clipboard playerctl mako libnotify polkit-gnome network-manager-applet xdg-desktop-portal-hyprland ttf-nerd-fonts-symbols pipewire pipewire-alsa pipewire-pulse wireplumber pavucontrol bluez bluez-utils blueman"; DM="sddm" ;;
# i3 : session X11 → COMPATIBLE VR (SteamVR ne marche pas sous Wayland).
# xorg-server est requis (SDDM ne le tire pas) ; maim+xclip = captures X11,
# dunst = notifications X11 (équivalent de mako), i3lock = verrouillage.
# fws-vr-setup (Super+Shift+V ou menu) installe les drivers du casque.
i3) PKGS="i3-wm i3lock xorg-server polybar picom rofi kitty dolphin kio-extras udisks2 sddm noto-fonts ttf-nerd-fonts-symbols maim xclip dunst libnotify polkit-gnome network-manager-applet playerctl brightnessctl pipewire pipewire-alsa pipewire-pulse wireplumber pavucontrol bluez bluez-utils blueman"; DM="sddm" ;;
cli|*) PKGS="" ;;
esac
# Navigateur de base ajouté DÈS QU'un bureau est choisi (jamais en CLI).
# + python-gobject : fenêtre de bienvenue fws-hello (GTK3 vient avec Firefox)
# + pacman-contrib : checkupdates/paccache (indicateur de MàJ + fws-update)
[ -n "$PKGS" ] && PKGS="$PKGS firefox python-gobject pacman-contrib"
if [ -n "$PKGS" ]; then
pacman-key --init &>/dev/null || true
pacman-key --populate archlinux &>/dev/null || true
if pacman -Sy --noconfirm $PKGS; then
# Script de RATTRAPAGE généré avec les valeurs de CE choix : si l'install
# du bureau échoue (réseau/miroirs), l'utilisateur la rejoue en une
# commande depuis le système installé. --needed → inoffensif si déjà fait.
cat > /usr/local/bin/fws-desktop-retry <<RETRYEOF
#!/bin/bash
# Généré par l'installateur FWS — (ré)installe le bureau « $DESK ».
set -u
[ "\$(id -u)" -eq 0 ] || exec sudo "\$0" "\$@"
echo "==> Installation du bureau $DESK…"
pacman -Syy --noconfirm --needed $PKGS || { echo "Échec (réseau ?) — réessayez."; exit 1; }
[ -n "$DM" ] && systemctl enable "$DM"
systemctl set-default graphical.target
rm -f /var/lib/fws/desktop-failed
[ -f /etc/motd ] && grep -q fws-desktop-retry /etc/motd && rm -f /etc/motd
echo "==> Bureau installé ✔ — redémarrez (reboot) pour arriver sur l'écran de connexion."
RETRYEOF
chmod 755 /usr/local/bin/fws-desktop-retry
# Le réseau peut mettre quelques secondes à monter dans l'environnement
# d'installation : on attend la résolution DNS avant le premier essai,
# puis on tente pacman jusqu'à 3 fois (miroir capricieux ≠ échec définitif).
for _i in 1 2 3 4 5 6 7 8 9 10; do
getent hosts geo.mirror.pkgbuild.com >/dev/null 2>&1 && break
sleep 3
done
_DE_OK=""
for _try in 1 2 3; do
pacman -Sy --noconfirm --needed $PKGS && { _DE_OK=1; break; }
echo "[FWS] installation du bureau : tentative $_try échouée — nouvel essai dans 15 s…"
sleep 15
done
if [ -n "$_DE_OK" ]; then
[ -n "$DM" ] && systemctl enable "$DM" || true
systemctl set-default graphical.target || true
# SDDM (KDE / XFCE / Hyprland) : pavé numérique + écran de connexion FWS.
# Le thème « fws » (livré par l'airootfs → recopié par le payload dans
# /usr/share/sddm/themes/fws) est en pur QtQuick/Controls Basic :
# qt6-declarative, dépendance de sddm, suffit → même écran soigné pour
# les 3 DE SANS paquet de thème supplémentaire (Breeze n'existe qu'avec
# plasma). Accent adapté au bureau via theme.conf.user (mécanisme de
# surcharge standard de SDDM, lu par-dessus theme.conf du thème).
if [ "$DM" = "sddm" ]; then
install -d /etc/sddm.conf.d
cat > /etc/sddm.conf.d/10-fws.conf <<'SDDMEOF'
[General]
Numlock=on
[Theme]
Current=fws
SDDMEOF
case "$DESK" in
xfce) _ACCENT="#16a085" ;; # sarcelle (assorti à XFCE)
hyprland) _ACCENT="#89b4fa" ;; # bleu Catppuccin (assorti à Hyprland)
*) _ACCENT="#3daee9" ;; # bleu Breeze (KDE, défaut)
esac
if [ -d /usr/share/sddm/themes/fws ]; then
printf '[General]\naccent=%s\n' "$_ACCENT" \
> /usr/share/sddm/themes/fws/theme.conf.user
else
echo "[FWS] Bureau '$DESK' non installé (réseau ?) — à refaire après le 1er boot."
echo "[FWS] thème SDDM fws introuvable — thème par défaut conservé"
sed -i '/^Current=fws$/d' /etc/sddm.conf.d/10-fws.conf
fi
fi
# GDM (GNOME) : « la même chose » autant que possible — le greeter de
# GDM EST GNOME Shell, il ne se thème pas comme SDDM. On passe par
# dconf (base système « gdm ») : bannière d'accueil FWS, horloge avec
# date complète, pavé numérique allumé — même confort qu'avec SDDM.
if [ "$DM" = "gdm" ]; then
install -d /etc/dconf/profile /etc/dconf/db/gdm.d
cat > /etc/dconf/profile/gdm <<'GDMEOF'
user-db:user
system-db:gdm
file-db:/usr/share/gdm/greeter-dconf-defaults
GDMEOF
cat > /etc/dconf/db/gdm.d/10-fws <<'GDMEOF'
[org/gnome/login-screen]
banner-message-enable=true
banner-message-text='Bienvenue sur FWS'
[org/gnome/desktop/interface]
clock-show-date=true
clock-show-weekday=true
[org/gnome/desktop/peripherals/keyboard]
numlock-state=true
remember-numlock-state=false
GDMEOF
dconf update || echo "[FWS] dconf update a échoué (bannière GDM à refaire)"
fi
# Hyprland en VM : sur GPU virtuel (VMware/VirtualBox) le compositeur
# crashe au lancement → SDDM « remet au login ». On le lance via un
# wrapper qui force le rendu LOGICIEL UNIQUEMENT en VM (systemd-detect-virt)
# ; sur un vrai GPU le rendu matériel est conservé.
if [ "$DESK" = "hyprland" ]; then
cat > /usr/local/bin/fws-hyprland <<'HYEOF'
#!/bin/sh
if systemd-detect-virt --quiet 2>/dev/null; then
# GPU virtuel (VMware vmwgfx…) : on FORCE le chemin d'affichage 100% logiciel.
# La PIÈCE MAÎTRESSE est MESA_LOADER_DRIVER_OVERRIDE=kms_swrast : elle force
# Mesa à allouer le GBM/EGL via kms_swrast (rendu logiciel dans les « dumb
# buffers » KMS) AU LIEU du pilote vmwgfx (qui ne fournit pas le GBM dont
# aquamarine a besoin → « CBackend::create() failed »). + llvmpipe pour le GL.
export MESA_LOADER_DRIVER_OVERRIDE=kms_swrast
export GALLIUM_DRIVER=llvmpipe LIBGL_ALWAYS_SOFTWARE=1
export WLR_RENDERER_ALLOW_SOFTWARE=1 WLR_NO_HARDWARE_CURSORS=1
export AQ_NO_ATOMIC=1
fi
# Hyprland ≥ 0.53 : start-hyprland est le lanceur officiel (prépare la
# session/env systemd) — sans lui, bandeau « started without start-hyprland ».
if command -v start-hyprland >/dev/null 2>&1; then
exec start-hyprland "$@"
fi
exec Hyprland "$@"
HYEOF
chmod 755 /usr/local/bin/fws-hyprland
if [ -f /usr/share/wayland-sessions/hyprland.desktop ]; then
sed -i 's|^Exec=.*|Exec=/usr/local/bin/fws-hyprland|' \
/usr/share/wayland-sessions/hyprland.desktop
fi
# Bluetooth prêt au 1er démarrage (bluez vient d'être installé
# avec le bureau ; fws-setup-hardware le refait — idempotent).
systemctl enable bluetooth.service 2>/dev/null || true
fi
if [ "$DESK" = "i3" ]; then
systemctl enable bluetooth.service 2>/dev/null || true
# Icône du lanceur « Installation VR » (casque) visible dans rofi.
gtk-update-icon-cache -f /usr/share/icons/hicolor 2>/dev/null || true
fi
else
echo "[FWS] Bureau '$DESK' NON installé après 3 tentatives (réseau/miroirs ?)."
echo "[FWS] Rattrapage sur le système installé : sudo fws-desktop-retry"
mkdir -p /var/lib/fws
echo "$DESK" > /var/lib/fws/desktop-failed
# Le premier login (console) explique quoi faire — le motd est retiré
# par fws-desktop-retry une fois le bureau posé.
cat > /etc/motd <<'MOTD'
⚠ FWS : l'installation du bureau a échoué (réseau indisponible pendant
l'installation). Le système fonctionne, mais en console.
Une fois le réseau branché, lancez : sudo fws-desktop-retry
puis redémarrez : le bureau choisi sera installé et activé.
MOTD
fi
fi
# --- 4quater) ADN FWS : détection matériel + drivers + stack « Windows » ------
# Script dédié (NON bloquant ; réseau requis) : drivers GPU détectés via lspci +
# outils invité VM, son/micro/casque (PipeWire), Bluetooth, Podman, et — si un
# bureau est choisi — Steam, Wine, VR (Monado/SteamVR), winboat-bin (apps
# Windows). C'est le cœur « remplacement de Windows » de FWS.
if [ -x /usr/local/bin/fws-setup-hardware ]; then
/usr/local/bin/fws-setup-hardware "$DESK" || true
fi
# --- Config Hyprland (si choisi) : clavier + apps + waybar -------------------
# Hyprland (Wayland) NE lit PAS /etc/X11/xorg.conf.d/00-keyboard.conf : sa
# disposition est dans SA config (input { kb_layout }). Sans config il démarre nu
# (QWERTY US, aucune touche utile). On déploie une config FWS (template +
# clavier XL/XV injecté) dans /etc/skel ET le home de chaque utilisateur déjà
# créé par Anaconda (le %post tourne APRÈS la création de l'utilisateur).
if [ "$DESK" = "hyprland" ] && [ -f /usr/local/share/fws/hyprland.conf.tmpl ]; then
sed -e "s/__KB_LAYOUT__/${XL:-us}/" -e "s/__KB_VARIANT__/${XV}/" \
/usr/local/share/fws/hyprland.conf.tmpl > /tmp/fws-hyprland.conf
install -Dm644 /tmp/fws-hyprland.conf /etc/skel/.config/hypr/hyprland.conf
# Waybar FWS : barre assortie + bouton « ⌨ Raccourcis » (fws-keys,
# aussi sur SUPER+K) qui liste les binds RÉELS chargés par Hyprland
# (hyprctl binds -j) — les descriptions viennent des bindd de la config.
install -Dm644 /usr/local/share/fws/waybar-config.json /etc/skel/.config/waybar/config
install -Dm644 /usr/local/share/fws/waybar-style.css /etc/skel/.config/waybar/style.css
# wofi : style du menu d'applications (Super+R) — le panneau « Raccourcis »
# a son propre style (wofi-keys.css), passé en --style par fws-keys.
install -Dm644 /usr/local/share/fws/wofi-config /etc/skel/.config/wofi/config
install -Dm644 /usr/local/share/fws/wofi-style.css /etc/skel/.config/wofi/style.css
while IFS=: read -r _u _x _uid _g _gec _home _sh; do
[ "$_uid" -ge 1000 ] && [ "$_uid" -le 60000 ] && [ -d "$_home" ] || continue
install -Dm644 /tmp/fws-hyprland.conf "$_home/.config/hypr/hyprland.conf"
install -Dm644 /usr/local/share/fws/waybar-config.json "$_home/.config/waybar/config"
install -Dm644 /usr/local/share/fws/waybar-style.css "$_home/.config/waybar/style.css"
install -Dm644 /usr/local/share/fws/wofi-config "$_home/.config/wofi/config"
install -Dm644 /usr/local/share/fws/wofi-style.css "$_home/.config/wofi/style.css"
chown "$_u": "$_home/.config" 2>/dev/null || true
chown -R "$_u": "$_home/.config/hypr" "$_home/.config/waybar" \
"$_home/.config/wofi" 2>/dev/null || true
done < /etc/passwd
rm -f /tmp/fws-hyprland.conf
fi
# --- Config i3 (si choisi) : raccourcis FWS + barre + autostart -------------
# Pas d'injection clavier ici : les sessions X11 lisent la disposition dans
# /etc/X11/xorg.conf.d/00-keyboard.conf, écrite à l'étape 3bis.
if [ "$DESK" = "i3" ] && [ -f /usr/local/share/fws/i3-config ]; then
install -Dm644 /usr/local/share/fws/i3-config /etc/skel/.config/i3/config
# Polybar (clone de la waybar) + picom (coins arrondis) : même rendu
# visuel que la session Hyprland FWS.
install -Dm644 /usr/local/share/fws/polybar-config.ini /etc/skel/.config/polybar/config.ini
install -Dm644 /usr/local/share/fws/picom.conf /etc/skel/.config/picom.conf
# rofi : menu d'applications au look FWS (équivalent du wofi d'Hyprland).
install -Dm644 /usr/local/share/fws/rofi-config.rasi /etc/skel/.config/rofi/config.rasi
while IFS=: read -r _u _x _uid _g _gec _home _sh; do
[ "$_uid" -ge 1000 ] && [ "$_uid" -le 60000 ] && [ -d "$_home" ] || continue
install -Dm644 /usr/local/share/fws/i3-config "$_home/.config/i3/config"
install -Dm644 /usr/local/share/fws/polybar-config.ini "$_home/.config/polybar/config.ini"
install -Dm644 /usr/local/share/fws/picom.conf "$_home/.config/picom.conf"
install -Dm644 /usr/local/share/fws/rofi-config.rasi "$_home/.config/rofi/config.rasi"
chown "$_u": "$_home/.config" 2>/dev/null || true
chown -R "$_u": "$_home/.config/i3" "$_home/.config/polybar" \
"$_home/.config/rofi" "$_home/.config/picom.conf" 2>/dev/null || true
done < /etc/passwd
fi
# --- 4bis) Sudo : l'utilisateur créé doit pouvoir administrer -----------------
# Arch laisse « %wheel » COMMENTÉ dans sudoers → même un membre de wheel ne peut
# pas sudo. On active le groupe wheel (drop-in) ET on y ajoute tous les
# utilisateurs HUMAINS (UID 1000-60000) créés par Anaconda → ils sont sudoers.
install -d -m 750 /etc/sudoers.d
printf '%%wheel ALL=(ALL:ALL) ALL\n' > /etc/sudoers.d/10-fws-wheel
chmod 440 /etc/sudoers.d/10-fws-wheel
while IFS=: read -r _u _x _uid _rest; do
if [ "$_uid" -ge 1000 ] && [ "$_uid" -le 60000 ]; then
usermod -aG wheel "$_u" 2>/dev/null || true
fi
done < /etc/passwd
# --- 4ter) Branding FWS sur le système installé -------------------------------
# Le hook os-release du build n'est PAS sur la cible et « pacman -S » a pu
# remettre l'os-release d'Arch → d'où « Arch Linux » au menu GRUB, le logo Arch
# sur l'écran de connexion (GDM lit os-release LOGO) et le nom « Arch ». On FORCE
# donc l'os-release FWS (+ lsb-release + GRUB_DISTRIBUTOR), on RÉGÉNÈRE le menu
# GRUB (10_linux lit le NAME d'os-release), et on retire le motd « live ».
if [ -f /usr/local/share/fws/os-release ]; then
cp -f /usr/local/share/fws/os-release /usr/lib/os-release
ln -sf ../usr/lib/os-release /etc/os-release
fi
if [ -f /etc/lsb-release ]; then
sed -i 's/^DISTRIB_ID=.*/DISTRIB_ID=FWS/; s/^DISTRIB_DESCRIPTION=.*/DISTRIB_DESCRIPTION="FWS Linux"/' \
/etc/lsb-release
fi
if grep -q '^GRUB_DISTRIBUTOR=' /etc/default/grub 2>/dev/null; then
sed -i 's/^GRUB_DISTRIBUTOR=.*/GRUB_DISTRIBUTOR="FWS"/' /etc/default/grub
else
echo 'GRUB_DISTRIBUTOR="FWS"' >> /etc/default/grub
fi
grub-mkconfig -o /boot/grub/grub.cfg 2>/dev/null || true
# motd « live » (texte inadapté + codes ANSI bruts affichés crus par GDM) —
# SAUF si c'est notre message « fws-desktop-retry » (échec d'install du
# bureau), qui doit survivre jusqu'au rattrapage.
grep -q fws-desktop-retry /etc/motd 2>/dev/null || rm -f /etc/motd
# --- 4quinquies) Pare-feu par défaut -------------------------------------------
# firewalld est dans l'IMAGE (packages.x86_64) → recopié par le payload,
# activation possible HORS LIGNE. Zone par défaut « public » : entrées
# bloquées, sortant libre — le comportement attendu d'un poste de travail.
# GUI incluse dans le paquet (firewall-config) ; KDE a en plus plasma-firewall.
systemctl enable firewalld.service 2>/dev/null || true
# --- 4quinquies-bis) Horloge juste : synchronisation NTP -----------------------
# Sans NTP l'horloge dérive (TLS/pacman finissent par râler, et un utilisateur
# venant de Windows s'attend à une heure exacte). systemd-timesyncd est dans
# l'image ; on l'ACTIVE explicitement — les symlinks d'activation hérités du
# live étaient des FICHIERS ordinaires (artefact de checkout) que systemd
# ignore (« not a symlink »), corrigés côté live mais on assure ici quoi
# qu'il arrive sur le système installé.
systemctl enable systemd-timesyncd.service 2>/dev/null || true
# NB Récupération : l'entrée GRUB « Réparer FWS » (générée par
# /etc/grub.d/42_fws-recovery lors des grub-mkconfig ci-dessus) boote
# systemd.unit=fws-recovery.target → fws-recovery-boot.service prend tty1 et
# lance l'application de réparation. RIEN à activer : le service n'a pas de
# [Install] (son Conflicts=getty@tty1 ne doit jamais entrer dans un boot
# normal), la target le tire par Requires. Dans le bureau, le lanceur
# « Récupération FWS » (fws-recovery.desktop, polkit) fait le même travail.
# --- 5) Sécurité : verrouiller root s'il est resté SANS mot de passe ----------
# L'airootfs live a souvent un root sans mot de passe ; si l'utilisateur n'en a
# pas défini dans le GUI, on verrouille plutôt que de laisser le compte ouvert.
@@ -223,9 +577,21 @@ if passwd -S root 2>/dev/null | grep -qE '\bNP\b'; then
passwd -l root || true
fi
# --- 6) Le resolv.conf du live ne doit pas rester figé -----------------------
# NetworkManager (activé sur le système installé) régénère /etc/resolv.conf.
rm -f /tmp/fws-desktop /etc/resolv.conf 2>/dev/null || true
# --- 6) DNS du système installé : NetworkManager + systemd-resolved ----------
# Le live utilise systemd-resolved (/etc/resolv.conf → stub 127.0.0.53). L'ANCIEN
# « rm /etc/resolv.conf » comptait sur NM pour recréer le fichier — MAIS avec
# systemd-resolved actif, NM ne réécrit PAS resolv.conf (il pousse les DNS vers
# resolved). Sans le lien, la résolution est MORTE → « Limited Connectivity » /
# Firefox « Server Not Found » (le réseau marche, le DNS non). Fix robuste :
# - resolv.conf → stub systemd-resolved,
# - service systemd-resolved activé,
# - on FORCE NM à utiliser resolved (dns=systemd-resolved) → il lui fournit les
# DNS DHCP, la résolution marche dès le 1er boot.
rm -f /tmp/fws-desktop 2>/dev/null || true
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
systemctl enable systemd-resolved.service 2>/dev/null || true
install -d /etc/NetworkManager/conf.d
printf '[main]\ndns=systemd-resolved\n' > /etc/NetworkManager/conf.d/dns.conf
%end
@@ -0,0 +1,10 @@
[Desktop Entry]
Type=Application
Name=À propos de FWS
Name[en]=About FWS
Comment=Informations sur le projet FWS et sur le système
Exec=fws-about
Icon=help-about
Terminal=false
Categories=System;
Keywords=about;système;projet;info;FWS;
@@ -0,0 +1,10 @@
[Desktop Entry]
Type=Application
Name=Installation VR (FWS)
Name[en]=VR Setup (FWS)
Comment=Installer les drivers/runtimes pour casque VR (HTC Vive, Index, Quest…)
Exec=kitty -e fws-vr-setup
Icon=fws-vr
Terminal=false
Categories=System;Settings;
Keywords=VR;SteamVR;Vive;Index;Quest;casque;
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- FWS — icône « casque VR » (lanceur fws-vr-setup). Palette FWS/Catppuccin. -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
<!-- sangles -->
<path d="M8 28 C1 29 1 41 8 42" fill="none" stroke="#89b4fa" stroke-width="4" stroke-linecap="round"/>
<path d="M56 28 C63 29 63 41 56 42" fill="none" stroke="#89b4fa" stroke-width="4" stroke-linecap="round"/>
<!-- corps du casque avec encoche du nez -->
<path d="M16 18 h32 a10 10 0 0 1 10 10 v10 a10 10 0 0 1 -10 10 h-7
a9 9 0 0 0 -18 0 h-7 a10 10 0 0 1 -10 -10 v-10 a10 10 0 0 1 10 -10 z"
fill="#1e1e2e" stroke="#89b4fa" stroke-width="3" stroke-linejoin="round"/>
<!-- lentilles -->
<circle cx="23" cy="32" r="5.5" fill="#74c7ec"/>
<circle cx="41" cy="32" r="5.5" fill="#74c7ec"/>
<circle cx="21.5" cy="30.5" r="1.8" fill="#cdd6f4"/>
<circle cx="39.5" cy="30.5" r="1.8" fill="#cdd6f4"/>
</svg>

After

Width:  |  Height:  |  Size: 964 B

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
<!--
FWS — autorise fws-recovery-gui (lancée en utilisateur depuis le menu de
l'OS installé) à exécuter le MOTEUR fws-recovery via pkexec.
auth_admin_keep : le mot de passe administrateur n'est demandé qu'une fois
(mémorisé quelques minutes), pas à chaque bouton.
-->
<policyconfig>
<vendor>FWS</vendor>
<action id="org.fws.recovery">
<description>Réparer le système FWS</description>
<description xml:lang="en">Repair the FWS system</description>
<message>Authentification requise pour réparer le système</message>
<message xml:lang="en">Authentication is required to repair the system</message>
<icon_name>fws-recovery</icon_name>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">/usr/local/bin/fws-recovery</annotate>
</action>
</policyconfig>
@@ -0,0 +1,249 @@
// Thème SDDM « fws » — écran de connexion FWS (KDE / XFCE / Hyprland).
//
// VOLONTAIREMENT en pur QtQuick + QtQuick.Controls (style Basic) : ces
// modules arrivent avec qt6-declarative, dépendance de sddm lui-même →
// AUCUN paquet supplémentaire requis (pas de Breeze/plasma-workspace, pas
// de QtGraphicalEffects — supprimé en Qt6). Le greeter fournit en contexte
// les objets sddm, userModel, sessionModel, keyboard et config (theme.conf,
// surchargé par theme.conf.user écrit par le %post : accent selon le DE).
import QtQuick
import QtQuick.Controls.Basic
Rectangle {
id: root
width: 1280
height: 800
readonly property color accent: config.accent || "#3daee9"
readonly property color fieldBg: "#2a2a44"
gradient: Gradient {
GradientStop { position: 0.0; color: config.colorTop || "#1a1a2e" }
GradientStop { position: 1.0; color: config.colorBottom || "#16213e" }
}
Connections {
target: sddm
function onLoginFailed() {
errorLabel.text = "Échec de la connexion — vérifiez le mot de passe"
passwordField.text = ""
passwordField.forceActiveFocus()
}
function onLoginSucceeded() {
errorLabel.text = ""
}
}
function doLogin() {
errorLabel.text = ""
sddm.login(userField.text, passwordField.text, sessionBox.currentIndex)
}
// --- Horloge -----------------------------------------------------------
Timer {
interval: 1000
running: true
repeat: true
onTriggered: {
var now = new Date()
clockLabel.text = Qt.formatTime(now, "hh:mm")
dateLabel.text = Qt.formatDate(now, "dddd d MMMM yyyy")
}
Component.onCompleted: triggered()
}
Column {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: parent.height * 0.10
spacing: 4
Text {
id: clockLabel
anchors.horizontalCenter: parent.horizontalCenter
color: "#f0f0f5"
font.pixelSize: 84
font.weight: Font.Light
}
Text {
id: dateLabel
anchors.horizontalCenter: parent.horizontalCenter
color: "#a0a0b8"
font.pixelSize: 20
font.capitalization: Font.Capitalize
}
}
// --- Carte de connexion --------------------------------------------------
Rectangle {
id: card
anchors.centerIn: parent
anchors.verticalCenterOffset: parent.height * 0.08
width: 400
height: cardContent.implicitHeight + 56
radius: 14
color: "#232338"
border.color: Qt.rgba(1, 1, 1, 0.10)
border.width: 1
Column {
id: cardContent
anchors.centerIn: parent
width: parent.width - 56
spacing: 14
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: config.greeting || "Bienvenue sur FWS"
color: "#f0f0f5"
font.pixelSize: 22
font.weight: Font.Medium
}
Rectangle { width: 44; height: 3; radius: 1.5; color: accent
anchors.horizontalCenter: parent.horizontalCenter }
TextField {
id: userField
width: parent.width
text: userModel.lastUser
placeholderText: "Utilisateur"
placeholderTextColor: "#7d7d95"
color: "#f0f0f5"
font.pixelSize: 15
leftPadding: 14
background: Rectangle {
radius: 8
color: fieldBg
border.color: userField.activeFocus ? accent : "transparent"
border.width: 2
}
onAccepted: passwordField.forceActiveFocus()
}
TextField {
id: passwordField
width: parent.width
placeholderText: "Mot de passe"
placeholderTextColor: "#7d7d95"
color: "#f0f0f5"
font.pixelSize: 15
leftPadding: 14
echoMode: TextInput.Password
focus: true
background: Rectangle {
radius: 8
color: fieldBg
border.color: passwordField.activeFocus ? accent : "transparent"
border.width: 2
}
onAccepted: doLogin()
}
Text {
visible: keyboard.capsLock
anchors.horizontalCenter: parent.horizontalCenter
text: "⚠ Verrouillage majuscules activé"
color: "#e6c07b"
font.pixelSize: 13
}
Text {
id: errorLabel
width: parent.width
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
color: "#e06c75"
font.pixelSize: 13
}
Button {
id: loginButton
width: parent.width
height: 44
onClicked: doLogin()
contentItem: Text {
text: "Se connecter"
color: "#ffffff"
font.pixelSize: 16
font.weight: Font.Medium
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
radius: 8
color: loginButton.down ? Qt.darker(accent, 1.25)
: loginButton.hovered ? Qt.lighter(accent, 1.10)
: accent
}
}
ComboBox {
id: sessionBox
width: parent.width
model: sessionModel
currentIndex: sessionModel.lastIndex
textRole: "name"
font.pixelSize: 13
contentItem: Text {
leftPadding: 14
text: sessionBox.displayText
color: "#a0a0b8"
font.pixelSize: 13
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
radius: 8
color: "transparent"
border.color: Qt.rgba(1, 1, 1, 0.15)
border.width: 1
}
}
}
}
// --- Boutons d'alimentation ----------------------------------------------
// PAS de Repeater avec des fonctions dans un modèle JS : en Qt6 elles ne
// survivent pas toujours à la conversion QVariant (clic silencieusement
// inopérant). Deux boutons explicites via un composant inline.
component PowerButton: Button {
id: powerButton
property string label: ""
height: 36
contentItem: Text {
text: powerButton.label
color: powerButton.hovered ? "#f0f0f5" : "#a0a0b8"
font.pixelSize: 14
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
leftPadding: 14
rightPadding: 14
}
background: Rectangle {
radius: 8
color: powerButton.hovered ? Qt.rgba(1, 1, 1, 0.08) : "transparent"
border.color: Qt.rgba(1, 1, 1, 0.15)
border.width: 1
}
}
Row {
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 24
spacing: 10
PowerButton { label: "Redémarrer"; onClicked: sddm.reboot() }
PowerButton { label: "Éteindre"; onClicked: sddm.powerOff() }
}
// Nom de la machine, discret, en bas à gauche.
Text {
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.margins: 24
text: sddm.hostName
color: "#5d5d78"
font.pixelSize: 13
}
}
@@ -0,0 +1,17 @@
[SddmGreeterTheme]
Name=FWS
Description=Écran de connexion FWS
Author=FWS
Copyright=FWS
License=GPL-2.0-or-later
Type=sddm-theme
Version=1.0
Website=
Screenshot=
MainScript=Main.qml
ConfigFile=theme.conf
TranslationsDirectory=
Email=
Theme-Id=fws
Theme-API=2.0
QtVersion=6
@@ -0,0 +1,7 @@
# Thème SDDM « fws » — valeurs par défaut, surchargées par theme.conf.user
# (écrit par le %post du kickstart : accent différent selon le bureau choisi).
[General]
accent=#3daee9
colorTop=#1a1a2e
colorBottom=#16213e
greeting=Bienvenue sur FWS
@@ -2,4 +2,4 @@ title FWS install medium (%ARCH%, UEFI)
sort-key 01
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID%
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% udev.event_timeout=45
@@ -0,0 +1,8 @@
# Mode graphique de secours : SANS KMS (nomodeset). Filet de sécurité
# standard pour les GPU/hyperviseurs dont le pilote KMS pose problème :
# X démarre alors sur le framebuffer VESA/EFI (plus lent mais fiable).
title FWS install medium (%ARCH%, UEFI, safe graphics)
sort-key 01b
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% nomodeset udev.event_timeout=45
@@ -0,0 +1,9 @@
# « Réparer FWS » : boote le live SANS installateur et lance l'assistant de
# réparation en console (fws-recovery, via le hook fws.recovery=1 du
# .zprofile) : réinstaller GRUB, régénérer l'initramfs, réparer pacman,
# shell chroot — le point de restauration FWS.
title FWS - Reparer une installation (recovery)
sort-key 01d
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% udev.event_timeout=45 fws.recovery=1
@@ -2,4 +2,4 @@ title FWS install medium (%ARCH%, UEFI) with speech
sort-key 02
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on udev.event_timeout=45
+7
View File
@@ -42,6 +42,7 @@ mdadm
parted
gptfdisk
fatresize
firewalld
testdisk
smartmontools
nvme-cli
@@ -87,6 +88,8 @@ sof-firmware
# --- Xorg + Openbox (session graphique de l'installateur) ---
xorg-server
zenity
zram-generator
xorg-xinit
xorg-xrandr
xorg-xsetroot
@@ -114,3 +117,7 @@ python-crypt_r
# Namespace GI 'OSTree-1.0' : pyanaconda/rescue.py importe le payload rpm_ostree
# (inconditionnellement, même si FWS utilise liveimg) → fourni par 'ostree'.
ostree
# Terminal X : debug de l'installateur + entrée « Terminals » du menu Openbox.
xterm
# Gestionnaire D&D 5e (dés, personnages, grimoire, sessions, combat).
fws-dnd
+13
View File
@@ -24,4 +24,17 @@ file_permissions=(
["/usr/local/bin/livecd-sound"]="0:0:755"
["/usr/bin/load_policy"]="0:0:755"
["/usr/bin/setstatus"]="0:0:755"
["/usr/local/bin/fws-setup-hardware"]="0:0:755"
["/usr/local/bin/fws-keys"]="0:0:755"
["/usr/local/bin/fws-vr-setup"]="0:0:755"
["/usr/local/bin/fws-hello"]="0:0:755"
["/usr/local/bin/fws-update"]="0:0:755"
["/usr/local/bin/fws-updates-check"]="0:0:755"
["/usr/local/bin/fws-proton-ge"]="0:0:755"
["/usr/local/bin/fws-recovery"]="0:0:755"
["/usr/local/bin/fws-recovery-session"]="0:0:755"
["/usr/local/bin/fws-recovery-gui"]="0:0:755"
["/usr/local/bin/fws-powermenu"]="0:0:755"
["/usr/local/bin/fws-about"]="0:0:755"
["/etc/grub.d/42_fws-recovery"]="0:0:755"
)
+31 -2
View File
@@ -6,7 +6,36 @@ ENDTEXT
MENU LABEL FWS install medium (%ARCH%, BIOS)
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID%
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% udev.event_timeout=45
# Mode graphique de secours : SANS KMS (nomodeset). Filet de sécurité
# standard pour les GPU/hyperviseurs dont le pilote KMS pose problème :
# X démarre alors sur le framebuffer VESA/EFI (plus lent mais fiable).
# NB : la lenteur au démarrage vue en VM VMware (2-4 min d'écran bleu)
# N'EST PAS un gel graphique — c'est un worker udev tué après 180 s ;
# le boot normal finit par afficher l'installateur (splash ajouté).
LABEL fwsnomodeset
TEXT HELP
Boot FWS WITHOUT kernel mode setting (safe graphics).
Use this if the normal boot freezes on a black/blank screen
(e.g. VMware Workstation vmwgfx hang).
ENDTEXT
MENU LABEL FWS install medium (%ARCH%, BIOS, ^safe graphics)
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% nomodeset udev.event_timeout=45
# « Réparer FWS » : live sans installateur → assistant de réparation console
# (fws-recovery via le hook fws.recovery=1 du .zprofile).
LABEL fwsrecovery
TEXT HELP
Repair an existing FWS installation (GRUB, initramfs, pacman)
without reinstalling. Boots to a guided console assistant.
ENDTEXT
MENU LABEL FWS - ^Repair installation (recovery)
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% udev.event_timeout=45 fws.recovery=1
# Accessibility boot option
LABEL fwsspeech
@@ -17,4 +46,4 @@ ENDTEXT
MENU LABEL FWS install medium (%ARCH%, BIOS) with ^speech
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on udev.event_timeout=45
+1 -1
View File
@@ -1,6 +1,6 @@
INCLUDE archiso_head.cfg
DEFAULT arch
DEFAULT fws
TIMEOUT 150
INCLUDE archiso_sys-linux.cfg
Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 14 KiB

+24
View File
@@ -6,6 +6,30 @@ Ce document décrit **l'environnement exact** sur lequel le build FWS fonctionne
---
## 0. Build natif Arch Linux — la référence actuelle
Depuis juillet 2026, l'environnement de référence est un **Arch Linux natif**
(même machine, sans Windows/WSL) : `build.sh` et `setup-aur.sh` détectent le
cas et s'exécutent directement. Les ISO récentes du dépôt sont produites ainsi.
| Composant | Valeur de référence |
|---|---|
| OS | Arch Linux (rolling), kernel `7.x-archN` |
| Paquets requis | `base-devel`, `git`, `archiso`, `dos2unix` (`pacman -S --needed …`) |
| Droits | `sudo` (mkarchiso et pacstrap exigent root) |
| Réseau | requis (pacstrap télécharge les paquets ; `setup-aur.sh` clone/compile l'AUR) |
```bash
sudo ./setup-aur.sh # paquets AUR/anaconda → local-repo/
sudo ./build.sh # mkarchiso → out/FWS-<date>-x86_64.iso
```
Le reste de ce document décrit le chemin **Windows 11 + WSL Arch**, toujours
fonctionnel (bascule automatique depuis Git Bash) mais qui n'est plus la
référence.
---
## 1. Matériel (host Windows)
| Composant | Valeur de référence | Minimum acceptable | Pourquoi |
+102 -1
View File
@@ -25,7 +25,7 @@
# ============================================================================
pkgname=anaconda
pkgver=45.8
pkgrel=2
pkgrel=4
pkgdesc="The Anaconda installer (porté sur Arch pour FWS, payload liveimg)"
arch=('x86_64')
url="https://github.com/rhinstaller/anaconda"
@@ -57,6 +57,107 @@ source=("anaconda-$pkgver.tar.gz::https://github.com/rhinstaller/anaconda/archiv
sha256sums=('5ef1464bff7f28a4a09a821cbb0121f09ef504ec169288d742373f86807ec4ed')
_srcdir="anaconda-anaconda-$pkgver"
prepare() {
cd "$srcdir/$_srcdir"
# ── FWS : clavier par défaut selon le TERRITOIRE de la locale ──────────────
# set_layouts() amont fait « locale = get_language_id(self.language) » →
# get_language_id JETTE le territoire (fr_CH.UTF-8 → 'fr') → list_keyboards('fr')
# = 'fr(oss)' (France) au lieu de 'ch(fr)' (Suisse). On passe la LOCALE COMPLÈTE
# (self.language) : langtable.list_keyboards('fr_CH.UTF-8') = 'ch(fr)'. La
# validation l'accepte (is_valid_langcode → parse_locale().language non vide).
# → le clavier du GUI ET du système installé suivent enfin la locale choisie.
sed -i \
's/^\(\s*\)locale = get_language_id(self\.language)/\1locale = self.language # FWS: garder le territoire (fr_CH -> ch(fr))/' \
pyanaconda/modules/localization/localization.py
# Garde-fou : casse le build si le motif amont a changé (le patch silencieux).
grep -q 'locale = self.language # FWS' \
pyanaconda/modules/localization/localization.py
# ── FWS : champ « Nom de machine » dans le spoke Utilisateur ───────────────
# Anaconda ne propage PAS le hostname (le champ du spoke Réseau est perdu chez
# nous → /etc/hostname vide). On ajoute donc un champ Hostname à l'onglet de
# création de compte (ligne 2 de la grille, libre) ; user.py écrit la valeur
# dans /tmp/fws-hostname, que le %post du kickstart applique à /etc/hostname.
python3 - <<'PYEOF'
import glob
# 1) user.glade : insérer label + entry "Host name" APRÈS le bloc username_entry.
glades = [p for p in glob.glob("pyanaconda/**/user.glade", recursive=True)
if 'username_entry' in open(p, encoding='utf-8').read()]
assert glades, "FWS: user.glade introuvable"
g = glades[0]
s = open(g, encoding='utf-8').read()
assert 'id="hostname_entry"' not in s, "FWS: glade déjà patché"
i = s.index('id="username_entry"')
k = s.index('</packing>', i) # fin du packing de username_entry
j = s.index('</child>', k) + len('</child>') # fin du <child> EXTERNE (pas l'accessible)
block = '''
<child>
<object class="GtkLabel" id="hostname_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="xpad">10</property>
<property name="label" translatable="yes" context="GUI|User">_Host name</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">hostname_entry</property>
<property name="xalign">1</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="hostname_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="width-chars">50</property>
<child internal-child="accessible">
<object class="AtkObject" id="hostname_entry-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">Host Name</property>
</object>
</child>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>'''
open(g, 'w', encoding='utf-8').write(s[:j] + block + s[j:])
# 2) user.py (GUI) : récupérer le widget + écrire /tmp/fws-hostname dans apply().
pys = glob.glob("pyanaconda/ui/gui/spokes/user.py")
assert pys, "FWS: user.py introuvable"
p = pys[0]
s = open(p, encoding='utf-8').read()
anchor = 'self._username_entry = self.builder.get_object("username_entry")'
assert anchor in s, "FWS: ancre username_entry absente"
s = s.replace(anchor,
anchor + '\n self._hostname_entry = self.builder.get_object("hostname_entry")',
1)
ap = 'set_user_list(self._users_module, self._user_list, remove_unset=True)'
assert ap in s, "FWS: ancre apply() absente"
s = s.replace(ap, ap + '''
# FWS : mémorise le nom de machine saisi (cible pas encore montée) pour
# le %post du kickstart, qui l'écrira dans /etc/hostname.
try:
_hn = self._hostname_entry.get_text().strip()
except Exception:
_hn = ""
try:
with open("/tmp/fws-hostname", "w") as _f:
_f.write(_hn)
except OSError:
pass''', 1)
open(p, 'w', encoding='utf-8').write(s)
print("FWS: champ Hostname ajouté au spoke Utilisateur (glade + user.py)")
PYEOF
}
build() {
cd "$srcdir/$_srcdir"
# Glade désactivé (catalogue designer, inutile au runtime) → pas de dép glade.
+19 -1
View File
@@ -5,7 +5,7 @@
# l'arborescence à la main plutôt que de patcher la chaîne distutils.
pkgname=python-meh
pkgver=0.43
pkgrel=1
pkgrel=2
pkgdesc="Python exception handling library (dépendance Anaconda)"
arch=('any')
url="https://github.com/rhinstaller/python-meh"
@@ -16,6 +16,24 @@ source=("$pkgname-$pkgver.tar.gz::https://github.com/rhinstaller/python-meh/arch
sha256sums=('cd50301794ab68a064715f1ace960f6de07aea809aa85f73087d44573c98be52')
_srcdir="python-meh-$_tag"
prepare() {
cd "$srcdir/$_srcdir"
# gettext.ldgettext a été SUPPRIMÉ en Python 3.11 : sans ce remplacement, le
# gestionnaire d'exceptions d'Anaconda plante LUI-MÊME (AttributeError) dès
# qu'il doit AFFICHER une erreur → traceback brut au lieu du dialogue meh,
# en GUI comme en TUI. dgettext est l'équivalent direct (ldgettext n'était
# que la variante bytes). Corrigé en amont après 0.43.
sed -i 's/gettext\.ldgettext(/gettext.dgettext(/' \
meh/handler.py meh/ui/text.py meh/ui/gui.py
# .pyc d'une extraction précédente (src/ n'est pas nettoyé par makepkg -f) :
# à jeter, sinon ils partiraient dans le paquet via cp -a.
find meh -type d -name __pycache__ -exec rm -rf {} +
if grep -rn --include='*.py' 'ldgettext' meh/; then
echo "ERREUR : ldgettext encore présent après patch" >&2
return 1
fi
}
package() {
cd "$srcdir/$_srcdir"
local sitedir
+26 -5
View File
@@ -114,7 +114,10 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
"${SUDO[@]}" podman pull "$IMAGE"
info "Relance dans le conteneur (setup-aur.sh)…"
"${SUDO[@]}" podman run --rm "${TTY[@]}" --privileged \
# --network=host : le bridge podman ne route plus vers l'extérieur sur
# certains réseaux (firewalld/NAT, → « Resolving timed out »). On partage la
# pile réseau de l'hôte (internet + DNS OK) pour les pacman/makepkg/AUR.
"${SUDO[@]}" podman run --rm "${TTY[@]}" --network=host --privileged \
-e FWS_IN_CONTAINER=1 \
-v "$REPO_DIR":"$REPO_DIR" \
-v fws-pacman-cache:/var/cache/pacman/pkg \
@@ -229,11 +232,14 @@ repo_has() {
# Publie les .pkg d'un build vers local-repo en purgeant d'abord les anciennes
# versions de CE paquet (+ son sous-paquet -debug). Le « -[0-9] » empêche
# « anaconda » d'attraper « anaconda-widgets » / « anaconda-debug ».
# Prend la LISTE EXPLICITE des fichiers à publier (pas un glob sur le dossier :
# un .pkg d'un build précédent peut traîner dans $BUILD_DIR/<name> et serait
# republié par-dessus la purge).
publish_to_repo() {
local name="$1" d="$2"
local name="$1"; shift
rm -f "$LOCAL_REPO/$name"-[0-9]*.pkg.tar.* \
"$LOCAL_REPO/$name"-debug-[0-9]*.pkg.tar.* 2>/dev/null || true
cp -f "$d"/*.pkg.tar.* "$LOCAL_REPO/" || error "Copie de $name vers local-repo échouée"
cp -f "$@" "$LOCAL_REPO/" || error "Copie de $name vers local-repo échouée"
}
# Construit OU saute <name> (source déjà déposée dans $BUILD_DIR/<name>).
@@ -256,8 +262,23 @@ build_or_skip() {
chown -R "$BUILD_USER:$BUILD_USER" "$d"
su - "$BUILD_USER" -c "cd '$d' && makepkg -sf --noconfirm" \
|| error "Build $name échoué"
pacman -U --noconfirm "$d/"*.pkg.tar.* || error "Install de $name échouée"
publish_to_repo "$name" "$d"
# N'installer/publier QUE les paquets produits par CE PKGBUILD dans SA
# version courante (makepkg --packagelist), jamais un glob "$d/"* : après
# un bump de pkgrel, l'ancien .pkg traîne encore dans $d (BUILD_DIR n'est
# pas nettoyé entre les runs) → pacman -U recevait 2× le même nom de
# paquet (« erreur : cible répétée ») et la vieille version repartait
# dans local-repo. Le grep filtre tout bruit éventuel de su - (PAM…).
# NB : --packagelist liste aussi un -debug qui n'est PAS toujours produit
# (paquet pur python : rien à stripper) → ne garder que les fichiers
# réellement présents sur disque.
local -a pkgfiles=() existing=()
local f
mapfile -t pkgfiles < <(su - "$BUILD_USER" -c "cd '$d' && makepkg --packagelist" \
| grep -E '\.pkg\.tar\.')
for f in "${pkgfiles[@]}"; do [ -e "$f" ] && existing+=("$f"); done
[ "${#existing[@]}" -gt 0 ] || error "Aucun paquet produit trouvé pour $name"
pacman -U --noconfirm "${existing[@]}" || error "Install de $name échouée"
publish_to_repo "$name" "${existing[@]}"
success "$name build + install OK"
}