Compare commits
2 commits
c408693861
...
c0a1757cab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0a1757cab | ||
|
|
8be372e076 |
6 changed files with 215 additions and 91 deletions
|
|
@ -1,5 +1,34 @@
|
|||
{ 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
|
||||
{
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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).
|
||||
'';
|
||||
};
|
||||
|
||||
config = {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.persistDir == null || lib.hasPrefix "/" cfg.persistDir;
|
||||
message = "services.automatic-timezoned.persistDir must be an absolute path";
|
||||
}
|
||||
];
|
||||
|
||||
services.dbus.enable = lib.mkDefault true;
|
||||
services.geoclue2.enable = true;
|
||||
|
||||
|
|
@ -7,8 +36,10 @@
|
|||
services.automatic-timezoned.enable = true;
|
||||
|
||||
systemd.services.automatic-timezoned = {
|
||||
after = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ];
|
||||
wants = [ "dbus.socket" "systemd-timedated.service" "geoclue.service" ];
|
||||
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";
|
||||
|
|
@ -21,6 +52,117 @@
|
|||
wants = [ "dbus.socket" ];
|
||||
};
|
||||
|
||||
# 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" ];
|
||||
};
|
||||
|
||||
# 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" ];
|
||||
|
|
@ -61,4 +203,5 @@
|
|||
Unit = "fix-localtime-symlink.service";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,16 +206,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
|
||||
(mkIf cfg.gpu.amd.enable {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@
|
|||
}:
|
||||
let
|
||||
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
|
||||
{
|
||||
imports = [
|
||||
|
|
@ -294,6 +296,8 @@ in
|
|||
lookAndFeel = "org.kde.breezedark.desktop";
|
||||
theme = "breeze-dark";
|
||||
cursor.theme = "breeze_cursors";
|
||||
} // optionalAttrs (wallpaper != null) {
|
||||
wallpaper = wallpaper;
|
||||
};
|
||||
|
||||
configFile = {
|
||||
|
|
|
|||
36
hosts/juni/flake.lock
generated
36
hosts/juni/flake.lock
generated
|
|
@ -38,40 +38,28 @@
|
|||
},
|
||||
"common": {
|
||||
"locked": {
|
||||
"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"
|
||||
"path": "../../flakes/common",
|
||||
"type": "path"
|
||||
},
|
||||
"original": {
|
||||
"dir": "flakes/common",
|
||||
"type": "git",
|
||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||
}
|
||||
"path": "../../flakes/common",
|
||||
"type": "path"
|
||||
},
|
||||
"parent": []
|
||||
},
|
||||
"de_plasma": {
|
||||
"inputs": {
|
||||
"plasma-manager": "plasma-manager"
|
||||
},
|
||||
"locked": {
|
||||
"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"
|
||||
"path": "../../flakes/de_plasma",
|
||||
"type": "path"
|
||||
},
|
||||
"original": {
|
||||
"dir": "flakes/de_plasma",
|
||||
"type": "git",
|
||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||
}
|
||||
"path": "../../flakes/de_plasma",
|
||||
"type": "path"
|
||||
},
|
||||
"parent": []
|
||||
},
|
||||
"flatpaks": {
|
||||
"inputs": {
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
../../_shared_assets/wallpapers/pixel_neon.png
|
||||
../../_shared_assets/wallpapers/pixel_neon_v.png
|
||||
../../hosts/_shared_assets/wallpapers/pixel_neon.png
|
||||
../../hosts/_shared_assets/wallpapers/pixel_neon_v.png
|
||||
];
|
||||
};
|
||||
})
|
||||
|
|
@ -84,6 +84,7 @@
|
|||
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
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@
|
|||
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).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue