Compare commits

..

No commits in common. "c0a1757cab2ed794e2a611ccc9d0e5dd12d9030c" and "c408693861f5aceca177628f3480d9ccd87c42fb" have entirely different histories.

6 changed files with 91 additions and 215 deletions

View file

@ -1,207 +1,64 @@
{ config, lib, pkgs, ... }: { lib, pkgs, ... }:
let
cfg = config.services.automatic-timezoned;
persistFile = if cfg.persistDir == null then null else "${cfg.persistDir}/timezone";
tzdata = pkgs.tzdata;
in
{ {
options.services.automatic-timezoned.persistDir = lib.mkOption { services.dbus.enable = lib.mkDefault true;
type = lib.types.nullOr lib.types.str; services.geoclue2.enable = true;
default = null;
description = ''
Absolute runtime directory used to persist the timezone for impermanence setups.
Important: this must be a normal filesystem path (a string like time.timeZone = null;
"/persist/var/lib/timezone-persist"), not a Nix `path` value, otherwise it services.automatic-timezoned.enable = true;
can be coerced into a `/nix/store/...` path and become unwritable at runtime.
When set, the timezone is saved to this directory and restored on boot, systemd.services.automatic-timezoned = {
allowing offline boots to use the last known timezone. after = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ];
Set to null to disable persistence (default). wants = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ];
''; serviceConfig = {
ExecStartPre = "${lib.getExe' pkgs.coreutils "sleep"} 5";
Restart = "on-failure";
RestartSec = "10s";
};
}; };
config = { systemd.services.automatic-timezoned-geoclue-agent = {
assertions = [ after = [ "dbus.socket" ];
{ wants = [ "dbus.socket" ];
assertion = cfg.persistDir == null || lib.hasPrefix "/" cfg.persistDir; };
message = "services.automatic-timezoned.persistDir must be an absolute path";
}
];
services.dbus.enable = lib.mkDefault true; systemd.services.fix-localtime-symlink = {
services.geoclue2.enable = true; description = "Fix /etc/localtime symlink to be absolute";
wantedBy = [ "multi-user.target" ];
after = [ "automatic-timezoned.service" ];
wants = [ "automatic-timezoned.service" ];
time.timeZone = null; serviceConfig = {
services.automatic-timezoned.enable = true; 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
systemd.services.automatic-timezoned = { if [[ "$target" == /* ]]; then
after = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ] exit 0
++ lib.optional (cfg.persistDir != null) "timezone-restore.service"; fi
wants = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ]
++ lib.optional (cfg.persistDir != null) "timezone-restore.service"; abs_target="/etc/$target"
serviceConfig = { if [ -e "$abs_target" ]; then
ExecStartPre = "${lib.getExe' pkgs.coreutils "sleep"} 5"; ${pkgs.coreutils}/bin/ln -sf "$abs_target" /etc/localtime
Restart = "on-failure"; fi
RestartSec = "10s"; '';
};
}; };
systemd.services.automatic-timezoned-geoclue-agent = { unitConfig = {
after = [ "dbus.socket" ]; ConditionPathIsSymbolicLink = "/etc/localtime";
wants = [ "dbus.socket" ];
}; };
};
# Ensure anything using timedate1 sees restored timezone first. systemd.paths.fix-localtime-symlink = {
systemd.services.systemd-timedated = lib.mkIf (cfg.persistDir != null) { description = "Watch /etc/localtime for changes";
after = [ "timezone-restore.service" ]; wantedBy = [ "multi-user.target" ];
wants = [ "timezone-restore.service" ];
requires = [ "timezone-restore.service" ];
};
# Restore timezone from persistent storage on boot (fallback for offline boots) pathConfig = {
systemd.services.timezone-restore = lib.mkIf (cfg.persistDir != null) { PathChanged = "/etc/localtime";
description = "Restore timezone from persistent storage"; Unit = "fix-localtime-symlink.service";
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

@ -206,6 +206,16 @@ 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,9 +5,7 @@
}: }:
let let
cfg = osConfig.ringofstorms.dePlasma; cfg = osConfig.ringofstorms.dePlasma;
inherit (lib) mkIf optionalAttrs; inherit (lib) mkIf;
# 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 = [
@ -296,8 +294,6 @@ 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,28 +38,40 @@
}, },
"common": { "common": {
"locked": { "locked": {
"path": "../../flakes/common", "dir": "flakes/common",
"type": "path" "lastModified": 1768255305,
"narHash": "sha256-XcXl5M0WNYhCCqE9qc9Aj2/2Jb/T0NHZnu2ZuVBvlHw=",
"ref": "refs/heads/master",
"rev": "15769eda748f6fcc6fdab04f79f14ed9b1ffc548",
"revCount": 1125,
"type": "git",
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
}, },
"original": { "original": {
"path": "../../flakes/common", "dir": "flakes/common",
"type": "path" "type": "git",
}, "url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
"parent": [] }
}, },
"de_plasma": { "de_plasma": {
"inputs": { "inputs": {
"plasma-manager": "plasma-manager" "plasma-manager": "plasma-manager"
}, },
"locked": { "locked": {
"path": "../../flakes/de_plasma", "dir": "flakes/de_plasma",
"type": "path" "lastModified": 1768255305,
"narHash": "sha256-XcXl5M0WNYhCCqE9qc9Aj2/2Jb/T0NHZnu2ZuVBvlHw=",
"ref": "refs/heads/master",
"rev": "15769eda748f6fcc6fdab04f79f14ed9b1ffc548",
"revCount": 1125,
"type": "git",
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
}, },
"original": { "original": {
"path": "../../flakes/de_plasma", "dir": "flakes/de_plasma",
"type": "path" "type": "git",
}, "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 = [
../../hosts/_shared_assets/wallpapers/pixel_neon.png ../../_shared_assets/wallpapers/pixel_neon.png
../../hosts/_shared_assets/wallpapers/pixel_neon_v.png ../../_shared_assets/wallpapers/pixel_neon_v.png
]; ];
}; };
}) })
@ -84,7 +84,6 @@
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,6 +32,8 @@
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).