--- - name: Resolve desktop facts when: system_cfg.features.desktop.enabled | bool vars: _autologin: "{{ system_cfg.features.desktop.autologin | default(false) }}" ansible.builtin.set_fact: # KDE resolves to the plasmalogin unit on Arch/Fedora44+ (Plasma 6.6), else sddm. _desktop_dm: >- {{ ('plasmalogin' if system_cfg.features.desktop.display_manager == 'plasma-login-manager' else system_cfg.features.desktop.display_manager) if (system_cfg.features.desktop.display_manager | length > 0) else ( ('plasmalogin' if (os == 'archlinux' or (os == 'fedora' and (os_version | int) >= 44)) else 'sddm') if system_cfg.features.desktop.environment == 'kde' 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: >- {{ ['NetworkManager'] + ([_configuration_platform.ssh_service] if system_cfg.features.ssh.enabled | bool else []) + (['logrotate', 'systemd-timesyncd'] if os == 'archlinux' else []) + (['bluetooth'] if system_cfg.features.desktop.enabled | bool else []) }} ansible.builtin.command: "{{ chroot_command }} systemctl enable {{ item }}" loop: "{{ configuration_systemd_services }}" register: configuration_enable_service_result changed_when: configuration_enable_service_result.rc == 0 failed_when: >- configuration_enable_service_result.rc != 0 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('')) - name: Enable display manager for selected desktop when: - _configuration_platform.init_system == 'systemd' - system_cfg.features.desktop.enabled | bool - _desktop_dm | length > 0 - _desktop_dm != 'ly' 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 when: - system_cfg.features.firewall.backend == 'ufw' - system_cfg.features.firewall.enabled | bool ansible.builtin.command: "{{ chroot_command }} ufw --force enable" register: _ufw_enable_result changed_when: _ufw_enable_result.rc == 0 failed_when: false - name: Enable ly on its tty when: - _configuration_platform.init_system == 'systemd' - system_cfg.features.desktop.enabled | bool - _desktop_dm == 'ly' vars: _ly_tty: tty2 block: - name: Enable ly display manager ansible.builtin.command: "{{ chroot_command }} systemctl enable ly@{{ _ly_tty }}.service" register: configuration_enable_ly_result changed_when: configuration_enable_ly_result.rc == 0 failed_when: >- configuration_enable_ly_result.rc != 0 or 'No such file or directory' in (configuration_enable_ly_result.stderr | default('')) or 'does not exist' in (configuration_enable_ly_result.stderr | default('')) # ly drives the VT itself; mask getty so logind never spawns a login on that tty. - name: Mask getty on ly's tty ansible.builtin.command: "{{ chroot_command }} systemctl mask getty@{{ _ly_tty }}.service" register: configuration_mask_getty_result changed_when: configuration_mask_getty_result.rc == 0 failed_when: >- configuration_mask_getty_result.rc != 0 and 'No such file or directory' not in (configuration_mask_getty_result.stderr | default('')) and 'does not exist' not in (configuration_mask_getty_result.stderr | default('')) - name: Set default systemd target when: _configuration_platform.init_system == 'systemd' vars: _default_target: "{{ 'graphical.target' if system_cfg.features.desktop.enabled | bool else 'multi-user.target' }}" ansible.builtin.command: "{{ chroot_command }} systemctl set-default {{ _default_target }}" register: _set_default_target_result changed_when: _set_default_target_result.rc == 0 - name: Enable PipeWire user services globally when: - _configuration_platform.init_system == 'systemd' - system_cfg.features.desktop.enabled | bool ansible.builtin.command: "{{ chroot_command }} systemctl --global enable {{ item }}" loop: "{{ configuration_desktop_audio_units }}" register: _desktop_audio_result 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: - name: Ensure greetd config directory exists ansible.builtin.file: path: /mnt/etc/greetd state: directory mode: "0755" - name: Write greetd config.toml ansible.builtin.template: src: greetd-config.toml.j2 dest: /mnt/etc/greetd/config.toml mode: "0644" - name: Configure GDM autologin when: - _configuration_platform.init_system == 'systemd' - system_cfg.features.desktop.enabled | bool - _desktop_dm == 'gdm' - _desktop_autologin_user | length > 0 vars: # Debian gdm3 reads daemon.conf; RedHat/Arch gdm read custom.conf. _gdm_dir: "/mnt/etc/{{ 'gdm3' if os_family == 'Debian' else 'gdm' }}" _gdm_conf: "{{ 'daemon.conf' if os_family == 'Debian' else 'custom.conf' }}" block: - name: Ensure GDM config directory exists ansible.builtin.file: path: "{{ _gdm_dir }}" state: directory mode: "0755" - name: Write GDM autologin config ansible.builtin.template: src: gdm-custom.conf.j2 dest: "{{ _gdm_dir }}/{{ _gdm_conf }}" mode: "0644" # SDDM and plasma-login-manager share the [Autologin] format and the KDE Wayland # session; only the config dir differs (sddm.conf.d vs plasmalogin.conf.d). - name: Configure SDDM / plasma-login-manager autologin when: - _configuration_platform.init_system == 'systemd' - system_cfg.features.desktop.enabled | bool - _desktop_dm in ['sddm', 'plasmalogin'] - _desktop_autologin_user | length > 0 vars: _autologin_conf_dir: "/mnt/etc/{{ 'plasmalogin.conf.d' if _desktop_dm == 'plasmalogin' else 'sddm.conf.d' }}" block: - name: Ensure KDE login-manager config directory exists ansible.builtin.file: path: "{{ _autologin_conf_dir }}" state: directory mode: "0755" # Plasma 6 ships the Wayland session as plasma.desktop; Plasma 5 ships it as # plasmawayland.desktop (plasma.desktop is the X11 session there). Pick the # 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 KDE login-manager autologin drop-in ansible.builtin.template: src: sddm-autologin.conf.j2 dest: "{{ _autologin_conf_dir }}/10-autologin.conf" mode: "0644" # ly ships a flat (sectionless) config.ini; edit it in place to keep upstream # defaults. Both keys are required: an unresolved session writes 'null', which # disables autologin rather than leaving it half-configured. - name: Configure ly autologin when: - _configuration_platform.init_system == 'systemd' - system_cfg.features.desktop.enabled | bool - _desktop_dm == 'ly' - _desktop_autologin_user | length > 0 community.general.ini_file: path: /mnt/etc/ly/config.ini option: "{{ item.key }}" value: "{{ item.value }}" create: false mode: "0644" loop: - key: auto_login_user value: "{{ _desktop_autologin_user }}" - key: auto_login_session value: "{{ _greetd_session if (_greetd_session | length > 0) else 'null' }}"