I have already started blogging about Ansible; in particular, I have shown how to develop and test an Ansible role with Molecule and Docker, also on Gitpod.
This blog post will describe my Ansible role for installing the GNOME desktop environment with several programs and configurations. As for the other roles I’ve blogged about, this one is tested with Molecule and Docker and can be developed with Gitpod (see the linked posts above). In particular, it is tested in Arch, Ubuntu, and Fedora.
This role is for my personal installation and configuration and is not meant to be reusable.
The role can be found here: https://github.com/LorenzoBettini/my_gnome_role.
The role assumes that at least the basic GNOME DE is already installed in the Linux distribution. The role then installs several programs I’m using on a daily basis and performs a few configurations (it also installs a few extensions I use).
At the time of writing, the role has the following directory structure, which is standard for Ansible roles tested with Molecule.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
├── defaults │ └── main.yml ├── files ├── handlers │ └── main.yml ├── LICENSE ├── meta │ └── main.yml ├── molecule │ ├── default │ │ ├── molecule.yml │ │ └── prepare.yml │ ├── fedora │ │ └── molecule.yml │ ├── no-flatpak │ │ ├── converge.yml │ │ ├── molecule.yml │ │ └── verify.yml │ ├── shared │ │ ├── converge.yml │ │ └── verify.yml │ └── ubuntu │ ├── molecule.yml │ └── prepare.yml ├── pip │ └── requirements.txt ├── README.md ├── requirements.yml ├── tasks │ ├── flatpak.yml │ ├── gnome-arch.yml │ ├── gnome-configurations.yml │ ├── gnome-extension-manager.yml │ ├── gnome-extensions.yml │ ├── gnome-templates.yml │ ├── gnome-tracker.yml │ ├── guake.yml │ └── main.yml ├── templates ├── tests │ ├── inventory │ └── test.yml └── vars └── main.yml |
The role has a few requirements, listed in “requirements.yml”:
1 2 3 4 5 6 7 |
--- roles: - name: petermosmans.customize-gnome version: 0.2.10 collections: - name: community.general |
These requirements must also be present in playbooks using this role; my playbooks (which I’ll write about in future articles) have such dependencies in the requirements.
The main file “tasks/main.yml” is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
--- # tasks file for my_gnome_role - name: System details ansible.builtin.debug: msg: "{{ item }}" with_items: - "ansible_os_family: {{ ansible_os_family }}" - "ansible_distribution: {{ ansible_distribution }}" - "ansible_distribution_release: {{ ansible_distribution_release }}" - "ansible_distribution_version: {{ ansible_distribution_version }}" - "ansible_distribution_major_version: {{ ansible_distribution_major_version }}" - name: Install Gnome Packages become: true ansible.builtin.package: state: present name: - gnome-shell - gnome-tweaks - dconf-editor - xdg-utils # required for xdg-mime below - gnome-screenshot # the original screenshot application - baobab - deja-dup - eog - evince - evolution - file-roller - gnome-backgrounds - gnome-calculator - gnome-calendar - gnome-contacts - gnome-logs - gnome-photos - gnome-remote-desktop - gnome-system-monitor - gnome-text-editor - gnome-weather - simple-scan - gvfs - name: Override python-psutil package name for Arch. ansible.builtin.set_fact: python_psutil: python-psutil when: ansible_os_family == 'Archlinux' - name: Install Python psutil become: true ansible.builtin.package: state: present name: "{{ python_psutil }}" - name: Tasks for Guake Drop-down Terminal ansible.builtin.include_tasks: guake.yml - name: Tasks for ArchLinux ansible.builtin.include_tasks: gnome-arch.yml when: ansible_os_family == 'Archlinux' - name: Tasks for Template files ansible.builtin.include_tasks: gnome-templates.yml - name: Tasks for Gnome Tracker ansible.builtin.include_tasks: gnome-tracker.yml - name: Tasks for Gnome configuration ansible.builtin.include_tasks: gnome-configurations.yml - name: Tasks for Gnome extensions ansible.builtin.include_tasks: gnome-extensions.yml - name: Tasks for Flatpak ansible.builtin.include_tasks: flatpak.yml when: with_flatpak - name: Tasks for Gnome Extension Manager ansible.builtin.include_tasks: gnome-extension-manager.yml when: with_flatpak |
This shows a few debug information about the current Linux distribution. Indeed, the whole role has conditional tasks and variables depending on the current Linux distribution.
The file installs a few programs, mainly Gnome programs, but also other programs I’m using in GNOME.
The “vars/main.yml” only defines a few default variables used above:
1 2 3 4 |
--- # vars file for my_gnome_role python_psutil: python3-psutil with_flatpak: true |
As seen above, the package for “python psutils” has a different name in Arch, and it is overridden.
For Arch, we have to install a few additional packages, which are not required in the other distributions (file “gnome-arch.yml”):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
--- - name: Install Gnome Packages (Arch Linux) become: true ansible.builtin.package: state: present name: - gvfs-afc - gvfs-goa - gvfs-google - gvfs-gphoto2 - gvfs-mtp - gvfs-nfs - gvfs-smb |
For the Guake dropdown terminal, we install it (see the corresponding YAML file).
The file “gnome-templates.yml” creates the template for “New File”, which, otherwise, would not be available in recent versions of GNOME, at least in the distributions I’m using.
1 2 3 4 5 6 7 8 9 10 11 |
- name: Create Templates directory ansible.builtin.file: path: '~/Templates' state: directory mode: 0755 - name: Create Templates ansible.builtin.copy: content: "" dest: '~/Templates/New File' mode: 0644 |
For the search engine GNOME Tracker, I performed a few configurations concerning the exclusion mechanisms. This is done by using the Community “dconf” module:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# the default was ['.trackerignore', '.git', '.hg', '.nomedia'] # but that way the contents of a git working directory are not indexed - name: Customize Tracker Ignored directories with content community.general.dconf: key: "/org/freedesktop/tracker/miner/files/ignored-directories-with-content" value: "['.trackerignore', '.hg', '.nomedia']" state: present - name: Customize Tracker Ignored directories community.general.dconf: key: "/org/freedesktop/tracker/miner/files/ignored-directories" value: "['po', 'CVS', 'core-dumps', 'lost+found', 'bin', 'test-bin', 'bin-test', 'target', 'xtend-gen', 'src-gen', 'cache', 'node_modules', 'node_packages', 'thunderbird']" state: present - name: Make sure Tracker 3 is installed (Arch) become: true ansible.builtin.package: state: present name: - tracker3 - tracker3-miners when: ansible_os_family == 'Archlinux' - name: Make sure Tracker 2 is NOT installed (Arch) become: true ansible.builtin.package: state: absent name: - tracker - tracker-miners when: ansible_os_family == 'Archlinux' # In previous versions of ubuntu the service file was # tracker-extract.service # In more recent versions is # tracker-extract-3.service - name: Disable Tracker Extract at system level ansible.builtin.systemd: name: tracker-extract-3 scope: global masked: yes # Better to mask it at the global level # so that it can be run also in a chroot environment # otherwise we get "Failed to connect to bus: No such file or directory" |
This also ensures that possibly previous versions of Tracker are not installed. Moreover, while I use Tracker to quickly look for files (e.g., with the GNOME Activities search bar), I don’t want to use “Tracker extract”, which also indexes file contents. For indexing file contents, I prefer “Recoll”, which is installed and configured in my dedicated playbooks for specific Linux distributions (I’ll blog about them in the future).
Then, the file “gnome-configurations.yml” configures a few aspects (the comments should be self-documented), including some custom keyboard shortcuts (including the one for Guake, which, in Wayland, must be set explicitly as a GNOME shortcut):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
- name: Enable minimize and maximize community.general.dconf: key: "/org/gnome/desktop/wm/preferences/button-layout" value: "'appmenu:minimize,maximize,close'" - name: Terminal transparency community.general.dconf: key: "/org/gnome/terminal/legacy/profiles:/:b1dcc9dd-5262-4d8d-a863-c897e6d979b9/use-transparent-background" value: "true" - name: Terminal transparency percentage community.general.dconf: key: "/org/gnome/terminal/legacy/profiles:/:b1dcc9dd-5262-4d8d-a863-c897e6d979b9/background-transparency-percent" value: "5" - name: Ctrl+Alt+Up reserved for Eclipse community.general.dconf: key: "/org/gnome/desktop/wm/keybindings/switch-to-workspace-up" value: "['<Super>Page_Up']" - name: Ctrl+Alt+Down reserved for Eclipse community.general.dconf: key: "/org/gnome/desktop/wm/keybindings/switch-to-workspace-down" value: "['<Super>Page_Down']" - name: Super+d Show Desktop community.general.dconf: key: "/org/gnome/desktop/wm/keybindings/show-desktop" value: "['<Super>d']" # Custom shortcuts: # For each shortcut, define a name, a command, and a binding. - name: Maximized terminal shortcut <Super>t" community.general.dconf: key: "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/name" value: "'terminal'" - name: Terminal shortcut command community.general.dconf: key: "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/command" value: "'gnome-terminal --maximize'" - name: Terminal shortcut binding community.general.dconf: key: "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/binding" value: "'<Super>t'" - name: Terminal shortcut Ctrl+Alt+t" community.general.dconf: key: "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/name" value: "'terminal'" - name: Terminal shortcut command community.general.dconf: key: "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/command" value: "'gnome-terminal'" - name: Terminal shortcut binding community.general.dconf: key: "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/binding" value: "'<Primary><Alt>t'" - name: Guake shortcut F12 for Wayland" community.general.dconf: key: "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/guake/name" value: "'guake'" - name: Guake shortcut command community.general.dconf: key: "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/guake/command" value: "'guake-toggle'" - name: Guake shortcut binding community.general.dconf: key: "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/guake/binding" value: "'F12'" # All custom shortcuts must also be added to an array. - name: Enable custom keybindings community.general.dconf: key: "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings" value: "['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/guake/']" |
Then, by using the “petermosmans.customize-gnome” role (see the requirements file above), I install a few GNOME extensions, which are specified by their identifiers (these can be found on the GNOME extensions website). I leave a few of them commented out, since I don’t use them anymore, but I might need them in the future):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# Required for unzipping extension archives - name: Install Unzip become: true ansible.builtin.package: state: present name: - unzip # Downloading extensions is very flaky, so if it succeeds on 'converge' # we skip it on 'idempotence' - name: Install Gnome Extensions ansible.builtin.include_role: name: petermosmans.customize-gnome tags: molecule-idempotence-notest vars: gnome_extensions: - id: 19 # User Theme "user-theme@gnome-shell-extensions.gcampax.github.com" - id: 615 # AppIndicator and KStatusNotifierItem Support - id: 3740 # Magic Lamp "compiz-alike-magic-lamp-effect@hermes83.github.com" - id: 307 # dash-to-dock "dash-to-dock@micxgx.gmail.com" - id: 973 # switcher "switcher@landau.fi" - id: 779 # clipboard-indicator - id: 1112 # screenshot-tool - id: 3088 # extension-list - id: 7 # removable-drive-menu - id: 97 # Coverflow Alt-Tab # - id: 16 # Auto Move Windows # - id: 10 # windowNavigator # - id: 5004 # dash-to-dock-for-cosmic # - id: 2890 # Tray Icons: Reloaded, works with Dropbox in Gnome 42! # - id: 120 # system-monitor # - id: 3843 # just-perfection # - id: 4033 # x11-gestures "x11gestures@joseexposito.github.io" requires touchegg |
Then, we have the files for installing and configuring Flatpak, which I use only to install the GNOME Extension manager:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
- name: Install Flatpak become: true ansible.builtin.package: state: present name: - flatpak - name: Add the flathub flatpak repository remote become: true community.general.flatpak_remote: name: flathub state: present flatpakrepo_url: https://dl.flathub.org/repo/flathub.flatpakrepo # method: user # To remove the filters on flathub introduced by Fedora # see https://ask.fedoraproject.org/t/ansible-flathub-repo-setup/19176 # see https://fedoraproject.org/wiki/Changes/Filtered_Flathub_Applications - name: Remove filters from flathub become: true ansible.builtin.command: cmd: flatpak remote-modify --no-filter flathub changed_when: false |
1 2 3 4 5 6 |
- name: Install Gnome Extension Manager become: true community.general.flatpak: name: com.mattjakeman.ExtensionManager state: present # method: user |
I installed them system-wide (the “user” option is commented out).
Concerning Molecule, I have several scenarios. As I said, I tested this role in Arch, Ubuntu, and Fedora, so I have a scenario for each operating system. The “default” scenario is Arch, which nowadays is my daily driver.
For Ubuntu, we have a “prepare.yml” file:
1 2 3 4 5 6 7 8 |
--- - name: Prepare hosts: all gather_facts: false tasks: - name: Install python in Ubuntu ansible.builtin.raw: apt update && apt install -y --no-install-recommends python3 sudo changed_when: false |
The reason why is explained in my previous posts on Ansible and Molecule.
By default, I test that “flatpak” is installed. (see the default variables above: by default, Flatpak is installed)
1 2 3 4 5 6 7 8 9 10 11 |
--- # This is an example playbook to execute Ansible tests. - name: Verify hosts: all gather_facts: false tasks: - name: Assert flatpak is installed in this scenario ansible.builtin.shell: > flatpak --version changed_when: false |
But I also have a scenario (in Arch) where I run the role without Flatpak:
1 2 3 4 5 6 7 8 9 |
--- - name: Converge hosts: all tasks: - name: "Include lorenzobettini.my_gnome_role" ansible.builtin.include_role: name: "lorenzobettini.my_gnome_role" vars: with_flatpak: false |
For this scenario, the “verify.yml” verifies Flatpak is not installed:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
--- # This is an example playbook to execute Ansible tests. - name: Verify hosts: all gather_facts: false tasks: - name: Assert flatpak is NOT installed in this scenario ansible.builtin.shell: > flatpak --version register: flatpak_cmd failed_when: flatpak_cmd.rc == 0 changed_when: false |
Of course, this is tested on GitHub Actions and can be developed directly on the web IDE Gitpod.
I hope you find this post useful for inspiration on how to use Ansible to automatize your Linux installations 🙂