Merge branch 'master' of ssh://git.joshuabell.xyz:3032/ringofstorms/dotfiles

This commit is contained in:
RingOfStorms (Joshua Bell) 2026-01-13 14:10:11 -06:00
commit 282208d537
6 changed files with 215 additions and 91 deletions

View file

@ -1,64 +1,207 @@
{ lib, pkgs, ... }: { config, lib, pkgs, ... }:
let
cfg = config.services.automatic-timezoned;
persistFile = if cfg.persistDir == null then null else "${cfg.persistDir}/timezone";
tzdata = pkgs.tzdata;
in
{ {
services.dbus.enable = lib.mkDefault true; options.services.automatic-timezoned.persistDir = lib.mkOption {
services.geoclue2.enable = true; type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Absolute runtime directory used to persist the timezone for impermanence setups.
time.timeZone = null; Important: this must be a normal filesystem path (a string like
services.automatic-timezoned.enable = true; "/persist/var/lib/timezone-persist"), not a Nix `path` value, otherwise it
can be coerced into a `/nix/store/...` path and become unwritable at runtime.
systemd.services.automatic-timezoned = { When set, the timezone is saved to this directory and restored on boot,
after = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ]; allowing offline boots to use the last known timezone.
wants = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ]; Set to null to disable persistence (default).
serviceConfig = { '';
ExecStartPre = "${lib.getExe' pkgs.coreutils "sleep"} 5";
Restart = "on-failure";
RestartSec = "10s";
};
}; };
systemd.services.automatic-timezoned-geoclue-agent = { config = {
after = [ "dbus.socket" ]; assertions = [
wants = [ "dbus.socket" ]; {
}; assertion = cfg.persistDir == null || lib.hasPrefix "/" cfg.persistDir;
message = "services.automatic-timezoned.persistDir must be an absolute path";
}
];
systemd.services.fix-localtime-symlink = { services.dbus.enable = lib.mkDefault true;
description = "Fix /etc/localtime symlink to be absolute"; services.geoclue2.enable = true;
wantedBy = [ "multi-user.target" ];
after = [ "automatic-timezoned.service" ];
wants = [ "automatic-timezoned.service" ];
serviceConfig = { time.timeZone = null;
Type = "oneshot"; services.automatic-timezoned.enable = true;
RemainAfterExit = true;
ExecStart = pkgs.writeShellScript "fix-localtime-symlink" ''
target=$(${pkgs.coreutils}/bin/readlink /etc/localtime 2>/dev/null || true)
if [ -z "$target" ]; then
exit 0
fi
if [[ "$target" == /* ]]; then systemd.services.automatic-timezoned = {
exit 0 after = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ]
fi ++ lib.optional (cfg.persistDir != null) "timezone-restore.service";
wants = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ]
abs_target="/etc/$target" ++ lib.optional (cfg.persistDir != null) "timezone-restore.service";
if [ -e "$abs_target" ]; then serviceConfig = {
${pkgs.coreutils}/bin/ln -sf "$abs_target" /etc/localtime ExecStartPre = "${lib.getExe' pkgs.coreutils "sleep"} 5";
fi Restart = "on-failure";
''; RestartSec = "10s";
};
}; };
unitConfig = { systemd.services.automatic-timezoned-geoclue-agent = {
ConditionPathIsSymbolicLink = "/etc/localtime"; after = [ "dbus.socket" ];
wants = [ "dbus.socket" ];
}; };
};
systemd.paths.fix-localtime-symlink = { # Ensure anything using timedate1 sees restored timezone first.
description = "Watch /etc/localtime for changes"; systemd.services.systemd-timedated = lib.mkIf (cfg.persistDir != null) {
wantedBy = [ "multi-user.target" ]; after = [ "timezone-restore.service" ];
wants = [ "timezone-restore.service" ];
requires = [ "timezone-restore.service" ];
};
pathConfig = { # Restore timezone from persistent storage on boot (fallback for offline boots)
PathChanged = "/etc/localtime"; systemd.services.timezone-restore = lib.mkIf (cfg.persistDir != null) {
Unit = "fix-localtime-symlink.service"; description = "Restore timezone from persistent storage";
wantedBy = [ "sysinit.target" ];
# NixOS activation may recreate /etc/localtime based on config.
# Run after activation so the restored timezone "wins" on offline boots.
after = [
"local-fs.target"
"systemd-remount-fs.service"
"nixos-activation.service"
];
wants = [ "nixos-activation.service" ];
before = [
"time-sync.target"
"automatic-timezoned.service"
"systemd-timedated.service"
];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
RequiresMountsFor = [ cfg.persistDir ];
ExecStart = pkgs.writeShellScript "timezone-restore" ''
set -euo pipefail
persist_file="${persistFile}"
if [ ! -f "$persist_file" ]; then
echo "No persisted timezone found, skipping restore"
exit 0
fi
tz=$(${pkgs.coreutils}/bin/cat "$persist_file")
if [ -z "$tz" ]; then
echo "Persisted timezone file is empty, skipping restore"
exit 0
fi
tzfile="${tzdata}/share/zoneinfo/$tz"
if [ ! -f "$tzfile" ]; then
echo "Invalid timezone '$tz' in persist file, skipping restore"
exit 0
fi
echo "Restoring timezone: $tz"
${pkgs.coreutils}/bin/ln -sf "$tzfile" /etc/localtime
# Some NixOS setups may generate /etc/timezone as a symlink into the store.
# Replace it so we don't fail the whole restore.
${pkgs.coreutils}/bin/rm -f /etc/timezone
${pkgs.coreutils}/bin/printf '%s\n' "$tz" > /etc/timezone
'';
};
};
# Save timezone whenever it changes
systemd.services.timezone-persist = lib.mkIf (cfg.persistDir != null) {
description = "Persist timezone to storage";
serviceConfig = {
Type = "oneshot";
RequiresMountsFor = [ cfg.persistDir ];
ExecStart = pkgs.writeShellScript "timezone-persist" ''
set -euo pipefail
${pkgs.coreutils}/bin/mkdir -p "${cfg.persistDir}"
# Try to read timezone from /etc/timezone first, fall back to parsing symlink
if [ -f /etc/timezone ]; then
tz=$(${pkgs.coreutils}/bin/cat /etc/timezone | ${pkgs.coreutils}/bin/tr -d '[:space:]')
else
target=$(${pkgs.coreutils}/bin/readlink /etc/localtime 2>/dev/null || true)
if [ -z "$target" ]; then
echo "Cannot determine timezone, skipping persist"
exit 0
fi
# Extract timezone name from path like /nix/store/.../share/zoneinfo/America/Chicago
tz=$(echo "$target" | ${pkgs.gnused}/bin/sed -n 's|.*/zoneinfo/||p')
fi
if [ -z "$tz" ]; then
echo "Cannot determine timezone, skipping persist"
exit 0
fi
persist_file="${persistFile}"
echo "Persisting timezone: $tz"
echo "$tz" > "$persist_file"
'';
};
};
# Watch /etc/localtime and /etc/timezone for changes and trigger persist
systemd.paths.timezone-persist = lib.mkIf (cfg.persistDir != null) {
description = "Watch timezone changes to persist";
wantedBy = [ "multi-user.target" ];
pathConfig = {
PathChanged = [ "/etc/localtime" "/etc/timezone" ];
Unit = "timezone-persist.service";
};
};
systemd.services.fix-localtime-symlink = {
description = "Fix /etc/localtime symlink to be absolute";
wantedBy = [ "multi-user.target" ];
after = [ "automatic-timezoned.service" ];
wants = [ "automatic-timezoned.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = pkgs.writeShellScript "fix-localtime-symlink" ''
target=$(${pkgs.coreutils}/bin/readlink /etc/localtime 2>/dev/null || true)
if [ -z "$target" ]; then
exit 0
fi
if [[ "$target" == /* ]]; then
exit 0
fi
abs_target="/etc/$target"
if [ -e "$abs_target" ]; then
${pkgs.coreutils}/bin/ln -sf "$abs_target" /etc/localtime
fi
'';
};
unitConfig = {
ConditionPathIsSymbolicLink = "/etc/localtime";
};
};
systemd.paths.fix-localtime-symlink = {
description = "Watch /etc/localtime for changes";
wantedBy = [ "multi-user.target" ];
pathConfig = {
PathChanged = "/etc/localtime";
Unit = "fix-localtime-symlink.service";
};
}; };
}; };
} }

View file

@ -209,16 +209,6 @@ in
''; '';
}) })
(mkIf ((length cfg.wallpapers) > 0) {
environment.etc."xdg/plasma-org.kde.plasma.desktop-appletsrc".text =
let
wallpaperPath = builtins.head cfg.wallpapers;
in
''
[Containments][1][Wallpaper][org.kde.image][General]
Image=file://${wallpaperPath}
'';
})
# GPU blocks # GPU blocks
(mkIf cfg.gpu.amd.enable { (mkIf cfg.gpu.amd.enable {

View file

@ -5,7 +5,9 @@
}: }:
let let
cfg = osConfig.ringofstorms.dePlasma; cfg = osConfig.ringofstorms.dePlasma;
inherit (lib) mkIf; inherit (lib) mkIf optionalAttrs;
# Get the first wallpaper from the list if available
wallpaper = if (builtins.length cfg.wallpapers) > 0 then builtins.head cfg.wallpapers else null;
in in
{ {
imports = [ imports = [
@ -294,6 +296,8 @@ in
lookAndFeel = "org.kde.breezedark.desktop"; lookAndFeel = "org.kde.breezedark.desktop";
theme = "breeze-dark"; theme = "breeze-dark";
cursor.theme = "breeze_cursors"; cursor.theme = "breeze_cursors";
} // optionalAttrs (wallpaper != null) {
wallpaper = wallpaper;
}; };
configFile = { configFile = {

36
hosts/juni/flake.lock generated
View file

@ -38,40 +38,28 @@
}, },
"common": { "common": {
"locked": { "locked": {
"dir": "flakes/common", "path": "../../flakes/common",
"lastModified": 1768255305, "type": "path"
"narHash": "sha256-XcXl5M0WNYhCCqE9qc9Aj2/2Jb/T0NHZnu2ZuVBvlHw=",
"ref": "refs/heads/master",
"rev": "15769eda748f6fcc6fdab04f79f14ed9b1ffc548",
"revCount": 1125,
"type": "git",
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
}, },
"original": { "original": {
"dir": "flakes/common", "path": "../../flakes/common",
"type": "git", "type": "path"
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles" },
} "parent": []
}, },
"de_plasma": { "de_plasma": {
"inputs": { "inputs": {
"plasma-manager": "plasma-manager" "plasma-manager": "plasma-manager"
}, },
"locked": { "locked": {
"dir": "flakes/de_plasma", "path": "../../flakes/de_plasma",
"lastModified": 1768255305, "type": "path"
"narHash": "sha256-XcXl5M0WNYhCCqE9qc9Aj2/2Jb/T0NHZnu2ZuVBvlHw=",
"ref": "refs/heads/master",
"rev": "15769eda748f6fcc6fdab04f79f14ed9b1ffc548",
"revCount": 1125,
"type": "git",
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
}, },
"original": { "original": {
"dir": "flakes/de_plasma", "path": "../../flakes/de_plasma",
"type": "git", "type": "path"
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles" },
} "parent": []
}, },
"flatpaks": { "flatpaks": {
"inputs": { "inputs": {

View file

@ -9,16 +9,16 @@
impermanence.url = "github:nix-community/impermanence"; impermanence.url = "github:nix-community/impermanence";
# Use relative to get current version for testin # Use relative to get current version for testin
# common.url = "path:../../flakes/common"; common.url = "path:../../flakes/common";
common.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/common"; # common.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/common";
# secrets-bao.url = "path:../../flakes/secrets-bao"; # secrets-bao.url = "path:../../flakes/secrets-bao";
secrets-bao.url = "path:../../flakes/secrets-bao"; secrets-bao.url = "path:../../flakes/secrets-bao";
# flatpaks.url = "path:../../flakes/flatpaks"; # flatpaks.url = "path:../../flakes/flatpaks";
flatpaks.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/flatpaks"; flatpaks.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/flatpaks";
# beszel.url = "path:../../flakes/beszel"; # beszel.url = "path:../../flakes/beszel";
beszel.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/beszel"; beszel.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/beszel";
# de_plasma.url = "path:../../flakes/de_plasma"; de_plasma.url = "path:../../flakes/de_plasma";
de_plasma.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/de_plasma"; # de_plasma.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/de_plasma";
opencode.url = "github:sst/opencode"; opencode.url = "github:sst/opencode";
ros_neovim.url = "git+https://git.joshuabell.xyz/ringofstorms/nvim"; ros_neovim.url = "git+https://git.joshuabell.xyz/ringofstorms/nvim";
@ -63,8 +63,8 @@
gpu.intel.enable = true; gpu.intel.enable = true;
sddm.autologinUser = "josh"; sddm.autologinUser = "josh";
wallpapers = [ wallpapers = [
../../_shared_assets/wallpapers/pixel_neon.png ../../hosts/_shared_assets/wallpapers/pixel_neon.png
../../_shared_assets/wallpapers/pixel_neon_v.png ../../hosts/_shared_assets/wallpapers/pixel_neon_v.png
]; ];
}; };
}) })
@ -84,6 +84,7 @@
inputs.common.nixosModules.hardening inputs.common.nixosModules.hardening
inputs.common.nixosModules.nix_options inputs.common.nixosModules.nix_options
inputs.common.nixosModules.timezone_auto inputs.common.nixosModules.timezone_auto
({ services.automatic-timezoned.persistDir = "/persist/var/lib/timezone-persist"; })
inputs.common.nixosModules.tty_caps_esc inputs.common.nixosModules.tty_caps_esc
inputs.common.nixosModules.zsh inputs.common.nixosModules.zsh
inputs.common.nixosModules.tailnet inputs.common.nixosModules.tailnet

View file

@ -32,8 +32,6 @@
files = [ files = [
"/machine-key.json" "/machine-key.json"
"/etc/machine-id" "/etc/machine-id"
"/etc/localtime"
"/etc/timezone"
"/etc/adjtime" "/etc/adjtime"
# NOTE: if you want mutable passwords across reboots, persist these, # NOTE: if you want mutable passwords across reboots, persist these,
# but you must do a one-time migration (see notes in chat). # but you must do a one-time migration (see notes in chat).