new version after nix

This commit is contained in:
Bartal Laearsson
2026-06-29 13:23:11 +01:00
commit 753dfe7dc8
14 changed files with 898 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
# Fedora Sway workstation
An Ansible playbook for rebuilding the two NixOS workstation hosts as a native
Fedora Sway desktop. The NixOS server host (`sta-len-nix`) and its services,
WireGuard, firewall, and Netdata configuration are intentionally excluded.
## Target
- Native Fedora 44 on x86_64
- Sway with SDDM, Waybar, PipeWire, printing, SSH, and NetworkManager
- Faroese keyboard layout, Danish regional formats, and Atlantic/Faroe time
- Foot as the terminal, opening the persistent `tmux` session
- Zen as the default browser, installed from Zen's official release tarball
- Development tools, formatters, and Fedora-packaged language servers from NixOS
## Run
Install Ansible as the normal desktop user:
```bash
sudo dnf install -y ansible
git clone https://codeberg.org/bartal-lsn/fedora-provisioning.git
cd fedora-provisioning
ansible-playbook setup.yml --ask-become-pass
```
Do not start the playbook with `sudo`. It uses privilege escalation only for
system tasks and installs Zen and user configuration into the invoking user's
home directory. Log out or reboot after the first run to enter Sway through
SDDM and pick up the new group memberships and environment.
## Configuration ownership
The Home Manager configuration lives in the public
`https://codeberg.org/bartal-lsn/dotfiles` repository as GNU Stow packages.
During provisioning, Ansible clones or updates that repository at
`~/dotfiles`, then runs `stow -R *` from that directory. Stow therefore
restows every package into the user's home directory.
Commit dotfile changes to the Codeberg repository, then rerun the playbook to
pull `~/dotfiles` and refresh its links. Editor-managed binaries such as `zls`,
`omnisharp`, `jdtls`, and `lua-language-server` remain Neovim concerns because
Fedora does not package them consistently. Ansible installs Fedora's `clangd`
and `gopls`, Pyright, TypeScript language server, Black, isort, Prettier, and
StyLua. Rust and `rust-analyzer` are installed through the official rustup
installer.
## Desktop keys
| Key | Action |
|---|---|
| `Super+t` | Foot attached to tmux session `main` |
| `Super+b` | Zen Browser |
| `Super+d` | Rofi application launcher |
| `Super+q` | Close window |
| `Super+h/j/k/l` | Move focus |
| `Super+Shift+h/j/k/l` | Move window |
| `Super+1..0` | Switch workspace |
| `Super+Shift+1..0` | Move window to workspace |
| `Super+f` | Toggle fullscreen |
| `Super+Shift+Space` | Toggle floating |
| `Super+-` | Show scratchpad |
| `Super+Shift+-` | Move window to scratchpad |
## Optional behavior
The main switches live near the top of `vars.yml`:
- `upgrade_system`: upgrade packages before provisioning
- `install_virtualization`: install Fedora's libvirt virtualization group
- `install_sqlcmd`: retain the Fedora repository's existing SQL Server CLI
- `install_typora`: install the unverified Flathub Typora wrapper
The playbook installs OpenJDK 25 because Fedora 44 does not publish the
OpenJDK 21 RPM used by the NixOS setup.
Fedora does not have a direct equivalent for NixOS's
`virtualisation.vmware.host.enable`; this playbook uses Fedora's supported
libvirt stack instead. Typora is optional because its Flathub wrapper is not
verified by Typora. Zen never uses Flatpak: Ansible downloads the official
release archive directly, places it under `~/.tarball-installations/zen`, and
creates the `~/.local/bin/zen` launcher.
## Re-running
The playbook is designed to be repeatable. Zen, Bun, Rust, Starship, npm tools,
services, and configuration files are checked before changes are made. Zen
handles browser updates itself after the initial binary installation.
+4
View File
@@ -0,0 +1,4 @@
[defaults]
inventory = inventory.ini
inject_facts_as_vars = False
+3
View File
@@ -0,0 +1,3 @@
[workstation]
localhost ansible_connection=local ansible_python_interpreter=/usr/bin/python3
+34
View File
@@ -0,0 +1,34 @@
---
- name: Configure Fedora Sway workstation
hosts: workstation
become: true
vars_files:
- vars.yml
pre_tasks:
- name: Verify that the target is Fedora
ansible.builtin.assert:
that:
- ansible_facts["distribution"] == "Fedora"
- ansible_facts["distribution_major_version"] == "44"
- ansible_facts["architecture"] == "x86_64"
fail_msg: "This playbook supports native Fedora 44 x86_64 workstations."
- name: Verify that the workstation user is not root
ansible.builtin.assert:
that:
- user_name != "root"
- user_home != "/root"
fail_msg: "Run ansible-playbook as the workstation user, using --ask-become-pass for sudo."
tasks:
- ansible.builtin.import_tasks: tasks/dnf_packages.yml
- ansible.builtin.import_tasks: tasks/system.yml
- ansible.builtin.import_tasks: tasks/services.yml
- ansible.builtin.import_tasks: tasks/tooling.yml
- ansible.builtin.import_tasks: tasks/sqlcmd.yml
when: install_sqlcmd
- ansible.builtin.import_tasks: tasks/zen.yml
- ansible.builtin.import_tasks: tasks/dotfiles.yml
- ansible.builtin.import_tasks: tasks/user_config.yml
+26
View File
@@ -0,0 +1,26 @@
---
- name: Enable RPM Fusion free repository
ansible.builtin.dnf:
name: "https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-{{ ansible_facts['distribution_major_version'] }}.noarch.rpm"
state: present
disable_gpg_check: true
- name: Upgrade installed Fedora packages
ansible.builtin.dnf:
name: "*"
state: latest
update_cache: true
when: upgrade_system
- name: Install Fedora workstation packages
ansible.builtin.dnf:
name: "{{ fedora_packages }}"
state: present
allowerasing: true
update_cache: true
- name: Install virtualization package group
ansible.builtin.dnf:
name: "{{ virtualization_packages }}"
state: present
when: install_virtualization
+27
View File
@@ -0,0 +1,27 @@
---
- name: Ensure Stow target directories exist
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
owner: "{{ user_name }}"
mode: "{{ item.mode }}"
loop:
- { path: "{{ user_home }}/.config", mode: "0755" }
- { path: "{{ user_home }}/.ssh", mode: "0700" }
become: false
- name: Clone or update the public dotfiles repository
ansible.builtin.git:
repo: "{{ dotfiles_repo }}"
dest: "{{ dotfiles_dir }}"
version: main
update: true
become: false
- name: Install all dotfiles with GNU Stow
ansible.builtin.shell: stow -R *
args:
chdir: "{{ dotfiles_dir }}"
executable: /bin/bash
become: false
changed_when: false
+35
View File
@@ -0,0 +1,35 @@
---
- name: Enable workstation system services
ansible.builtin.systemd_service:
name: "{{ item }}"
enabled: true
state: started
loop:
- NetworkManager.service
- cups.service
- sshd.service
- name: Select SDDM as the display manager
ansible.builtin.file:
src: /usr/lib/systemd/system/sddm.service
dest: /etc/systemd/system/display-manager.service
state: link
force: true
- name: Use the graphical boot target
ansible.builtin.file:
src: /usr/lib/systemd/system/graphical.target
dest: /etc/systemd/system/default.target
state: link
force: true
- name: Reload systemd after selecting the display manager
ansible.builtin.systemd_service:
daemon_reload: true
- name: Enable libvirt
ansible.builtin.systemd_service:
name: libvirtd.service
enabled: true
state: started
when: install_virtualization
+43
View File
@@ -0,0 +1,43 @@
---
- name: Check if sqlcmd is already installed
ansible.builtin.stat:
path: /usr/local/bin/sqlcmd
register: sqlcmd_bin
- name: Download sqlcmd archive
ansible.builtin.get_url:
url: "{{ sqlcmd_url }}"
dest: "/tmp/{{ sqlcmd_archive }}"
when: not sqlcmd_bin.stat.exists
- name: Create sqlcmd extraction directory
ansible.builtin.file:
path: /tmp/sqlcmd_extract
state: directory
mode: "0755"
when: not sqlcmd_bin.stat.exists
- name: Extract sqlcmd
ansible.builtin.unarchive:
src: "/tmp/{{ sqlcmd_archive }}"
dest: /tmp/sqlcmd_extract
remote_src: true
creates: /tmp/sqlcmd_extract/sqlcmd
when: not sqlcmd_bin.stat.exists
- name: Move sqlcmd binary to /usr/local/bin
ansible.builtin.copy:
src: /tmp/sqlcmd_extract/sqlcmd
dest: /usr/local/bin/sqlcmd
mode: "0755"
remote_src: true
when: not sqlcmd_bin.stat.exists
- name: Clean up sqlcmd temp files
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- "/tmp/{{ sqlcmd_archive }}"
- /tmp/sqlcmd_extract
when: not sqlcmd_bin.stat.exists
+76
View File
@@ -0,0 +1,76 @@
---
- name: Read the configured timezone
ansible.builtin.command: timedatectl show --property=Timezone --value
register: configured_timezone
changed_when: false
- name: Set the system timezone
ansible.builtin.command: "timedatectl set-timezone {{ system_timezone }}"
when: configured_timezone.stdout != system_timezone
- name: Configure system locale
ansible.builtin.copy:
dest: /etc/locale.conf
mode: "0644"
content: |
LANG={{ system_locale }}
LC_ADDRESS={{ regional_locale }}
LC_IDENTIFICATION={{ regional_locale }}
LC_MEASUREMENT={{ regional_locale }}
LC_MONETARY={{ regional_locale }}
LC_NAME={{ regional_locale }}
LC_NUMERIC={{ regional_locale }}
LC_PAPER={{ regional_locale }}
LC_TELEPHONE={{ regional_locale }}
LC_TIME={{ regional_locale }}
- name: Configure console keyboard
ansible.builtin.copy:
dest: /etc/vconsole.conf
mode: "0644"
content: |
KEYMAP=dk
- name: Add workstation user to administrative and device groups
ansible.builtin.user:
name: "{{ user_name }}"
groups: "{{ ['wheel', 'video', 'input'] + (['libvirt'] if install_virtualization else []) }}"
append: true
shell: /usr/bin/zsh
- name: Check transparent hugepage kernel argument
ansible.builtin.shell: |
grubby --info=ALL | awk '
/^args=/ {
seen = 1
if (index($0, "transparent_hugepage=never") == 0) missing = 1
}
END { exit !(seen && !missing) }
'
args:
executable: /bin/bash
register: transparent_hugepage_argument
changed_when: false
failed_when: false
- name: Disable transparent hugepages in all boot entries
ansible.builtin.command:
argv:
- grubby
- --update-kernel=ALL
- --args=transparent_hugepage=never
when: transparent_hugepage_argument.rc != 0
- name: Configure workstation environment
ansible.builtin.copy:
dest: /etc/profile.d/fedora-workstation.sh
mode: "0644"
content: |
export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
export GOPATH="${GOPATH:-$HOME/go}"
export PATH="$HOME/.scripts:$HOME/.local/bin:$HOME/.cargo/bin:$HOME/.bun/bin:$HOME/bin/zig:$HOME/bin/zls:$GOPATH/bin:$PATH"
export TERMINAL=foot
export BROWSER=zen
export GTK_THEME=Adwaita:dark
export MOZ_ENABLE_WAYLAND=1
export ELECTRON_OZONE_PLATFORM_HINT=wayland
+119
View File
@@ -0,0 +1,119 @@
---
- name: List globally installed npm packages
ansible.builtin.command: npm list --global --depth=0 --json
register: npm_global_list
changed_when: false
failed_when: false
- name: Install global language servers and formatters
ansible.builtin.command: "npm install --global {{ item }}"
loop: "{{ npm_global_packages }}"
when: item not in ((npm_global_list.stdout | default('{}', true) | from_json).dependencies | default({}))
- name: Check for rustup
ansible.builtin.stat:
path: "{{ user_home }}/.cargo/bin/rustup"
become: false
register: rustup_binary
- name: Download the official rustup installer
ansible.builtin.get_url:
url: https://sh.rustup.rs
dest: /tmp/rustup-init.sh
mode: "0755"
when: not rustup_binary.stat.exists
- name: Install Rust stable with the official rustup installer
ansible.builtin.command:
argv:
- /tmp/rustup-init.sh
- -y
- --default-toolchain
- stable
- --profile
- default
environment:
HOME: "{{ user_home }}"
become: false
when: not rustup_binary.stat.exists
- name: Remove the rustup installer
ansible.builtin.file:
path: /tmp/rustup-init.sh
state: absent
- name: Check the active Rust toolchain
ansible.builtin.command: "{{ user_home }}/.cargo/bin/rustup show active-toolchain"
become: false
register: active_rust_toolchain
changed_when: false
failed_when: false
- name: Select the Rust stable toolchain
ansible.builtin.command: "{{ user_home }}/.cargo/bin/rustup default stable"
become: false
when: active_rust_toolchain.rc != 0 or not active_rust_toolchain.stdout.startswith('stable-')
- name: List installed rustup components
ansible.builtin.command: "{{ user_home }}/.cargo/bin/rustup component list --installed"
become: false
register: installed_rustup_components
changed_when: false
- name: Install Rust development components
ansible.builtin.command: "{{ user_home }}/.cargo/bin/rustup component add {{ item }}"
loop: "{{ rustup_components }}"
become: false
when: installed_rustup_components.stdout_lines | select('match', '^' ~ item ~ '(-|$)') | list | length == 0
- name: Check for StyLua
ansible.builtin.stat:
path: "{{ user_home }}/.cargo/bin/stylua"
become: false
register: stylua_binary
- name: Install StyLua
ansible.builtin.command: "{{ user_home }}/.cargo/bin/cargo install stylua --locked"
become: false
when: not stylua_binary.stat.exists
- name: Check for Starship
ansible.builtin.stat:
path: "{{ user_home }}/.local/bin/starship"
become: false
register: starship_binary
- name: Download the Starship installer
ansible.builtin.get_url:
url: https://starship.rs/install.sh
dest: /tmp/starship-install.sh
mode: "0755"
when: not starship_binary.stat.exists
- name: Install Starship for the workstation user
ansible.builtin.command:
argv:
- /tmp/starship-install.sh
- --yes
- --bin-dir
- "{{ user_home }}/.local/bin"
become: false
when: not starship_binary.stat.exists
- name: Check for Bun
ansible.builtin.stat:
path: "{{ user_home }}/.bun/bin/bun"
become: false
register: bun_binary
- name: Download the Bun installer
ansible.builtin.get_url:
url: https://bun.sh/install
dest: /tmp/bun-install.sh
mode: "0755"
when: not bun_binary.stat.exists
- name: Install Bun for the workstation user
ansible.builtin.command: /tmp/bun-install.sh
become: false
when: not bun_binary.stat.exists
+56
View File
@@ -0,0 +1,56 @@
---
- name: Create Ansible-managed user directories
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
owner: "{{ user_name }}"
mode: "{{ item.mode }}"
loop:
- { path: "{{ user_home }}/.ssh", mode: "0700" }
- { path: "{{ user_home }}/Pictures", mode: "0755" }
become: false
- name: Check for the Git signing public key
ansible.builtin.stat:
path: "{{ git_signing_key }}"
become: false
register: git_signing_public_key
- name: Read the Git signing public key
ansible.builtin.slurp:
src: "{{ git_signing_key }}"
become: false
register: git_signing_public_key_contents
when: git_signing_public_key.stat.exists
- name: Configure allowed SSH signers
ansible.builtin.copy:
dest: "{{ user_home }}/.ssh/allowed_signers"
owner: "{{ user_name }}"
mode: "0644"
content: "{{ git_email }} {{ git_signing_public_key_contents.content | b64decode | trim }}\n"
become: false
when: git_signing_public_key.stat.exists
- name: Install Typora from Flathub
when: install_typora
block:
- name: Ensure Flathub is configured
ansible.builtin.command:
argv:
- flatpak
- remote-add
- --if-not-exists
- flathub
- https://flathub.org/repo/flathub.flatpakrepo
changed_when: false
- name: Check for Typora
ansible.builtin.command: flatpak info io.typora.Typora
register: typora_flatpak
changed_when: false
failed_when: false
- name: Install Typora
ansible.builtin.command: flatpak install --noninteractive flathub io.typora.Typora
when: typora_flatpak.rc != 0
+112
View File
@@ -0,0 +1,112 @@
---
- name: Check for Zen Browser
ansible.builtin.stat:
path: "{{ zen_install_dir }}/zen"
become: false
register: zen_binary
- name: Create Zen installation directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ user_name }}"
mode: "0755"
become: false
loop:
- "{{ user_home }}/.tarball-installations"
- "{{ user_home }}/.config"
- "{{ user_home }}/.local/bin"
- "{{ user_home }}/.local/share/applications"
- "{{ user_home }}/.local/share/icons/hicolor/128x128/apps"
- name: Download the current Zen release binary
ansible.builtin.get_url:
url: "{{ zen_archive_url }}"
dest: "{{ zen_archive }}"
mode: "0644"
become: false
when: not zen_binary.stat.exists
- name: Extract Zen Browser
ansible.builtin.unarchive:
src: "{{ zen_archive }}"
dest: "{{ user_home }}/.tarball-installations"
remote_src: true
creates: "{{ zen_install_dir }}/zen"
become: false
- name: Link Zen into the user's PATH
ansible.builtin.file:
src: "{{ zen_install_dir }}/zen"
dest: "{{ user_home }}/.local/bin/zen"
state: link
force: true
become: false
- name: Install Zen application icon
ansible.builtin.copy:
src: "{{ zen_install_dir }}/browser/chrome/icons/default/default128.png"
dest: "{{ user_home }}/.local/share/icons/hicolor/128x128/apps/zen.png"
remote_src: true
owner: "{{ user_name }}"
mode: "0644"
become: false
- name: Install Zen desktop entry
ansible.builtin.copy:
dest: "{{ user_home }}/.local/share/applications/{{ zen_desktop_file }}"
owner: "{{ user_name }}"
mode: "0644"
content: |
[Desktop Entry]
Version=1.0
Type=Application
Name=Zen Browser
Comment=Browse the web
Exec=zen %u
Icon=zen
Terminal=false
StartupNotify=true
StartupWMClass=zen
Categories=Network;WebBrowser;
MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;application/x-xpinstall;application/pdf;application/json;
Actions=new-window;new-private-window;profile-manager;
[Desktop Action new-window]
Name=New Window
Exec=zen --new-window %u
[Desktop Action new-private-window]
Name=New Private Window
Exec=zen --private-window %u
[Desktop Action profile-manager]
Name=Profile Manager
Exec=zen --ProfileManager
become: false
- name: Remove downloaded Zen archive
ansible.builtin.file:
path: "{{ zen_archive }}"
state: absent
become: false
- name: Query default MIME handlers
ansible.builtin.command: "xdg-mime query default {{ item }}"
become: false
loop:
- text/html
- application/xhtml+xml
- x-scheme-handler/http
- x-scheme-handler/https
register: current_browser_handlers
changed_when: false
failed_when: false
- name: Set Zen as the default browser
ansible.builtin.command: "xdg-mime default {{ zen_desktop_file }} {{ item.item }}"
become: false
loop: "{{ current_browser_handlers.results }}"
loop_control:
label: "{{ item.item }}"
when: item.stdout != zen_desktop_file
+118
View File
@@ -0,0 +1,118 @@
# Sway workstation configuration translated from NixOS behavior.
set $mod Mod4
set $term foot -e tmux new-session -A -s main
set $browser zen
set $menu rofi -show drun
font pango:DejaVu Sans Mono 10
output * scale 1
output * bg ~/.config/sway/tux.png fill
input type:keyboard {
xkb_layout {{ keyboard_layout }}
xkb_model pc105
}
input type:touchpad {
natural_scroll disabled
tap enabled
}
gaps inner 5
gaps outer 10
smart_gaps on
default_border pixel 2
default_floating_border pixel 2
client.focused #33ccff #33ccff #1a1b26 #33ccff #33ccff
client.focused_inactive #595959 #595959 #c0caf5 #595959 #595959
client.unfocused #595959 #595959 #c0caf5 #595959 #595959
floating_modifier $mod normal
# Applications and session
bindsym $mod+t exec $term
bindsym $mod+b exec $browser
bindsym $mod+d exec $menu
bindsym $mod+q kill
bindsym $mod+Shift+c reload
bindsym $mod+Shift+e exec swaynag -t warning -m 'Exit Sway?' -B 'Exit' 'swaymsg exit'
# Focus movement
bindsym $mod+h focus left
bindsym $mod+j focus down
bindsym $mod+k focus up
bindsym $mod+l focus right
bindsym $mod+Left focus left
bindsym $mod+Down focus down
bindsym $mod+Up focus up
bindsym $mod+Right focus right
# Move windows
bindsym $mod+Shift+h move left
bindsym $mod+Shift+j move down
bindsym $mod+Shift+k move up
bindsym $mod+Shift+l move right
bindsym $mod+Shift+Left move left
bindsym $mod+Shift+Down move down
bindsym $mod+Shift+Up move up
bindsym $mod+Shift+Right move right
# Workspaces
set $ws1 1
set $ws2 2
set $ws3 3
set $ws4 4
set $ws5 5
set $ws6 6
set $ws7 7
set $ws8 8
set $ws9 9
set $ws10 10
bindsym $mod+1 workspace number $ws1
bindsym $mod+2 workspace number $ws2
bindsym $mod+3 workspace number $ws3
bindsym $mod+4 workspace number $ws4
bindsym $mod+5 workspace number $ws5
bindsym $mod+6 workspace number $ws6
bindsym $mod+7 workspace number $ws7
bindsym $mod+8 workspace number $ws8
bindsym $mod+9 workspace number $ws9
bindsym $mod+0 workspace number $ws10
bindsym $mod+Shift+1 move container to workspace number $ws1
bindsym $mod+Shift+2 move container to workspace number $ws2
bindsym $mod+Shift+3 move container to workspace number $ws3
bindsym $mod+Shift+4 move container to workspace number $ws4
bindsym $mod+Shift+5 move container to workspace number $ws5
bindsym $mod+Shift+6 move container to workspace number $ws6
bindsym $mod+Shift+7 move container to workspace number $ws7
bindsym $mod+Shift+8 move container to workspace number $ws8
bindsym $mod+Shift+9 move container to workspace number $ws9
bindsym $mod+Shift+0 move container to workspace number $ws10
# Layout and scratchpad
bindsym $mod+s layout toggle split
bindsym $mod+e layout toggle split
bindsym $mod+f fullscreen toggle
bindsym $mod+Shift+space floating toggle
bindsym $mod+minus scratchpad show
bindsym $mod+Shift+minus move scratchpad
# Hardware controls
bindsym XF86AudioRaiseVolume exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
bindsym XF86AudioLowerVolume exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
bindsym XF86AudioMute exec wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
bindsym XF86AudioMicMute exec wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
bindsym XF86MonBrightnessUp exec brightnessctl set 5%+
bindsym XF86MonBrightnessDown exec brightnessctl set 5%-
bindsym Print exec grim -g "$(slurp)" "$HOME/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png"
# Session services
exec dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
exec /usr/libexec/lxqt-policykit-agent
exec nm-applet --indicator
exec mako
exec ~/.config/waybar/launch.sh
+157
View File
@@ -0,0 +1,157 @@
---
# The playbook must be started as the desktop user, not through sudo.
user_name: "{{ lookup('env', 'USER') }}"
user_home: "{{ lookup('env', 'HOME') }}"
dotfiles_dir: "{{ user_home }}/dotfiles"
dotfiles_repo: https://codeberg.org/bartal-lsn/dotfiles.git
system_timezone: Atlantic/Faroe
system_locale: en_US.UTF-8
regional_locale: da_DK.UTF-8
keyboard_layout: fo
upgrade_system: true
install_virtualization: true
install_sqlcmd: true
install_typora: true
fedora_packages:
# Media and documents
- ffmpeg
- fuse
- fuse-libs
- ghostscript
- libheif-tools
- libreoffice
- libtiff
- libwebp
- openjpeg
# Development toolchains and libraries
- binutils
- clang
- clang-tools-extra
- composer
- dotnet-sdk-10.0
- file
- gcc
- gcc-c++
- glibc-langpack-da
- glibc-langpack-en
- golang
- gopls
- go-task
- hugo
- ibus
- java-25-openjdk-devel
- make
- maven
- ncurses-devel
- nodejs
- npm
- openssl-devel
- php
- python3
- black
- python3-isort
- sqlite
- tree-sitter-cli
- unixODBC
- unixODBC-devel
- uv
- zig
# Command-line workstation tools
- 7zip
- acpi
- curl
- fd-find
- findutils
- flatpak
- fish
- fzf
- gawk
- git
- jq
- kbd
- less
- lua
- neovim
- ripgrep
- stow
- tar
- tmux
- unzip
- util-linux
- vim-enhanced
- wget
- xz
- zip
- zsh
- zsh-autosuggestions
- zsh-syntax-highlighting
# Native Sway workstation
- brightnessctl
- foot
- gnome-keyring
- gnome-keyring-pam
- grim
- mako
- network-manager-applet
- nm-connection-editor
- pamixer
- pavucontrol
- pipewire
- pipewire-alsa
- pipewire-pulseaudio
- playerctl
- polkit
- lxqt-policykit
- rofi
- rtkit
- sddm
- sddm-wayland-sway
- slurp
- sway
- swayidle
- swaylock
- waybar
- wireplumber
- wl-clipboard
- xdg-desktop-portal
- xdg-desktop-portal-gtk
- xdg-desktop-portal-wlr
- xdg-utils
# Services and system integration
- cups
- grubby
- openssh-server
virtualization_packages:
- "@virtualization"
npm_global_packages:
- prettier
- pyright
- typescript
- typescript-language-server
rustup_components:
- rust-analyzer
- rust-src
# Direct stable release binary published by the verified Zen Browser project.
zen_archive_url: https://github.com/zen-browser/desktop/releases/latest/download/zen.linux-x86_64.tar.xz
zen_archive: /tmp/zen.linux-x86_64.tar.xz
zen_install_dir: "{{ user_home }}/.tarball-installations/zen"
zen_desktop_file: zen.desktop
# Existing Fedora-only tool retained from this repository.
sqlcmd_version: "1.8.1"
sqlcmd_archive: sqlcmd-linux-amd64.tar.bz2
sqlcmd_url: "https://github.com/microsoft/go-sqlcmd/releases/download/v{{ sqlcmd_version }}/{{ sqlcmd_archive }}"
git_email: bartal@flo.fo
git_signing_key: "{{ user_home }}/.ssh/id_ed25519.pub"