feat: complete wayland desktop deployment (gnome/kde/sway/hyprland)
This commit is contained in:
@@ -8,14 +8,37 @@
|
|||||||
_de: "{{ system_cfg.features.desktop.environment }}"
|
_de: "{{ system_cfg.features.desktop.environment }}"
|
||||||
_family_pkgs: "{{ bootstrap_desktop_packages[os_family] | default({}) }}"
|
_family_pkgs: "{{ bootstrap_desktop_packages[os_family] | default({}) }}"
|
||||||
_de_config: "{{ _family_pkgs[_de] | default({}) }}"
|
_de_config: "{{ _family_pkgs[_de] | default({}) }}"
|
||||||
|
_base: "{{ bootstrap_desktop_base_packages[os_family] | default([]) }}"
|
||||||
|
_requested_groups: "{{ system_cfg.features.desktop.groups | default([]) }}"
|
||||||
|
_group_pkgs: >-
|
||||||
|
{{
|
||||||
|
_requested_groups
|
||||||
|
| select('in', desktop_package_groups)
|
||||||
|
| map('extract', desktop_package_groups)
|
||||||
|
| map(attribute=os_family, default=[])
|
||||||
|
| list
|
||||||
|
| sum(start=[])
|
||||||
|
}}
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
_desktop_groups: "{{ _de_config.groups | default([]) }}"
|
# GNOME ships under different dnf environment groups: Fedora uses
|
||||||
_desktop_packages: "{{ _de_config.packages | default([]) }}"
|
# workstation-product-environment, enterprise RHEL/Rocky/Alma use
|
||||||
|
# graphical-server-environment ("Server with GUI").
|
||||||
|
_desktop_groups: >-
|
||||||
|
{{ ['graphical-server-environment']
|
||||||
|
if (_de == 'gnome' and os_family == 'RedHat' and os != 'fedora')
|
||||||
|
else (_de_config.groups | default([])) }}
|
||||||
|
_desktop_packages: >-
|
||||||
|
{{
|
||||||
|
((_de_config.packages | default([])) + _base + _group_pkgs)
|
||||||
|
| reject('equalto', '')
|
||||||
|
| unique
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
|
||||||
- name: Validate desktop environment is supported
|
- name: Validate desktop environment is supported
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- (_desktop_groups | length > 0) or (_desktop_packages | length > 0)
|
- system_cfg.features.desktop.environment in (bootstrap_desktop_packages[os_family] | default({}))
|
||||||
fail_msg: >-
|
fail_msg: >-
|
||||||
Desktop environment '{{ system_cfg.features.desktop.environment }}'
|
Desktop environment '{{ system_cfg.features.desktop.environment }}'
|
||||||
is not defined for os_family '{{ os_family }}'.
|
is not defined for os_family '{{ os_family }}'.
|
||||||
@@ -25,7 +48,7 @@
|
|||||||
- name: Install desktop package groups
|
- name: Install desktop package groups
|
||||||
when: _desktop_groups | length > 0
|
when: _desktop_groups | length > 0
|
||||||
ansible.builtin.command: >-
|
ansible.builtin.command: >-
|
||||||
{{ chroot_command }} dnf --releasever={{ os_version }}
|
{{ chroot_command }} dnf --releasever={{ os_version_major }}
|
||||||
--setopt=install_weak_deps=False group install -y {{ _desktop_groups | join(' ') }}
|
--setopt=install_weak_deps=False group install -y {{ _desktop_groups | join(' ') }}
|
||||||
register: _desktop_group_result
|
register: _desktop_group_result
|
||||||
changed_when: _desktop_group_result.rc == 0
|
changed_when: _desktop_group_result.rc == 0
|
||||||
@@ -35,14 +58,12 @@
|
|||||||
vars:
|
vars:
|
||||||
_install_commands:
|
_install_commands:
|
||||||
RedHat: >-
|
RedHat: >-
|
||||||
{{ chroot_command }} dnf --releasever={{ os_version }}
|
{{ chroot_command }} dnf --releasever={{ os_version_major }}
|
||||||
--setopt=install_weak_deps=False install -y {{ _desktop_packages | join(' ') }}
|
--setopt=install_weak_deps=False install -y {{ _desktop_packages | join(' ') }}
|
||||||
Debian: >-
|
Debian: >-
|
||||||
{{ chroot_command }} apt install -y {{ _desktop_packages | join(' ') }}
|
{{ chroot_command }} apt install -y --install-recommends {{ _desktop_packages | join(' ') }}
|
||||||
Archlinux: >-
|
Archlinux: >-
|
||||||
pacstrap /mnt {{ _desktop_packages | join(' ') }}
|
pacstrap /mnt {{ _desktop_packages | join(' ') }}
|
||||||
Suse: >-
|
|
||||||
{{ chroot_command }} zypper install -y {{ _desktop_packages | join(' ') }}
|
|
||||||
ansible.builtin.command: "{{ _install_commands[os_family] }}"
|
ansible.builtin.command: "{{ _install_commands[os_family] }}"
|
||||||
register: _desktop_pkg_result
|
register: _desktop_pkg_result
|
||||||
changed_when: _desktop_pkg_result.rc == 0
|
changed_when: _desktop_pkg_result.rc == 0
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
---
|
---
|
||||||
# Per-family desktop environment package definitions.
|
# Per-family desktop environment package definitions.
|
||||||
# Keyed by os_family -> environment -> groups (dnf group install) / packages.
|
# Keyed by os_family -> environment -> groups (dnf group install) / packages.
|
||||||
# Kept intentionally minimal: base DE + essential tools, no full suites.
|
# Wayland only: gnome, kde, sway, hyprland. No X11/xorg-server, no X11-only DEs.
|
||||||
|
# DE sets carry the session bits + the DE-specific xdg portal backend; the
|
||||||
|
# shared base below (fonts/audio/portal core/power/viewer apps) is layered on
|
||||||
|
# top for every DE via bootstrap_desktop_base_packages.
|
||||||
bootstrap_desktop_packages:
|
bootstrap_desktop_packages:
|
||||||
RedHat:
|
RedHat:
|
||||||
gnome:
|
gnome:
|
||||||
@@ -24,13 +27,6 @@ bootstrap_desktop_packages:
|
|||||||
- xdg-user-dirs
|
- xdg-user-dirs
|
||||||
- xdg-desktop-portal-kde
|
- xdg-desktop-portal-kde
|
||||||
- bluez
|
- bluez
|
||||||
- pipewire
|
|
||||||
- wireplumber
|
|
||||||
xfce:
|
|
||||||
groups:
|
|
||||||
- xfce-desktop-environment
|
|
||||||
packages:
|
|
||||||
- lightdm
|
|
||||||
Debian:
|
Debian:
|
||||||
gnome:
|
gnome:
|
||||||
groups: []
|
groups: []
|
||||||
@@ -53,15 +49,6 @@ bootstrap_desktop_packages:
|
|||||||
- xdg-user-dirs
|
- xdg-user-dirs
|
||||||
- xdg-desktop-portal-kde
|
- xdg-desktop-portal-kde
|
||||||
- bluez
|
- bluez
|
||||||
- pipewire
|
|
||||||
- wireplumber
|
|
||||||
xfce:
|
|
||||||
groups: []
|
|
||||||
packages:
|
|
||||||
- xfce4
|
|
||||||
- xfce4-goodies
|
|
||||||
- lightdm
|
|
||||||
- xdg-user-dirs
|
|
||||||
Archlinux:
|
Archlinux:
|
||||||
gnome:
|
gnome:
|
||||||
groups: []
|
groups: []
|
||||||
@@ -84,15 +71,6 @@ bootstrap_desktop_packages:
|
|||||||
- xdg-user-dirs
|
- xdg-user-dirs
|
||||||
- xdg-desktop-portal-kde
|
- xdg-desktop-portal-kde
|
||||||
- bluez
|
- bluez
|
||||||
- pipewire
|
|
||||||
- wireplumber
|
|
||||||
xfce:
|
|
||||||
groups: []
|
|
||||||
packages:
|
|
||||||
- xfce4
|
|
||||||
- xfce4-goodies
|
|
||||||
- lightdm
|
|
||||||
- xdg-user-dirs
|
|
||||||
sway:
|
sway:
|
||||||
groups: []
|
groups: []
|
||||||
packages:
|
packages:
|
||||||
@@ -100,12 +78,13 @@ bootstrap_desktop_packages:
|
|||||||
- waybar
|
- waybar
|
||||||
- foot
|
- foot
|
||||||
- wofi
|
- wofi
|
||||||
|
- nautilus
|
||||||
- greetd
|
- greetd
|
||||||
|
- greetd-tuigreet
|
||||||
- xdg-user-dirs
|
- xdg-user-dirs
|
||||||
- xdg-desktop-portal-wlr
|
- xdg-desktop-portal-wlr
|
||||||
|
- polkit-gnome
|
||||||
- bluez
|
- bluez
|
||||||
- pipewire
|
|
||||||
- wireplumber
|
|
||||||
hyprland:
|
hyprland:
|
||||||
groups: []
|
groups: []
|
||||||
packages:
|
packages:
|
||||||
@@ -113,37 +92,80 @@ bootstrap_desktop_packages:
|
|||||||
- kitty
|
- kitty
|
||||||
- wofi
|
- wofi
|
||||||
- waybar
|
- waybar
|
||||||
- ly
|
- nautilus
|
||||||
|
- greetd
|
||||||
|
- greetd-tuigreet
|
||||||
- xdg-user-dirs
|
- xdg-user-dirs
|
||||||
- xdg-desktop-portal-hyprland
|
- xdg-desktop-portal-hyprland
|
||||||
- polkit-kde-agent
|
- polkit-kde-agent
|
||||||
- qt5-wayland
|
- qt5-wayland
|
||||||
- qt6-wayland
|
- qt6-wayland
|
||||||
- bluez
|
- bluez
|
||||||
|
|
||||||
|
# Shared desktop base, installed for EVERY DE whenever desktop.enabled.
|
||||||
|
# Fonts (noto + emoji + one nerd font), audio stack (pipewire + wireplumber +
|
||||||
|
# pipewire-pulse), xdg portal core, power-profiles-daemon, and viewer-only base
|
||||||
|
# apps (browser, PDF/image/video viewers). DE metas (gnome/plasma) bundle their
|
||||||
|
# own file manager + settings, so no file manager is added here - the wlroots
|
||||||
|
# DE sets above carry their own (nautilus).
|
||||||
|
bootstrap_desktop_base_packages:
|
||||||
|
RedHat:
|
||||||
|
- google-noto-sans-fonts
|
||||||
|
- google-noto-emoji-fonts
|
||||||
|
- fira-code-fonts
|
||||||
- pipewire
|
- pipewire
|
||||||
- wireplumber
|
- wireplumber
|
||||||
Suse:
|
- pipewire-pulseaudio
|
||||||
gnome:
|
- xdg-desktop-portal
|
||||||
groups: []
|
- power-profiles-daemon
|
||||||
packages:
|
- firefox
|
||||||
- patterns-gnome-gnome_basic
|
- evince
|
||||||
- gdm
|
- eog
|
||||||
- xdg-user-dirs
|
- mpv
|
||||||
kde:
|
Debian:
|
||||||
groups: []
|
- fonts-noto
|
||||||
packages:
|
- fonts-noto-color-emoji
|
||||||
- patterns-kde-kde_plasma
|
- fonts-firacode
|
||||||
- sddm
|
- pipewire
|
||||||
- xdg-user-dirs
|
- wireplumber
|
||||||
|
- pipewire-pulse
|
||||||
|
- xdg-desktop-portal
|
||||||
|
- power-profiles-daemon
|
||||||
|
- firefox-esr
|
||||||
|
- evince
|
||||||
|
- eog
|
||||||
|
- mpv
|
||||||
|
Archlinux:
|
||||||
|
- noto-fonts
|
||||||
|
- noto-fonts-emoji
|
||||||
|
- ttf-nerd-fonts-symbols
|
||||||
|
- pipewire
|
||||||
|
- wireplumber
|
||||||
|
- pipewire-pulse
|
||||||
|
- xdg-desktop-portal
|
||||||
|
- power-profiles-daemon
|
||||||
|
- firefox
|
||||||
|
- evince
|
||||||
|
- loupe
|
||||||
|
- mpv
|
||||||
|
|
||||||
# Display manager auto-detection from desktop environment.
|
# Opt-in package groups, selected per host via features.desktop.groups (a list
|
||||||
bootstrap_desktop_dm_map:
|
# of group names). _desktop.yml installs the union of the requested groups'
|
||||||
gnome: gdm
|
# packages. Empty selection by default.
|
||||||
kde: sddm
|
desktop_package_groups:
|
||||||
xfce: lightdm
|
dev:
|
||||||
sway: greetd
|
RedHat:
|
||||||
hyprland: ly@tty2
|
- git
|
||||||
cinnamon: lightdm
|
- "@development-tools"
|
||||||
mate: lightdm
|
- neovim
|
||||||
lxqt: sddm
|
- python3-pip
|
||||||
budgie: gdm
|
Debian:
|
||||||
|
- git
|
||||||
|
- build-essential
|
||||||
|
- neovim
|
||||||
|
- python3-pip
|
||||||
|
Archlinux:
|
||||||
|
- git
|
||||||
|
- base-devel
|
||||||
|
- neovim
|
||||||
|
- python-pip
|
||||||
|
|||||||
@@ -1,13 +1,34 @@
|
|||||||
---
|
---
|
||||||
- name: Enable systemd services
|
- name: Resolve desktop facts
|
||||||
when: _configuration_platform.init_system == 'systemd'
|
when: system_cfg.features.desktop.enabled | bool
|
||||||
vars:
|
vars:
|
||||||
|
_autologin: "{{ system_cfg.features.desktop.autologin | default(false) }}"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
_desktop_dm: >-
|
_desktop_dm: >-
|
||||||
{{
|
{{
|
||||||
system_cfg.features.desktop.display_manager
|
system_cfg.features.desktop.display_manager
|
||||||
if (system_cfg.features.desktop.display_manager | length > 0)
|
if (system_cfg.features.desktop.display_manager | length > 0)
|
||||||
else (configuration_desktop_dm_map[system_cfg.features.desktop.environment] | default(''))
|
else (configuration_desktop_dm_map[system_cfg.features.desktop.environment] | default(''))
|
||||||
}}
|
}}
|
||||||
|
_desktop_session: "{{ system_cfg.features.desktop.session | default('') }}"
|
||||||
|
# Explicit session wins, else the per-environment command. Single source of
|
||||||
|
# truth for the greetd assert, the config gate, and the template.
|
||||||
|
_greetd_session: >-
|
||||||
|
{{
|
||||||
|
system_cfg.features.desktop.session
|
||||||
|
if (system_cfg.features.desktop.session | default('') | length > 0)
|
||||||
|
else (configuration_desktop_session_cmd_map[system_cfg.features.desktop.environment] | default(''))
|
||||||
|
}}
|
||||||
|
_desktop_autologin_user: >-
|
||||||
|
{{
|
||||||
|
_autologin
|
||||||
|
if (_autologin | string | lower not in ['', 'false'] and _autologin in system_cfg.users)
|
||||||
|
else ''
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Enable systemd services
|
||||||
|
when: _configuration_platform.init_system == 'systemd'
|
||||||
|
vars:
|
||||||
configuration_systemd_services: >-
|
configuration_systemd_services: >-
|
||||||
{{
|
{{
|
||||||
['NetworkManager']
|
['NetworkManager']
|
||||||
@@ -15,7 +36,6 @@
|
|||||||
+ (['ufw'] if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else [])
|
+ (['ufw'] if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else [])
|
||||||
+ ([_configuration_platform.ssh_service] if system_cfg.features.ssh.enabled | bool else [])
|
+ ([_configuration_platform.ssh_service] if system_cfg.features.ssh.enabled | bool else [])
|
||||||
+ (['logrotate', 'systemd-timesyncd'] if os == 'archlinux' else [])
|
+ (['logrotate', 'systemd-timesyncd'] if os == 'archlinux' else [])
|
||||||
+ ([_desktop_dm] if system_cfg.features.desktop.enabled | bool and _desktop_dm | length > 0 else [])
|
|
||||||
+ (['bluetooth'] if system_cfg.features.desktop.enabled | bool else [])
|
+ (['bluetooth'] if system_cfg.features.desktop.enabled | bool else [])
|
||||||
}}
|
}}
|
||||||
ansible.builtin.command: "{{ chroot_command }} systemctl enable {{ item }}"
|
ansible.builtin.command: "{{ chroot_command }} systemctl enable {{ item }}"
|
||||||
@@ -27,6 +47,21 @@
|
|||||||
and 'No such file or directory' not in (configuration_enable_service_result.stderr | default(''))
|
and 'No such file or directory' not in (configuration_enable_service_result.stderr | default(''))
|
||||||
and 'does not exist' not in (configuration_enable_service_result.stderr | default(''))
|
and 'does not exist' not in (configuration_enable_service_result.stderr | default(''))
|
||||||
|
|
||||||
|
- name: Enable display manager for selected desktop
|
||||||
|
when:
|
||||||
|
- _configuration_platform.init_system == 'systemd'
|
||||||
|
- system_cfg.features.desktop.enabled | bool
|
||||||
|
- _desktop_dm | length > 0
|
||||||
|
ansible.builtin.command: "{{ chroot_command }} systemctl enable {{ _desktop_dm }}"
|
||||||
|
register: configuration_enable_dm_result
|
||||||
|
changed_when: configuration_enable_dm_result.rc == 0
|
||||||
|
# Unlike optional services above, a missing/unenabled DM is fatal: chroot
|
||||||
|
# systemctl can exit 0 while only warning on stderr, so check both.
|
||||||
|
failed_when: >-
|
||||||
|
configuration_enable_dm_result.rc != 0
|
||||||
|
or 'No such file or directory' in (configuration_enable_dm_result.stderr | default(''))
|
||||||
|
or 'does not exist' in (configuration_enable_dm_result.stderr | default(''))
|
||||||
|
|
||||||
- name: Activate UFW firewall
|
- name: Activate UFW firewall
|
||||||
when:
|
when:
|
||||||
- system_cfg.features.firewall.backend == 'ufw'
|
- system_cfg.features.firewall.backend == 'ufw'
|
||||||
@@ -44,66 +79,108 @@
|
|||||||
register: _desktop_target_result
|
register: _desktop_target_result
|
||||||
changed_when: _desktop_target_result.rc == 0
|
changed_when: _desktop_target_result.rc == 0
|
||||||
|
|
||||||
- name: Enable OpenRC services
|
- name: Enable PipeWire user services globally
|
||||||
when: _configuration_platform.init_system == 'openrc'
|
when:
|
||||||
vars:
|
- _configuration_platform.init_system == 'systemd'
|
||||||
configuration_openrc_services: >-
|
- system_cfg.features.desktop.enabled | bool
|
||||||
{{
|
ansible.builtin.command: "{{ chroot_command }} systemctl --global enable {{ item }}"
|
||||||
['networking']
|
loop: "{{ configuration_desktop_audio_units }}"
|
||||||
+ (['sshd'] if system_cfg.features.ssh.enabled | bool else [])
|
register: _desktop_audio_result
|
||||||
+ ([system_cfg.features.firewall.backend] if system_cfg.features.firewall.enabled | bool else [])
|
changed_when: _desktop_audio_result.rc == 0
|
||||||
}}
|
failed_when: >-
|
||||||
|
_desktop_audio_result.rc != 0
|
||||||
|
and 'No such file or directory' not in (_desktop_audio_result.stderr | default(''))
|
||||||
|
and 'does not exist' not in (_desktop_audio_result.stderr | default(''))
|
||||||
|
|
||||||
|
- name: Assert greetd has a real session command to launch
|
||||||
|
when:
|
||||||
|
- system_cfg.features.desktop.enabled | bool
|
||||||
|
- _desktop_dm == 'greetd'
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- _greetd_session | length > 0
|
||||||
|
- not (_greetd_session | trim | regex_search('\\.desktop$'))
|
||||||
|
fail_msg: >-
|
||||||
|
greetd needs an executable session command, but the resolved command for desktop
|
||||||
|
environment '{{ system_cfg.features.desktop.environment }}' is
|
||||||
|
'{{ _greetd_session }}'. greetd suits wlroots compositors (sway, hyprland) that
|
||||||
|
launch from a plain command; kde/gnome ship a '.desktop' session and should use
|
||||||
|
their own display manager (sddm, gdm). Set features.desktop.session to an
|
||||||
|
executable, or pick a different display manager.
|
||||||
|
|
||||||
|
- name: Generate greetd configuration
|
||||||
|
when:
|
||||||
|
- _configuration_platform.init_system == 'systemd'
|
||||||
|
- system_cfg.features.desktop.enabled | bool
|
||||||
|
- _desktop_dm == 'greetd'
|
||||||
|
- _greetd_session | length > 0
|
||||||
block:
|
block:
|
||||||
- name: Ensure OpenRC runlevel directory exists
|
- name: Ensure greetd config directory exists
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: /mnt/etc/runlevels/default
|
path: /mnt/etc/greetd
|
||||||
state: directory
|
state: directory
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
|
|
||||||
- name: Check OpenRC init scripts
|
- name: Write greetd config.toml
|
||||||
ansible.builtin.stat:
|
ansible.builtin.template:
|
||||||
path: "/mnt/etc/init.d/{{ item }}"
|
src: greetd-config.toml.j2
|
||||||
loop: "{{ configuration_openrc_services }}"
|
dest: /mnt/etc/greetd/config.toml
|
||||||
register: configuration_openrc_service_stats
|
mode: "0644"
|
||||||
|
|
||||||
- name: Enable OpenRC services
|
- name: Configure GDM autologin
|
||||||
ansible.builtin.file:
|
when:
|
||||||
src: "/mnt/etc/init.d/{{ item.item }}"
|
- _configuration_platform.init_system == 'systemd'
|
||||||
dest: "/mnt/etc/runlevels/default/{{ item.item }}"
|
- system_cfg.features.desktop.enabled | bool
|
||||||
state: link
|
- _desktop_dm == 'gdm'
|
||||||
loop: "{{ configuration_openrc_service_stats.results }}"
|
- _desktop_autologin_user | length > 0
|
||||||
loop_control:
|
|
||||||
label: "{{ item.item }}"
|
|
||||||
when: item.stat.exists
|
|
||||||
|
|
||||||
- name: Enable runit services
|
|
||||||
when: _configuration_platform.init_system == 'runit'
|
|
||||||
vars:
|
vars:
|
||||||
configuration_runit_services: >-
|
# Debian's gdm3 reads /etc/gdm3/daemon.conf; RedHat/Arch GDM read
|
||||||
{{
|
# /etc/gdm/custom.conf. The keys are identical, only the path differs.
|
||||||
['dhcpcd']
|
_gdm_dir: "/mnt/etc/{{ 'gdm3' if os_family == 'Debian' else 'gdm' }}"
|
||||||
+ (['sshd'] if system_cfg.features.ssh.enabled | bool else [])
|
_gdm_conf: "{{ 'daemon.conf' if os_family == 'Debian' else 'custom.conf' }}"
|
||||||
+ ([system_cfg.features.firewall.backend] if system_cfg.features.firewall.enabled | bool else [])
|
|
||||||
}}
|
|
||||||
block:
|
block:
|
||||||
- name: Ensure runit service directory exists
|
- name: Ensure GDM config directory exists
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: /mnt/var/service
|
path: "{{ _gdm_dir }}"
|
||||||
state: directory
|
state: directory
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
|
|
||||||
- name: Check runit service definitions
|
- name: Write GDM autologin config
|
||||||
ansible.builtin.stat:
|
ansible.builtin.template:
|
||||||
path: "/mnt/etc/sv/{{ item }}"
|
src: gdm-custom.conf.j2
|
||||||
loop: "{{ configuration_runit_services }}"
|
dest: "{{ _gdm_dir }}/{{ _gdm_conf }}"
|
||||||
register: configuration_runit_service_stats
|
mode: "0644"
|
||||||
|
|
||||||
- name: Enable runit services
|
- name: Configure SDDM autologin
|
||||||
|
when:
|
||||||
|
- _configuration_platform.init_system == 'systemd'
|
||||||
|
- system_cfg.features.desktop.enabled | bool
|
||||||
|
- _desktop_dm == 'sddm'
|
||||||
|
- _desktop_autologin_user | length > 0
|
||||||
|
block:
|
||||||
|
- name: Ensure SDDM config directory exists
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
src: "/mnt/etc/sv/{{ item.item }}"
|
path: /mnt/etc/sddm.conf.d
|
||||||
dest: "/mnt/var/service/{{ item.item }}"
|
state: directory
|
||||||
state: link
|
mode: "0755"
|
||||||
loop: "{{ configuration_runit_service_stats.results }}"
|
|
||||||
loop_control:
|
# Plasma 6 ships the Wayland session as plasma.desktop; Plasma 5 ships it as
|
||||||
label: "{{ item.item }}"
|
# plasmawayland.desktop (plasma.desktop is the X11 session there). Pick the
|
||||||
when: item.stat.exists
|
# installed Wayland session so autologin never lands on X11.
|
||||||
|
- name: Discover installed KDE Wayland sessions
|
||||||
|
ansible.builtin.find:
|
||||||
|
paths: /mnt/usr/share/wayland-sessions
|
||||||
|
patterns: "plasma.desktop,plasmawayland.desktop"
|
||||||
|
register: _kde_wayland_sessions
|
||||||
|
|
||||||
|
- name: Resolve the KDE Wayland session file
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
_sddm_session: >-
|
||||||
|
{%- set names = _kde_wayland_sessions.files | map(attribute='path') | map('basename') | list -%}
|
||||||
|
{{ 'plasma.desktop' if 'plasma.desktop' in names else (names | first | default('')) }}
|
||||||
|
|
||||||
|
- name: Write SDDM autologin drop-in
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: sddm-autologin.conf.j2
|
||||||
|
dest: /mnt/etc/sddm.conf.d/10-autologin.conf
|
||||||
|
mode: "0644"
|
||||||
|
|||||||
4
roles/configuration/templates/gdm-custom.conf.j2
Normal file
4
roles/configuration/templates/gdm-custom.conf.j2
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[daemon]
|
||||||
|
WaylandEnable=true
|
||||||
|
AutomaticLoginEnable=true
|
||||||
|
AutomaticLogin={{ _desktop_autologin_user }}
|
||||||
12
roles/configuration/templates/greetd-config.toml.j2
Normal file
12
roles/configuration/templates/greetd-config.toml.j2
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[terminal]
|
||||||
|
vt = 1
|
||||||
|
|
||||||
|
[default_session]
|
||||||
|
command = "tuigreet --time --remember --cmd {{ _greetd_session }}"
|
||||||
|
user = "greeter"
|
||||||
|
{% if _desktop_autologin_user | length > 0 %}
|
||||||
|
|
||||||
|
[initial_session]
|
||||||
|
command = "{{ _greetd_session }}"
|
||||||
|
user = "{{ _desktop_autologin_user }}"
|
||||||
|
{% endif %}
|
||||||
6
roles/configuration/templates/sddm-autologin.conf.j2
Normal file
6
roles/configuration/templates/sddm-autologin.conf.j2
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{% set _session = _desktop_session if (_desktop_session | length > 0) else _sddm_session %}
|
||||||
|
[Autologin]
|
||||||
|
User={{ _desktop_autologin_user }}
|
||||||
|
{% if _session | length > 0 %}
|
||||||
|
Session={{ _session }}
|
||||||
|
{% endif %}
|
||||||
@@ -35,45 +35,24 @@ configuration_platform_config:
|
|||||||
grub_mkconfig_prefix: grub-mkconfig
|
grub_mkconfig_prefix: grub-mkconfig
|
||||||
locale_gen: true
|
locale_gen: true
|
||||||
init_system: systemd
|
init_system: systemd
|
||||||
Suse:
|
|
||||||
user_group: wheel
|
|
||||||
sudo_group: "%wheel"
|
|
||||||
ssh_service: sshd
|
|
||||||
efi_loader: grubx64.efi
|
|
||||||
grub_install: true
|
|
||||||
initramfs_cmd: "/usr/bin/dracut --regenerate-all --force"
|
|
||||||
grub_mkconfig_prefix: grub-mkconfig
|
|
||||||
locale_gen: true
|
|
||||||
init_system: systemd
|
|
||||||
Alpine:
|
|
||||||
user_group: wheel
|
|
||||||
sudo_group: "%wheel"
|
|
||||||
ssh_service: sshd
|
|
||||||
efi_loader: grubx64.efi
|
|
||||||
grub_install: true
|
|
||||||
initramfs_cmd: ""
|
|
||||||
grub_mkconfig_prefix: grub-mkconfig
|
|
||||||
locale_gen: false
|
|
||||||
init_system: openrc
|
|
||||||
Void:
|
|
||||||
user_group: wheel
|
|
||||||
sudo_group: "%wheel"
|
|
||||||
ssh_service: sshd
|
|
||||||
efi_loader: grubx64.efi
|
|
||||||
grub_install: true
|
|
||||||
initramfs_cmd: ""
|
|
||||||
grub_mkconfig_prefix: grub-mkconfig
|
|
||||||
locale_gen: false
|
|
||||||
init_system: runit
|
|
||||||
|
|
||||||
# Display manager auto-detection from desktop environment name.
|
# Display manager auto-detection from desktop environment name.
|
||||||
configuration_desktop_dm_map:
|
configuration_desktop_dm_map:
|
||||||
gnome: gdm
|
gnome: gdm
|
||||||
kde: sddm
|
kde: sddm
|
||||||
xfce: lightdm
|
|
||||||
sway: greetd
|
sway: greetd
|
||||||
hyprland: ly@tty2
|
hyprland: greetd
|
||||||
cinnamon: lightdm
|
|
||||||
mate: lightdm
|
# Per-environment session command for greetd-launched compositors (sway/hyprland):
|
||||||
lxqt: sddm
|
# the executable greetd starts. kde/gnome use a display manager (sddm/gdm) whose
|
||||||
budgie: gdm
|
# Wayland session is resolved separately, so they are not in this map.
|
||||||
|
configuration_desktop_session_cmd_map:
|
||||||
|
sway: sway
|
||||||
|
hyprland: Hyprland
|
||||||
|
|
||||||
|
# PipeWire user units enabled globally when a desktop is installed.
|
||||||
|
# pipewire/pipewire-pulse are socket-activated; wireplumber ships no socket.
|
||||||
|
configuration_desktop_audio_units:
|
||||||
|
- pipewire.socket
|
||||||
|
- pipewire-pulse.socket
|
||||||
|
- wireplumber.service
|
||||||
|
|||||||
@@ -28,21 +28,41 @@
|
|||||||
memory: "{{ [system_raw.memory | default(0) | int, 0] | max }}"
|
memory: "{{ [system_raw.memory | default(0) | int, 0] | max }}"
|
||||||
balloon: "{{ [system_raw.balloon | default(0) | int, 0] | max }}"
|
balloon: "{{ [system_raw.balloon | default(0) | int, 0] | max }}"
|
||||||
# --- Network ---
|
# --- Network ---
|
||||||
# Flat fields (bridge, ip, etc.) and interfaces[] are mutually exclusive.
|
# Flat fields (bridge, ip, etc.) and interfaces[] express the same primary NIC.
|
||||||
# When interfaces[] is set, flat fields are populated from the first
|
# When only flat fields are set, a synthetic interfaces[] entry is built below.
|
||||||
# interface in the "Populate primary network fields" task below.
|
# When interfaces[] is set, the flat ip/prefix/gateway are backfilled from
|
||||||
# When only flat fields are set, a synthetic interfaces[] entry is built.
|
# interfaces[0] so consumers reading the flat fields (e.g. the post-reboot
|
||||||
|
# reconnect block) still work.
|
||||||
network:
|
network:
|
||||||
bridge: "{{ system_raw.network.bridge | default('') | string }}"
|
bridge: >-
|
||||||
|
{{
|
||||||
|
(system_raw.network.bridge | default('') | string)
|
||||||
|
if (system_raw.network.bridge | default('') | string | length) > 0
|
||||||
|
else (system_raw.network.interfaces[0].bridge | default('') | string
|
||||||
|
if (system_raw.network.interfaces | default([]) | length) > 0 else '')
|
||||||
|
}}
|
||||||
vlan: "{{ system_raw.network.vlan | default('') | string }}"
|
vlan: "{{ system_raw.network.vlan | default('') | string }}"
|
||||||
ip: "{{ system_raw.network.ip | default('') | string }}"
|
ip: >-
|
||||||
|
{{
|
||||||
|
(system_raw.network.ip | default('') | string)
|
||||||
|
if (system_raw.network.ip | default('') | string | length) > 0
|
||||||
|
else (system_raw.network.interfaces[0].ip | default('') | string
|
||||||
|
if (system_raw.network.interfaces | default([]) | length) > 0 else '')
|
||||||
|
}}
|
||||||
prefix: >-
|
prefix: >-
|
||||||
{{
|
{{
|
||||||
(system_raw.network.prefix | int | string)
|
(system_raw.network.prefix | int | string)
|
||||||
if (system_raw.network.prefix | default('') | string | length) > 0
|
if (system_raw.network.prefix | default('') | string | length) > 0
|
||||||
else ''
|
else (system_raw.network.interfaces[0].prefix | default('') | string
|
||||||
|
if (system_raw.network.interfaces | default([]) | length) > 0 else '')
|
||||||
|
}}
|
||||||
|
gateway: >-
|
||||||
|
{{
|
||||||
|
(system_raw.network.gateway | default('') | string)
|
||||||
|
if (system_raw.network.gateway | default('') | string | length) > 0
|
||||||
|
else (system_raw.network.interfaces[0].gateway | default('') | string
|
||||||
|
if (system_raw.network.interfaces | default([]) | length) > 0 else '')
|
||||||
}}
|
}}
|
||||||
gateway: "{{ system_raw.network.gateway | default('') | string }}"
|
|
||||||
dns:
|
dns:
|
||||||
servers: "{{ system_raw.network.dns.servers | default([]) }}"
|
servers: "{{ system_raw.network.dns.servers | default([]) }}"
|
||||||
search: "{{ system_raw.network.dns.search | default([]) }}"
|
search: "{{ system_raw.network.dns.search | default([]) }}"
|
||||||
@@ -148,6 +168,9 @@
|
|||||||
enabled: "{{ system_raw.features.desktop.enabled | bool }}"
|
enabled: "{{ system_raw.features.desktop.enabled | bool }}"
|
||||||
environment: "{{ system_raw.features.desktop.environment | default('') | string | lower }}"
|
environment: "{{ system_raw.features.desktop.environment | default('') | string | lower }}"
|
||||||
display_manager: "{{ system_raw.features.desktop.display_manager | default('') | string | lower }}"
|
display_manager: "{{ system_raw.features.desktop.display_manager | default('') | string | lower }}"
|
||||||
|
autologin: "{{ system_raw.features.desktop.autologin | default(false) }}"
|
||||||
|
session: "{{ system_raw.features.desktop.session | default('') | string }}"
|
||||||
|
groups: "{{ system_raw.features.desktop.groups | default([]) }}"
|
||||||
secure_boot:
|
secure_boot:
|
||||||
enabled: "{{ system_raw.features.secure_boot.enabled | bool }}"
|
enabled: "{{ system_raw.features.secure_boot.enabled | bool }}"
|
||||||
method: "{{ system_raw.features.secure_boot.method | default('') | string | lower }}"
|
method: "{{ system_raw.features.secure_boot.method | default('') | string | lower }}"
|
||||||
@@ -169,7 +192,12 @@
|
|||||||
else (system_raw.features.firmware.microcode | bool)
|
else (system_raw.features.firmware.microcode | bool)
|
||||||
}}
|
}}
|
||||||
gpu:
|
gpu:
|
||||||
enabled: "{{ system_raw.features.gpu.enabled | bool }}"
|
enabled: >-
|
||||||
|
{{
|
||||||
|
(system_raw.features.desktop.enabled | bool)
|
||||||
|
if (system_raw.features.gpu.enabled | string | lower) == 'auto'
|
||||||
|
else (system_raw.features.gpu.enabled | bool)
|
||||||
|
}}
|
||||||
nvidia_driver: "{{ system_raw.features.gpu.nvidia_driver | default('auto') | string | lower }}"
|
nvidia_driver: "{{ system_raw.features.gpu.nvidia_driver | default('auto') | string | lower }}"
|
||||||
peripherals:
|
peripherals:
|
||||||
enabled: >-
|
enabled: >-
|
||||||
|
|||||||
@@ -140,7 +140,7 @@
|
|||||||
os in ["ubuntu", "ubuntu-lts"]
|
os in ["ubuntu", "ubuntu-lts"]
|
||||||
and (os_version | default('') | string | length) == 0
|
and (os_version | default('') | string | length) == 0
|
||||||
) or (
|
) or (
|
||||||
os in ["alpine", "archlinux", "opensuse", "void"]
|
os == "archlinux"
|
||||||
)
|
)
|
||||||
fail_msg: "Invalid os/version specified. Please check README.md for supported values."
|
fail_msg: "Invalid os/version specified. Please check README.md for supported values."
|
||||||
quiet: true
|
quiet: true
|
||||||
@@ -252,6 +252,49 @@
|
|||||||
peripherals.webcam in [auto|true|false]; hardware.profile must be a dict.
|
peripherals.webcam in [auto|true|false]; hardware.profile must be a dict.
|
||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate desktop environment
|
||||||
|
when: system_cfg.features.desktop.enabled | bool
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_cfg.features.desktop.environment in ["gnome", "kde", "sway", "hyprland"]
|
||||||
|
- >-
|
||||||
|
system_cfg.features.desktop.environment not in ["sway", "hyprland"]
|
||||||
|
or os_family_map[os] | default('') == "Archlinux"
|
||||||
|
- >-
|
||||||
|
system_cfg.features.desktop.display_manager | default('') | length == 0
|
||||||
|
or system_cfg.features.desktop.display_manager in ["gdm", "sddm", "greetd"]
|
||||||
|
- >-
|
||||||
|
system_cfg.features.desktop.display_manager | default('') != "greetd"
|
||||||
|
or system_cfg.features.desktop.environment in ["sway", "hyprland"]
|
||||||
|
- >-
|
||||||
|
system_cfg.features.desktop.environment != "gnome"
|
||||||
|
or system_cfg.features.desktop.display_manager | default('') in ["", "gdm"]
|
||||||
|
- >-
|
||||||
|
system_cfg.features.desktop.environment != "kde"
|
||||||
|
or system_cfg.features.desktop.display_manager | default('') in ["", "sddm"]
|
||||||
|
fail_msg: >-
|
||||||
|
Invalid desktop config: environment '{{ system_cfg.features.desktop.environment }}'
|
||||||
|
for os_family '{{ os_family_map[os] | default('Unknown') }}',
|
||||||
|
display_manager '{{ system_cfg.features.desktop.display_manager | default('') }}'.
|
||||||
|
gnome and kde are available on all families; sway and hyprland are Archlinux only.
|
||||||
|
display_manager must be empty (auto) or match the environment's native DM:
|
||||||
|
gnome->gdm, kde->sddm, sway/hyprland->greetd. Only that DM's package is
|
||||||
|
installed, so a mismatched override fails at enable time.
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate desktop autologin
|
||||||
|
when: system_cfg.features.desktop.enabled | bool
|
||||||
|
vars:
|
||||||
|
_autologin: "{{ system_cfg.features.desktop.autologin | default(false) }}"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- _autologin is boolean and not _autologin or (_autologin is string and _autologin | length > 0 and _autologin in system_cfg.users)
|
||||||
|
fail_msg: >-
|
||||||
|
desktop.autologin must be false or a username string present in
|
||||||
|
system.users; got '{{ _autologin }}'. Bool true is not accepted - the
|
||||||
|
resolver matches the value against system.users by name.
|
||||||
|
quiet: true
|
||||||
|
|
||||||
- name: Validate virtual system sizing
|
- name: Validate virtual system sizing
|
||||||
when: system_cfg.type == "virtual"
|
when: system_cfg.type == "virtual"
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
@@ -262,7 +305,7 @@
|
|||||||
- (system_cfg.disks[0].size | float) > 0
|
- (system_cfg.disks[0].size | float) > 0
|
||||||
- (system_cfg.disks[0].size | float) >= 20
|
- (system_cfg.disks[0].size | float) >= 20
|
||||||
# Btrfs minimum disk: swap_size + 5.5 GiB overhead (subvolumes + metadata).
|
# Btrfs minimum disk: swap_size + 5.5 GiB overhead (subvolumes + metadata).
|
||||||
# Swap sizing: memory < 16 GiB → max(memory_GiB, 2); memory >= 16 GiB → memory/2.
|
# Swap sizing: memory < 16 GiB -> max(memory_GiB, 2); memory >= 16 GiB -> memory/2.
|
||||||
- >-
|
- >-
|
||||||
system_cfg.filesystem != "btrfs"
|
system_cfg.filesystem != "btrfs"
|
||||||
or (
|
or (
|
||||||
|
|||||||
Reference in New Issue
Block a user