From bfec60848d1c2c36fd9a0894f5e00e55ecf89939 Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Fri, 2 Oct 2026 10:20:52 +0100 Subject: [PATCH] Use Rust 1.95 consistently throughout the workspace --- Cargo.toml | 2 +- crates/ferese-config/Cargo.toml | 2 +- crates/ferese-theme/Cargo.toml | 2 +- docs/installation.md | 120 +++++++++++++++++---------- portal/Cargo.toml | 2 +- rust-toolchain.toml | 4 + rustfmt.toml | 2 - shell/ferese-lock/Cargo.toml | 2 +- shell/ferese-lock/src/main.rs | 114 +++++++++++++------------ shell/ferese-polkit-agent/Cargo.toml | 2 +- shell/ferese-settings/Cargo.toml | 2 +- shell/ferese-shell/Cargo.toml | 2 +- 12 files changed, 146 insertions(+), 110 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/Cargo.toml b/Cargo.toml index 6162875..8dd2f74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ resolver = "3" version = "0.1.0" edition = "2024" license = "MIT" -rust-version = "1.98" +rust-version = "1.95" [workspace.dependencies] bitflags = "2.9.4" diff --git a/crates/ferese-config/Cargo.toml b/crates/ferese-config/Cargo.toml index 5a8eb77..1490f75 100644 --- a/crates/ferese-config/Cargo.toml +++ b/crates/ferese-config/Cargo.toml @@ -3,7 +3,7 @@ name = "ferese-config" version.workspace = true edition.workspace = true license.workspace = true -rust-version = "1.95" +rust-version.workspace = true [dependencies] jiff.workspace = true diff --git a/crates/ferese-theme/Cargo.toml b/crates/ferese-theme/Cargo.toml index 786622c..2fe753a 100644 --- a/crates/ferese-theme/Cargo.toml +++ b/crates/ferese-theme/Cargo.toml @@ -3,7 +3,7 @@ name = "ferese-theme" version.workspace = true edition.workspace = true license.workspace = true -rust-version = "1.95" +rust-version.workspace = true [dependencies] jiff.workspace = true diff --git a/docs/installation.md b/docs/installation.md index fdd75f3..2f2ee08 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -9,14 +9,14 @@ For a full session, you’ll need a Wayland-capable graphics driver and a local session with seat access through logind or seatd. A nested preview runs inside your existing Wayland desktop. -To build Ferese, install **Rust 1.95 or newer**, a C/C++ toolchain, CMake, pkg-config -and the libraries below. The installer builds the Ferese components once those system -packages are in place. +To build Ferese, install **Rust 1.95.0**, a C/C++ toolchain, CMake, pkg-config, +Python 3.9 or newer, and the libraries below. Once those packages are installed, +the installer builds the Ferese components. ### Fedora ```sh -sudo dnf install git curl gcc gcc-c++ make cmake pkgconf-pkg-config \ +sudo dnf install python3 git curl gcc gcc-c++ make cmake pkgconf-pkg-config \ wayland-devel libxkbcommon-devel libinput-devel systemd-devel libseat-devel \ mesa-libgbm-devel mesa-libEGL-devel libdrm-devel fontconfig-devel \ freetype-devel expat-devel dbus-daemon dbus-tools desktop-file-utils foot pam \ @@ -28,7 +28,7 @@ sudo dnf install git curl gcc gcc-c++ make cmake pkgconf-pkg-config \ ### Arch Linux ```sh -sudo pacman -S --needed base-devel git curl cmake pkgconf wayland libxkbcommon \ +sudo pacman -S --needed python base-devel git curl cmake pkgconf wayland libxkbcommon \ libinput systemd seatd mesa libdrm fontconfig freetype2 expat dbus \ desktop-file-utils foot pam clang pipewire libpipewire wireplumber polkit \ xdg-desktop-portal xdg-desktop-portal-gtk \ @@ -39,7 +39,7 @@ sudo pacman -S --needed base-devel git curl cmake pkgconf wayland libxkbcommon \ ```sh sudo apt update -sudo apt install build-essential git curl cmake pkg-config libwayland-dev \ +sudo apt install python3 build-essential git curl cmake pkg-config libwayland-dev \ libxkbcommon-dev libinput-dev libudev-dev libseat-dev libgbm-dev libegl-dev \ libdrm-dev libfontconfig1-dev libfreetype-dev libexpat1-dev dbus-bin \ dbus-user-session desktop-file-utils foot libpam0g clang libclang-dev \ @@ -56,22 +56,17 @@ libraries. ### Rust toolchain -Check your toolchain: +Install Rust through [rustup.rs](https://rustup.rs/). The checkout's +`rust-toolchain.toml` selects the required compiler and rustfmt automatically. +All workspace crates inherit their minimum Rust version from the root `Cargo.toml`. + +From inside the checkout, verify the selected toolchain: ```sh -rustc --version +rustup show active-toolchain cargo --version ``` -If you don’t have Rust yet, follow the instructions at [rustup.rs](https://rustup.rs/) -and reopen your terminal, then select the required toolchain for this checkout after -cloning: - -```sh -rustup toolchain install 1.95.0 -rustup override set 1.95.0 -``` - ## Build and install Run these commands as your normal user: @@ -233,11 +228,22 @@ git pull --ff-only Log out and back in to use the new release. A running session keeps its existing processes and release paths until it restarts. -Each release records the portal configuration it ships so later upgrades can identify -it. Installation stops if it encounters modified or untracked administrator files. -To replace Ferese portal configuration from an earlier development install, build first -and run `./scripts/install.sh --skip-build --replace-portal-config`, which backs up the -existing files in the new release’s `portal-config.previous/` directory. +The installer prepares a complete release bundle before asking for administrator +access. Its manifest records the source commit, dirty-tree status, build features, +file modes and SHA-256 checksums. The installer verifies the bundle again after +copying it into a root-owned staging directory. These checks detect changed or incomplete +bundles; they are not a publisher signature. + +The installer reports system installation and user setup separately. If user setup +fails after installation, retry it as your normal user: + +```sh +python3 scripts/installer/install.py user-setup +``` + +This backs up the exact old development portal override when present and reloads +user systemd configuration. It preserves custom overrides and does not restart the +running desktop or portal. Installer options: @@ -245,17 +251,39 @@ Installer options: ./scripts/install.sh --dry-run ./scripts/install.sh --offline --release-id my-build ./scripts/install.sh --resize-metrics -./scripts/install.sh --skip-build +./scripts/install.sh --bundle-only ./target/bundles/my-build --release-id my-build +./scripts/install.sh --bundle ./target/bundles/my-build ``` -Use `--dry-run` to see the planned commands, `--offline` when dependencies are already -cached, or `--skip-build` after building every component from the revision you want to -install. The latter uses binaries already in `target/release/`, and -`./scripts/install.sh --help` lists all available options. +`--bundle` replaces `--skip-build` and installs a complete bundle instead of loose +files in `target/release/`. Build options cannot be combined with `--bundle`. +The front-end dry run prints commands. To inspect the filesystem changes planned +for an existing bundle, run: -Use `--resize-metrics` to enable resize-barrier counters and duration histograms in -the release build. Ferese prints the measurements to its session log on normal exit. -This option requires building and cannot be combined with `--skip-build`. +```sh +./scripts/install-session.sh install ./target/bundles/my-build --dry-run +``` + +Modified administrator files stop installation before activation. To explicitly +replace Ferese's portal definition and preferences, use +`./scripts/install.sh --bundle ./target/bundles/my-build --replace-portal-config`. +Transaction backups live under `/usr/local/lib/ferese/transactions/`; each +`journal.json` maps the numbered backups to their original paths. Existing PAM +policies are preserved. + +A lock prevents concurrent installations. The installer records recovery data +before changing system files, then updates integration files and switches `current` +last. If an operation fails, it restores the previous files. After an interruption, +recovery runs on the next install, or you can run it yourself: + +```sh +sudo ./scripts/install-session.sh recover +``` + +Recovery stops if a recorded file was subsequently changed by an administrator. +Inspect the conflict and the retained `.transaction/journal.json` before restoring +the recorded old or new contents and retrying. Multi-file installation is +recoverable, not a single atomic filesystem operation. ## Logout and recovery @@ -274,30 +302,30 @@ then choose another desktop at the next login. ### Roll back a release -While Ferese is stopped, inspect the retained releases: +Stop the Ferese session, then run from the checkout: ```sh -readlink /usr/local/lib/ferese/current -readlink /usr/local/lib/ferese/previous -ls /usr/local/lib/ferese/releases/ +sudo ./scripts/install-session.sh rollback +# Or select a retained bundle by its release ID: +sudo ./scripts/install-session.sh rollback my-build ``` -If `previous` exists and points to the release you want, switch back: +Rollback restores both the binaries and that release's session, systemd, portal +and icon files. It refuses to overwrite modified administrator files and preserves +existing PAM policy. Log in again to use the selected release. After rollback, +`previous` points to the release you just left. + +Releases installed before bundle manifests can be upgraded using their retained +ownership records, but cannot be selected by this rollback command. Keep the old +release directory until you have validated the first bundled installation. + +To remove managed commands and desktop integration while retaining release bundles, +transaction backups, PAM policy and user configuration: ```sh -sudo sh -eu -c ' - cd /usr/local/lib/ferese - previous_release=$(readlink previous) - test -x "$previous_release/ferese-session" - ln -sfn -- "$previous_release" current-rollback - mv -Tf -- current-rollback current -' +sudo ./scripts/install-session.sh uninstall ``` -Log in again to use the selected binaries, with your config and PAM policy still in -place. If you also need to recover a configuration edit, Settings keeps a backup when -saving, as described in [Saving and validation](configuration.md#saving-and-validation). - ### Remove the login option To hide Ferese from login managers while keeping its releases and your config: diff --git a/portal/Cargo.toml b/portal/Cargo.toml index b82ef64..0f33b49 100644 --- a/portal/Cargo.toml +++ b/portal/Cargo.toml @@ -3,7 +3,7 @@ name = "xdg-desktop-portal-ferese" version.workspace = true edition.workspace = true license.workspace = true -rust-version = "1.95" +rust-version.workspace = true [[bin]] name = "xdg-desktop-portal-ferese" diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5a30785 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.95.0" +profile = "minimal" +components = ["rustfmt"] diff --git a/rustfmt.toml b/rustfmt.toml index 31bf2c9..237a52a 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,2 @@ max_width = 120 -imports_granularity = "Module" -group_imports = "StdExternalCrate" reorder_imports = true diff --git a/shell/ferese-lock/Cargo.toml b/shell/ferese-lock/Cargo.toml index 047ddb0..7a9a551 100644 --- a/shell/ferese-lock/Cargo.toml +++ b/shell/ferese-lock/Cargo.toml @@ -3,7 +3,7 @@ name = "ferese-lock" version.workspace = true edition.workspace = true license.workspace = true -rust-version = "1.95" +rust-version.workspace = true [dependencies] ferese-ipc = { path = "../../crates/ferese-ipc" } diff --git a/shell/ferese-lock/src/main.rs b/shell/ferese-lock/src/main.rs index 455eaf4..a98a58d 100644 --- a/shell/ferese-lock/src/main.rs +++ b/shell/ferese-lock/src/main.rs @@ -1,5 +1,3 @@ -#![feature(box_patterns)] - mod appearance; mod auth; mod runtime; @@ -264,9 +262,7 @@ impl cosmic::Application for Locker { } } } - Message::Event(box Event::Keyboard(iced::keyboard::Event::ModifiersChanged(modifiers))) => { - self.caps_lock = modifiers.contains(iced::keyboard::Modifiers::CAPS_LOCK); - } + Message::Event(event) => return self.handle_event(*event), Message::Tick => { let now = jiff::Zoned::now(); self.clock = now.strftime(self.appearance.clock_format()).to_string(); @@ -315,56 +311,8 @@ impl cosmic::Application for Locker { self.status = "Password not accepted. Try again."; return focus(); } - Message::Event(box Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::Output( - event, - output, - )))) if !self.preview => { - if matches!(event, wayland::OutputEvent::Removed) { - if let Some(id) = self.outputs.remove(&output) { - return session_lock::destroy_lock_surface(id); - } - } else if !self.outputs.contains_key(&output) { - let id = window::Id::unique(); - self.outputs.insert(output.clone(), id); - if self.started { - return session_lock::get_lock_surface(id, output); - } - } - } - Message::Event(box Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::SessionLock( - event, - )))) if !self.preview => match event { - wayland::SessionLockEvent::Locked => { - if self.confirmation.observe() { - use std::io::Write; - if std::env::var_os("FERESE_LOCK_READY").is_some() { - let _ = writeln!(std::io::stdout().lock(), "FERESE_LOCKED"); - } - } - if !self.started { - self.started = true; - return Task::batch( - self.outputs - .iter() - .map(|(output, id)| session_lock::get_lock_surface(*id, output.clone())), - ); - } - } - wayland::SessionLockEvent::Focused(..) => return focus(), - wayland::SessionLockEvent::Unlocked if self.state == AuthState::Unlocking => { - return iced::exit(); - } - wayland::SessionLockEvent::Finished | wayland::SessionLockEvent::NotSupported => { - eprintln!("ferese-lock: compositor refused the lock"); - return iced::exit(); - } - _ => {} - }, - Message::Event(box Event::Window(window::Event::Opened { .. })) => { - return focus(); - } - _ => {} } + Task::none() } @@ -387,6 +335,64 @@ fn focus() -> Task { } impl Locker { + fn handle_event(&mut self, event: Event) -> Task { + match event { + Event::Keyboard(iced::keyboard::Event::ModifiersChanged(modifiers)) => { + self.caps_lock = modifiers.contains(iced::keyboard::Modifiers::CAPS_LOCK); + } + Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::Output(event, output))) + if !self.preview => + { + if matches!(event, wayland::OutputEvent::Removed) { + if let Some(id) = self.outputs.remove(&output) { + return session_lock::destroy_lock_surface(id); + } + } else if !self.outputs.contains_key(&output) { + let id = window::Id::unique(); + self.outputs.insert(output.clone(), id); + if self.started { + return session_lock::get_lock_surface(id, output); + } + } + } + Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::SessionLock(event))) if !self.preview => { + match event { + wayland::SessionLockEvent::Locked => { + if self.confirmation.observe() { + use std::io::Write; + if std::env::var_os("FERESE_LOCK_READY").is_some() { + let _ = writeln!(std::io::stdout().lock(), "FERESE_LOCKED"); + } + } + if !self.started { + self.started = true; + return Task::batch( + self.outputs + .iter() + .map(|(output, id)| session_lock::get_lock_surface(*id, output.clone())), + ); + } + } + wayland::SessionLockEvent::Focused(..) => return focus(), + wayland::SessionLockEvent::Unlocked if self.state == AuthState::Unlocking => { + return iced::exit(); + } + wayland::SessionLockEvent::Finished | wayland::SessionLockEvent::NotSupported => { + eprintln!("ferese-lock: compositor refused the lock"); + return iced::exit(); + } + _ => {} + } + } + Event::Window(window::Event::Opened { .. }) => { + return focus(); + } + _ => {} + } + + Task::none() + } + fn label<'a>( &self, label: impl Into> + 'a, diff --git a/shell/ferese-polkit-agent/Cargo.toml b/shell/ferese-polkit-agent/Cargo.toml index 6b247f1..713ddba 100644 --- a/shell/ferese-polkit-agent/Cargo.toml +++ b/shell/ferese-polkit-agent/Cargo.toml @@ -3,7 +3,7 @@ name = "ferese-polkit-agent" version.workspace = true edition.workspace = true license.workspace = true -rust-version = "1.95" +rust-version.workspace = true [dependencies] ferese-config = { path = "../../crates/ferese-config" } diff --git a/shell/ferese-settings/Cargo.toml b/shell/ferese-settings/Cargo.toml index 647104f..61a956c 100644 --- a/shell/ferese-settings/Cargo.toml +++ b/shell/ferese-settings/Cargo.toml @@ -3,7 +3,7 @@ name = "ferese-settings" version.workspace = true edition.workspace = true license.workspace = true -rust-version = "1.93" +rust-version.workspace = true [dependencies] ferese-theme = { path = "../../crates/ferese-theme" } diff --git a/shell/ferese-shell/Cargo.toml b/shell/ferese-shell/Cargo.toml index 88cbd79..0be6db2 100644 --- a/shell/ferese-shell/Cargo.toml +++ b/shell/ferese-shell/Cargo.toml @@ -3,7 +3,7 @@ name = "ferese-shell" version.workspace = true edition.workspace = true license.workspace = true -rust-version = "1.93" +rust-version.workspace = true [dependencies] ferese-ipc = { path = "../../crates/ferese-ipc" }