diff --git a/flakes/common/nix_modules/timezone_auto.nix b/flakes/common/nix_modules/timezone_auto.nix index dac85767..3d2ee7f7 100644 --- a/flakes/common/nix_modules/timezone_auto.nix +++ b/flakes/common/nix_modules/timezone_auto.nix @@ -1,207 +1,64 @@ -{ config, lib, pkgs, ... }: -let - cfg = config.services.automatic-timezoned; - persistFile = if cfg.persistDir == null then null else "${cfg.persistDir}/timezone"; - tzdata = pkgs.tzdata; -in +{ lib, pkgs, ... }: { - options.services.automatic-timezoned.persistDir = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = '' - Absolute runtime directory used to persist the timezone for impermanence setups. + services.dbus.enable = lib.mkDefault true; + services.geoclue2.enable = true; - Important: this must be a normal filesystem path (a string like - "/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. + time.timeZone = null; + services.automatic-timezoned.enable = true; - When set, the timezone is saved to this directory and restored on boot, - allowing offline boots to use the last known timezone. - Set to null to disable persistence (default). - ''; + systemd.services.automatic-timezoned = { + after = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ]; + wants = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ]; + serviceConfig = { + ExecStartPre = "${lib.getExe' pkgs.coreutils "sleep"} 5"; + Restart = "on-failure"; + RestartSec = "10s"; + }; }; - config = { - assertions = [ - { - assertion = cfg.persistDir == null || lib.hasPrefix "/" cfg.persistDir; - message = "services.automatic-timezoned.persistDir must be an absolute path"; - } - ]; + systemd.services.automatic-timezoned-geoclue-agent = { + after = [ "dbus.socket" ]; + wants = [ "dbus.socket" ]; + }; - services.dbus.enable = lib.mkDefault true; - services.geoclue2.enable = true; + 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" ]; - time.timeZone = null; - services.automatic-timezoned.enable = true; + 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 - systemd.services.automatic-timezoned = { - after = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ] - ++ lib.optional (cfg.persistDir != null) "timezone-restore.service"; - wants = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ] - ++ lib.optional (cfg.persistDir != null) "timezone-restore.service"; - serviceConfig = { - ExecStartPre = "${lib.getExe' pkgs.coreutils "sleep"} 5"; - Restart = "on-failure"; - RestartSec = "10s"; - }; + 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 + ''; }; - systemd.services.automatic-timezoned-geoclue-agent = { - after = [ "dbus.socket" ]; - wants = [ "dbus.socket" ]; + unitConfig = { + ConditionPathIsSymbolicLink = "/etc/localtime"; }; + }; - # Ensure anything using timedate1 sees restored timezone first. - systemd.services.systemd-timedated = lib.mkIf (cfg.persistDir != null) { - after = [ "timezone-restore.service" ]; - wants = [ "timezone-restore.service" ]; - requires = [ "timezone-restore.service" ]; - }; + systemd.paths.fix-localtime-symlink = { + description = "Watch /etc/localtime for changes"; + wantedBy = [ "multi-user.target" ]; - # Restore timezone from persistent storage on boot (fallback for offline boots) - systemd.services.timezone-restore = lib.mkIf (cfg.persistDir != null) { - 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"; - }; + pathConfig = { + PathChanged = "/etc/localtime"; + Unit = "fix-localtime-symlink.service"; }; }; } diff --git a/flakes/de_plasma/de_plasma.nix b/flakes/de_plasma/de_plasma.nix index e001843e..01fd3b99 100644 --- a/flakes/de_plasma/de_plasma.nix +++ b/flakes/de_plasma/de_plasma.nix @@ -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 (mkIf cfg.gpu.amd.enable { diff --git a/flakes/de_plasma/home_manager/default.nix b/flakes/de_plasma/home_manager/default.nix index 4153b9da..e1d674d2 100644 --- a/flakes/de_plasma/home_manager/default.nix +++ b/flakes/de_plasma/home_manager/default.nix @@ -5,9 +5,7 @@ }: let cfg = osConfig.ringofstorms.dePlasma; - 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; + inherit (lib) mkIf; in { imports = [ @@ -296,8 +294,6 @@ in lookAndFeel = "org.kde.breezedark.desktop"; theme = "breeze-dark"; cursor.theme = "breeze_cursors"; - } // optionalAttrs (wallpaper != null) { - wallpaper = wallpaper; }; configFile = { diff --git a/hosts/juni/flake.lock b/hosts/juni/flake.lock index 8843bd3f..1d9d13d8 100644 --- a/hosts/juni/flake.lock +++ b/hosts/juni/flake.lock @@ -38,28 +38,40 @@ }, "common": { "locked": { - "path": "../../flakes/common", - "type": "path" + "dir": "flakes/common", + "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": { - "path": "../../flakes/common", - "type": "path" - }, - "parent": [] + "dir": "flakes/common", + "type": "git", + "url": "https://git.joshuabell.xyz/ringofstorms/dotfiles" + } }, "de_plasma": { "inputs": { "plasma-manager": "plasma-manager" }, "locked": { - "path": "../../flakes/de_plasma", - "type": "path" + "dir": "flakes/de_plasma", + "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": { - "path": "../../flakes/de_plasma", - "type": "path" - }, - "parent": [] + "dir": "flakes/de_plasma", + "type": "git", + "url": "https://git.joshuabell.xyz/ringofstorms/dotfiles" + } }, "flatpaks": { "inputs": { diff --git a/hosts/juni/flake.nix b/hosts/juni/flake.nix index 751ac7d8..52bbb47b 100644 --- a/hosts/juni/flake.nix +++ b/hosts/juni/flake.nix @@ -9,16 +9,16 @@ impermanence.url = "github:nix-community/impermanence"; # Use relative to get current version for testin - common.url = "path:../../flakes/common"; - # common.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/common"; + # common.url = "path:../../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"; # flatpaks.url = "path:../../flakes/flatpaks"; flatpaks.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/flatpaks"; # beszel.url = "path:../../flakes/beszel"; beszel.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/beszel"; - de_plasma.url = "path:../../flakes/de_plasma"; - # de_plasma.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/de_plasma"; + # de_plasma.url = "path:../../flakes/de_plasma"; + de_plasma.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/de_plasma"; opencode.url = "github:sst/opencode"; ros_neovim.url = "git+https://git.joshuabell.xyz/ringofstorms/nvim"; @@ -63,8 +63,8 @@ gpu.intel.enable = true; sddm.autologinUser = "josh"; wallpapers = [ - ../../hosts/_shared_assets/wallpapers/pixel_neon.png - ../../hosts/_shared_assets/wallpapers/pixel_neon_v.png + ../../_shared_assets/wallpapers/pixel_neon.png + ../../_shared_assets/wallpapers/pixel_neon_v.png ]; }; }) @@ -84,7 +84,6 @@ inputs.common.nixosModules.hardening inputs.common.nixosModules.nix_options inputs.common.nixosModules.timezone_auto - ({ services.automatic-timezoned.persistDir = "/persist/var/lib/timezone-persist"; }) inputs.common.nixosModules.tty_caps_esc inputs.common.nixosModules.zsh inputs.common.nixosModules.tailnet diff --git a/hosts/juni/impermanence.nix b/hosts/juni/impermanence.nix index c267211b..91536cad 100644 --- a/hosts/juni/impermanence.nix +++ b/hosts/juni/impermanence.nix @@ -32,6 +32,8 @@ files = [ "/machine-key.json" "/etc/machine-id" + "/etc/localtime" + "/etc/timezone" "/etc/adjtime" # NOTE: if you want mutable passwords across reboots, persist these, # but you must do a one-time migration (see notes in chat).