Thanks for the note @rofrol! I’ll offer as much as I can here, my plan is to eventually publish my home-manager configuration once it’s complete, but I’m not quite done yet and it needs some additional polish (technically, scrubbing) before I upload it.
Here’s the relevant details:
Technically I’m using the excellent devos by @nrdxp, so I symlink /etc/nixos
to my local checkout of my devos repository. So /etc/nixos
is a devos
repository, but can still serve as a place to nix build
, etc.
The interface in the screenshot is i3, polybar, and dunst. The widget in the lower left is this bit of polybar configuration:
[module/updates]
click-left=kill -USR1 %pid%
click-right=kill -USR2 %pid%
exec=/nix/store/jh57cw9p3p40z1c9c4ncvp6bli5ay3n8-packages/bin/packages --icon /nix/store/8qxzbn358fm7nq55w202ypjkizsg0dmc
-nixos-icons-2021-02-24/share/icons/hicolor/scalable/apps/nix-snowflake.svg
format-prefix=" "
format-prefix-foreground=${colors.foreground-alt}
format-suffix=" ↑"
format-suffix-foreground=${colors.foreground-alt}
format-underline=#4bffdc
tail=true
type=custom/script
The packages
executable is a python script I have that checks for updates on various distros. I suppose I’ll just copypasta that here, it’s a private script that I haven’t put anywhere else so it probably makes some assumptions that may break for others, but it may be helpful regardless.
packages.py
from pathlib import Path
from subprocess import DEVNULL, PIPE, run
from threading import Event
import argparse
import crayons
import dbus
import distro
import os
import signal
import sys
parser = argparse.ArgumentParser(
description="Mini-daemon for *bar update widgets")
parser.add_argument(
"--icon", default='system-software-update',
type=Path, help="Which icon to use"
)
args = parser.parse_args()
os.environ['PATH'] += os.pathsep + os.pathsep.join([
'/run/current-system/sw/bin',
os.path.sep.join([os.path.expanduser('~'), '.nix-profile', 'bin'])
])
bus = "org.freedesktop.Notifications"
delay = 60 * 60
AVAILABLE = []
CACHE = Path.home() / '.cache' / 'packages-pending'
RUN = Event()
BUS_INTERFACE = dbus.Interface(
dbus.SessionBus().get_object(bus, "/"+bus.replace(".", "/")), bus)
distribution = distro.name()
if 'Arch' in distribution:
CHECKER = ['checkupdates']
elif 'Fedora' in distribution:
CHECKER = ['dnf', 'updateinfo', '-q', '--list']
elif 'Nix' in distribution:
CHECKER = ['nix-updateinfo']
else:
print("Unknown distro")
sys.exit(1)
def discharging():
for supply in Path('/sys/class/power_supply').iterdir():
if '/usb' in str(supply.resolve()):
continue
status = supply / 'status'
if status.exists():
with status.open('r') as f:
if f.read().strip() == 'Discharging':
return True
return False
def updates():
global CHECKER
return crayons.clean(
run(CHECKER, stdout=PIPE, stderr=DEVNULL).stdout.decode('utf-8')
).split('\n')
def check():
print("↺", flush=True)
global AVAILABLE
global CACHE
cached = False
if discharging() and CACHE.exists():
with CACHE.open('r') as f:
AVAILABLE = list(map(lambda x: x.strip(), f.readlines()))
cached = True
else:
try:
AVAILABLE = list(filter(bool, updates()))
except Exception:
print("!", flush=True)
return
results = len(AVAILABLE)
if results > 0:
print(f"{results}{'💤' if cached else ''}", flush=True)
else:
print("✓", flush=True)
with CACHE.open('w') as f:
f.write('\n'.join(AVAILABLE))
def handle_force(sig, frame):
global RUN
RUN.set()
def handle_notify(sig, frame):
global AVAILABLE
global BUS_INTERFACE
global args
BUS_INTERFACE.Notify(
"System Updates", 0, str(args.icon), "Available Updates",
'\n'.join(AVAILABLE), [], {"urgency": 1}, 5000)
signal.signal(signal.SIGUSR1, handle_force)
signal.signal(signal.SIGUSR2, handle_notify)
RUN.set()
while True:
RUN.wait(delay)
check()
RUN.clear()
nix-updateinfo
is actually pretty short, this is the script. I’ll include what I have in my home-manager config verbatim:
nix-updates-script = pkgs.writers.writeBashBin "nix-updateinfo" ''
tempdir=$(mktemp -d /tmp/tmp.nix-updateinfo.XXX)
${pkgs.git}/bin/git clone --reference /etc/nixos /etc/nixos $tempdir
cd $tempdir
# Disable the noisy devshell git hook provisioner
sed -i 's/true/false/' shell/hooks/default.nix || true
${pkgs.nixUnstable}/bin/nix-shell --run "bud update latest"
${pkgs.nixUnstable}/bin/nix-shell --run "bud build toplevel"
${pkgs.nixUnstable}/bin/nix store diff-closures /run/current-system ./result | awk '/[0-9] →|→ [0-9]/ && !/nixos/' || echo
cd ~-
rm -rf "$tempdir"
'';
For those not using devos
, you can achieve the “update nixpkgs” step by either calling nix flake update <your nixpkgs input>
or whatever the equivalent is for !flakes. Running nix-updateinfo
from a normal shell should yield pending updates outside of the polybar widget.
The visual notification is from dunst; my configuration for that is pretty large but I’ll drop it here so that anyone can truly reproduce the setup if they want:
~/.config/dunst/dunstrc
[experimental]
per_monitor_dpi=yes
[global]
alignment="left"
always_run_script=yes
browser="/nix/store/kqdhiygq1rzbq1cpnhix29dq7p6a97z5-firefox-94.0.1/bin/firefox -new-tab"
class="Dunst"
corner_radius=5
dmenu="/nix/store/yf9sdd85amgn63kjkzhgi9gpm6lm2sbi-rofi-1.7.0/bin/rofi -dmenu -p Notification"
ellipsize="middle"
follow="mouse"
font="fixed 12"
format="<b>%s</b>\n\n%b\n\n<sub>%a</sub>"
frame_color="#282a36"
frame_width=3
hide_duplicate_count=no
history_length=20
horizontal_padding=20
icon_path="/run/current-system/sw/share/icons/Adwaita/32x32/actions:/run/current-system/sw/share/icons/hicolor/32x32/actions:/run/current-system/sw/share/icons/Adwaita/32x32/animations:/run/current-system/sw/share/icons/hicolor/32x32/animations:/run/current-system/sw/share/icons/Adwaita/32x32/apps:/run/current-system/sw/share/icons/hicolor/32x32/apps:/run/current-system/sw/share/icons/Adwaita/32x32/categories:/run/current-system/sw/share/icons/hicolor/32x32/categories:/run/current-system/sw/share/icons/Adwaita/32x32/devices:/run/current-system/sw/share/icons/hicolor/32x32/devices:/run/current-system/sw/share/icons/Adwaita/32x32/emblems:/run/current-system/sw/share/icons/hicolor/32x32/emblems:/run/current-system/sw/share/icons/Adwaita/32x32/emotes:/run/current-system/sw/share/icons/hicolor/32x32/emotes:/run/current-system/sw/share/icons/Adwaita/32x32/filesystem:/run/current-system/sw/share/icons/hicolor/32x32/filesystem:/run/current-system/sw/share/icons/Adwaita/32x32/intl:/run/current-system/sw/share/icons/hicolor/32x32/intl:/run/current-system/sw/share/icons/Adwaita/32x32/legacy:/run/current-system/sw/share/icons/hicolor/32x32/legacy:/run/current-system/sw/share/icons/Adwaita/32x32/mimetypes:/run/current-system/sw/share/icons/hicolor/32x32/mimetypes:/run/current-system/sw/share/icons/Adwaita/32x32/places:/run/current-system/sw/share/icons/hicolor/32x32/places:/run/current-system/sw/share/icons/Adwaita/32x32/status:/run/current-system/sw/share/icons/hicolor/32x32/status:/run/current-system/sw/share/icons/Adwaita/32x32/stock:/run/current-system/sw/share/icons/hicolor/32x32/stock:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/actions:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/actions:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/animations:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/animations:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/apps:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/apps:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/categories:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/categories:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/devices:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/devices:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/emblems:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/emblems:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/emotes:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/emotes:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/filesystem:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/filesystem:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/intl:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/intl:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/legacy:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/legacy:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/mimetypes:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/mimetypes:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/places:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/places:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/status:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/status:/etc/profiles/per-user/tylerjl/share/icons/Adwaita/32x32/stock:/etc/profiles/per-user/tylerjl/share/icons/hicolor/32x32/stock:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/actions:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/actions:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/animations:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/animations:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/apps:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/apps:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/categories:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/categories:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/devices:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/devices:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/emblems:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/emblems:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/emotes:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/emotes:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/filesystem:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/filesystem:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/intl:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/intl:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/legacy:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/legacy:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/mimetypes:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/mimetypes:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/places:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/places:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/status:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/status:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/Adwaita/32x32/stock:/nix/store/8hp8qiqps1gm3ibscmjgz5wa4a2q851v-adwaita-icon-theme-41.0/share/icons/hicolor/32x32/stock:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/actions:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/actions:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/animations:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/animations:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/apps:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/apps:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/categories:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/categories:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/devices:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/devices:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/emblems:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/emblems:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/emotes:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/emotes:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/filesystem:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/filesystem:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/intl:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/intl:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/legacy:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/legacy:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/mimetypes:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/mimetypes:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/places:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/places:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/status:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/status:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/Adwaita/32x32/stock:/nix/store/gz1r222x6s65lj091fl67c35g11hismj-hicolor-icon-theme-0.17/share/icons/hicolor/32x32/stock"
icon_position="left"
idle_threshold=120
ignore_newline="no"
indicate_hidden="yes"
line_height=0
markup="full"
max_icon_size=64
monitor=0
mouse_left_click="close_current"
mouse_middle_click="close_all"
mouse_right_click="do_action"
notification_limit="5"
offset="20x75"
origin="bottom-right"
padding=8
separator_color="frame"
separator_height=2
show_age_threshold=60
show_indicators="yes"
shrink="no"
sort="yes"
stack_duplicates=yes
sticky_history="yes"
title="Dunst"
transparency=30
width="(0, 800)"
word_wrap="yes"
[urgency_critical]
background="#ab4642"
foreground="#e8e8e8"
frame_color="#ff0000"
timeout=0
[urgency_low]
background="#282828"
foreground="#585858"
timeout=10
[urgency_normal]
background="#282a3690"
foreground="#f5f5f5"
timeout=10
Note that all of these configs (polybar, dunst, etc.) are all configured via the relevant home-manager settings so all of this lives in a .nix
file in my machine’s configuration. That should be enough (I think?) to explain the setup.
I hope that’s helpful, let me know if there’s anything else I can clarify!