From ca6a7920e516aae88053b9a7bb849d344b91c664 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 18:41:32 -0500 Subject: [PATCH 01/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 5c4b412..3e3a70c 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "6c0029057aa50d0b4d6a0205c3ded890eb08979c"; + rev = "b369a286d06834eb18500e0b626a2d8177a0ef70"; }; }; overrides = { -- 2.39.5 From a7362a9ce950856ab72e0cca1e34c846750c4eaa Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 18:47:19 -0500 Subject: [PATCH 02/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 3e3a70c..1d8ae9d 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "b369a286d06834eb18500e0b626a2d8177a0ef70"; + rev = "393baa636530ee8d915592ad71ede692e75ea7f8"; }; }; overrides = { -- 2.39.5 From 56ca4bcccffafa18628fca6d27f2af09da203df4 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 18:50:25 -0500 Subject: [PATCH 03/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 1d8ae9d..8a53905 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "393baa636530ee8d915592ad71ede692e75ea7f8"; + rev = "9d972effe03813f69db93cfd29a718ba290639a0"; }; }; overrides = { -- 2.39.5 From 9bbcc8479ca305cd2400e7e38acddd66c6b09439 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 19:08:03 -0500 Subject: [PATCH 04/39] update external config for usda-dash --- inventory.nix | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/inventory.nix b/inventory.nix index 8a53905..99d31fe 100644 --- a/inventory.nix +++ b/inventory.nix @@ -64,7 +64,7 @@ # "external" = { # devices."remote" = builtins.fetchGit { # External module via Git # url = "https://github.com/example/config"; - # rev = "abc123..."; + # rev = "e1ccd7cc3e709afe4f50b0627e1c4bde49165014"; # }; # }; # ========== Lab Laptops ========== # Creates: nix-laptop1, nix-laptop2 @@ -122,7 +122,8 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "9d972effe03813f69db93cfd29a718ba290639a0"; + rev = "e1ccd7cc3e709afe4f50b0627e1c4bde49165014"; + submodules = true; }; }; overrides = { @@ -150,7 +151,7 @@ # # Option 1: fetchGit with specific revision (recommended for reproducibility) # "prod-server" = builtins.fetchGit { # url = "https://github.com/example/server-config"; - # rev = "abc123def456..."; # Full commit hash + # rev = "e1ccd7cc3e709afe4f50b0627e1c4bde49165014"; # Full commit hash # ref = "main"; # Optional: branch/tag name # }; # -- 2.39.5 From e8805d046f989702360c6ce10ddd890d53aaac9c Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 19:19:25 -0500 Subject: [PATCH 05/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 99d31fe..d731bee 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "e1ccd7cc3e709afe4f50b0627e1c4bde49165014"; + rev = "ce9ff2a7e8c3457424a8fa79d4a4ec72fb26e11b"; submodules = true; }; }; -- 2.39.5 From cee87825a7ae4df1deedceb7c5a9fa1a56549a37 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 19:22:09 -0500 Subject: [PATCH 06/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index d731bee..a16df47 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "ce9ff2a7e8c3457424a8fa79d4a4ec72fb26e11b"; + rev = "7d1e734ca4bb8527b0eed4820ae96338c4b66c69"; submodules = true; }; }; -- 2.39.5 From cfc3ebf903f48af61335e7dafdc62c8c9881a493 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 19:23:15 -0500 Subject: [PATCH 07/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index a16df47..a4dda69 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "7d1e734ca4bb8527b0eed4820ae96338c4b66c69"; + rev = "58333cf9624e2b8a18a39a0ae8e372317f1e8f43"; submodules = true; }; }; -- 2.39.5 From c0e3ca09f9254283f6d1c13a310b32eda8724fa8 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 19:25:52 -0500 Subject: [PATCH 08/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index a4dda69..9456f4c 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "58333cf9624e2b8a18a39a0ae8e372317f1e8f43"; + rev = "308c044c75f4609c4aa8a03cb4b30d2029e3b407"; submodules = true; }; }; -- 2.39.5 From a7ff2b3bf91dbb87fd1ce29dab734668e53075b0 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 19:32:54 -0500 Subject: [PATCH 09/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 9456f4c..6daa17f 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "308c044c75f4609c4aa8a03cb4b30d2029e3b407"; + rev = "a27ac14872ca5f10e74843704872cae2f452ec32"; submodules = true; }; }; -- 2.39.5 From 9343784871bad4b5ca2547cec64ee02fedfee74e Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 19:41:46 -0500 Subject: [PATCH 10/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 6daa17f..91ba803 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "a27ac14872ca5f10e74843704872cae2f452ec32"; + rev = "a65db0d2b402ef9ef3d7052dde7464a648d48112"; submodules = true; }; }; -- 2.39.5 From f9e1c32cf73441c96398005bcec6e0f611f53389 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 19:51:25 -0500 Subject: [PATCH 11/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 91ba803..aeadaec 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "a65db0d2b402ef9ef3d7052dde7464a648d48112"; + rev = "a1c53fd0e1ce0f67e23916a6aae89bfd5f38d0d8"; submodules = true; }; }; -- 2.39.5 From f3d204c6eae5505c938637c9a2c167fdfd0db88c Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 19:56:18 -0500 Subject: [PATCH 12/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index aeadaec..618f043 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "a1c53fd0e1ce0f67e23916a6aae89bfd5f38d0d8"; + rev = "6d441a14a148b8d0bfc63dcba6db57f32803d084"; submodules = true; }; }; -- 2.39.5 From f66603c3c4ed8975819d6880f62a7e0de891fa38 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 20:43:05 -0500 Subject: [PATCH 13/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 618f043..79b913c 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "6d441a14a148b8d0bfc63dcba6db57f32803d084"; + rev = "ba5642f6c1f0f0ad74a3ac57e0eeb6a117d479d9"; submodules = true; }; }; -- 2.39.5 From 986b3ef5b3e5d91a72dc892035d47f80e03068e0 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 20:47:33 -0500 Subject: [PATCH 14/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 79b913c..645ad17 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "ba5642f6c1f0f0ad74a3ac57e0eeb6a117d479d9"; + rev = "13c9e20dd665ac8e3a9a9edd2b6d1da6711b8716"; submodules = true; }; }; -- 2.39.5 From 8c32462cc0474cc74fe5789b5470e687e1db1f52 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 20:52:54 -0500 Subject: [PATCH 15/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 645ad17..08365b9 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "13c9e20dd665ac8e3a9a9edd2b6d1da6711b8716"; + rev = "250153d84ec09a2b8ceca3e04fd1c99ac73401fd"; submodules = true; }; }; -- 2.39.5 From 7d121bd3319531684354924accc2a8b77bba7dd5 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 21:01:36 -0500 Subject: [PATCH 16/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 08365b9..1a3dd74 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "250153d84ec09a2b8ceca3e04fd1c99ac73401fd"; + rev = "d76ccbb2c699deca48ccf287a63c4763a368471d"; submodules = true; }; }; -- 2.39.5 From 98c9f0d57b6d538e653651e0b1da957b383461db Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 21:07:34 -0500 Subject: [PATCH 17/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 1a3dd74..f417197 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "d76ccbb2c699deca48ccf287a63c4763a368471d"; + rev = "8dd6aa5edf23afd742dc5451ef8ded33e87c5b34"; submodules = true; }; }; -- 2.39.5 From 7d1c9ac17371382982b38fd4f1d80b584c52d233 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 21:12:06 -0500 Subject: [PATCH 18/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index f417197..92b716d 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "8dd6aa5edf23afd742dc5451ef8ded33e87c5b34"; + rev = "0e333ed19bccb5bb816ffb09de18edf16e8f473b"; submodules = true; }; }; -- 2.39.5 From ae316529f8961f36a49dd0ebeaa3c3f1693a4848 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 21:14:03 -0500 Subject: [PATCH 19/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 92b716d..d26aeba 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "0e333ed19bccb5bb816ffb09de18edf16e8f473b"; + rev = "a8cd30874527730c465722909e0b64d8ab2deafb"; submodules = true; }; }; -- 2.39.5 From 9168430a29effae2600b7e614194c0bb10584ea4 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 21:16:09 -0500 Subject: [PATCH 20/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index d26aeba..87e0fb8 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "a8cd30874527730c465722909e0b64d8ab2deafb"; + rev = "bae934b120d5859606db748b48c3660835e1e75c"; submodules = true; }; }; -- 2.39.5 From d9a8373bad6a45414c8dc5e6009df14193d5aa11 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 21:17:22 -0500 Subject: [PATCH 21/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 87e0fb8..2ce56b1 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "bae934b120d5859606db748b48c3660835e1e75c"; + rev = "a50e8938dff8e835e7ab46ae7e14519d46534703"; submodules = true; }; }; -- 2.39.5 From 2ad7d3f31165f89900bc1aa22fc63755871f7170 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Thu, 18 Dec 2025 21:18:38 -0500 Subject: [PATCH 22/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 2ce56b1..80cf829 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "a50e8938dff8e835e7ab46ae7e14519d46534703"; + rev = "762be05aa7df018ef6889e145614c8a84a990d48"; submodules = true; }; }; -- 2.39.5 From 9fa470299aaa39ddc792146c5db22ae430acf072 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 09:42:15 -0500 Subject: [PATCH 23/39] update external config for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 80cf829..8969d9a 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "762be05aa7df018ef6889e145614c8a84a990d48"; + rev = "a52962b08d21a4c0b1f4f07dcb55146f2078a5d8"; submodules = true; }; }; -- 2.39.5 From b2d5b4c01c5e091c113c758f2aa934bd30c59d3a Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 10:40:34 -0500 Subject: [PATCH 24/39] update the updater to support flags for privileged users --- sw/updater.nix | 148 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 136 insertions(+), 12 deletions(-) diff --git a/sw/updater.nix b/sw/updater.nix index 3d169e1..445d766 100644 --- a/sw/updater.nix +++ b/sw/updater.nix @@ -35,25 +35,149 @@ with lib; (pkgs.writeShellScriptBin "update-system" '' set -euo pipefail + RED='\033[31m'; NC='\033[0m' + + is_root() { [ "''${EUID:-$(id -u)}" -eq 0 ]; } + in_wheel() { id -nG 2>/dev/null | tr ' ' '\n' | grep -qx wheel; } + + # Service path for unprivileged (no flags) UNIT="update-system.service" - # Start following logs in the background - journalctl -fu "$UNIT" -n 0 --output=cat & - JPID=$! + # Defaults for flagged mode + DEFAULT_REMOTE_URL="https://git.factory.uga.edu/UGA-Innovation-Factory/athenix" + REPO_MODE="default" # default | local | remote + LOCAL_PATH="" + REMOTE_URL="" + BRANCH="" + IMPURE=0 - # Start the service and wait for it to finish - if systemctl start --wait --no-ask-password "$UNIT"; then - STATUS=$? - else - STATUS=$? + usage() { + cat >&2 <<'EOF' + usage: + update-system + update-system [--local-repo[=PATH]] [--remote-repo=URL] [--branch=BRANCH] [--impure] + + notes: + - No flags: runs the systemd service (works for unprivileged users via polkit). + - Any flags: only allowed for root or wheel (runs nixos-rebuild directly). + EOF + exit 2 + } + + # No flags -> polkit-friendly systemd service route + if [ "$#" -eq 0 ]; then + journalctl -fu "$UNIT" -n 0 --output=cat & + JPID=$! + + if systemctl start --wait --no-ask-password "$UNIT"; then + STATUS=$? + else + STATUS=$? + fi + + sleep 2 + kill "$JPID" 2>/dev/null || true + exit "$STATUS" fi - sleep 2 + # Flags -> require root or wheel + if ! is_root && ! in_wheel; then + printf "''${RED}error:''${NC} flags are only allowed for root or wheel. Run without flags (service path), or use sudo / add yourself to wheel.\n" >&2 + exit 2 + fi - # Kill the log follower - kill "$JPID" 2>/dev/null || true + # Parse flags + while [ "$#" -gt 0 ]; do + case "$1" in + --local-repo) + REPO_MODE="local" + LOCAL_PATH="''${HOME}/athenix" + shift + ;; + --local-repo=*) + REPO_MODE="local" + LOCAL_PATH="''${1#*=}" + shift + ;; + --remote-repo=*) + REPO_MODE="remote" + REMOTE_URL="''${1#*=}" + shift + ;; + --branch) + [ "$#" -ge 2 ] || usage + BRANCH="$2" + shift 2 + ;; + --branch=*) + BRANCH="''${1#*=}" + shift + ;; + --impure) + IMPURE=1 + shift + ;; + -h|--help) usage ;; + *) + printf "''${RED}error:''${NC} unknown argument: %s\n" "$1" >&2 + usage + ;; + esac + done - exit "$STATUS" + if [ "$REPO_MODE" = "local" ] && [ -n "$REMOTE_URL" ]; then + printf "''${RED}error:''${NC} can't use --local-repo and --remote-repo together.\n" >&2 + exit 2 + fi + + host="''${HOSTNAME:-$(hostname)}" + + # Build flake ref + if [ "$REPO_MODE" = "local" ]; then + [ -n "$LOCAL_PATH" ] || LOCAL_PATH="''${HOME}/athenix" + + # Clone default repo if missing + if [ ! -d "$LOCAL_PATH" ]; then + printf "local repo not found at %s, cloning %s...\n" "$LOCAL_PATH" "$DEFAULT_REMOTE_URL" >&2 + if [ -n "$BRANCH" ]; then + git clone --branch "$BRANCH" "$DEFAULT_REMOTE_URL" "$LOCAL_PATH" + else + git clone "$DEFAULT_REMOTE_URL" "$LOCAL_PATH" + fi + fi + + flakeRef="''${LOCAL_PATH}#''${host}" + else + url="''${REMOTE_URL:-$DEFAULT_REMOTE_URL}" + + if echo "$url" | grep -qE '^(https?|ssh)://'; then + base="git+''${url}" + elif echo "$url" | grep -qE '^[^/@:]+@[^/:]+:'; then + # scp-style: git@host:owner/repo(.git) + userhost="''${url%%:*}" + path="''${url#*:}" + base="git+ssh://''${userhost}/''${path}" + else + base="''${url}" + fi + + if [ -n "$BRANCH" ]; then + if echo "$base" | grep -q '?'; then + base="''${base}&ref=''${BRANCH}" + else + base="''${base}?ref=''${BRANCH}" + fi + fi + + flakeRef="''${base}#''${host}" + fi + + impureFlag="" + if [ "$IMPURE" -eq 1 ]; then + impureFlag="--impure" + fi + + exec nixos-rebuild switch --refresh --print-build-logs $impureFlag --flake "''${flakeRef}" '') ]; -- 2.39.5 From 60f908baa13f97240618b8ee41269dec77b3eb98 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 10:51:35 -0500 Subject: [PATCH 25/39] updater to not require sudo prefix for flags --- sw/updater.nix | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/sw/updater.nix b/sw/updater.nix index 445d766..b8a3cbe 100644 --- a/sw/updater.nix +++ b/sw/updater.nix @@ -43,6 +43,14 @@ with lib; # Service path for unprivileged (no flags) UNIT="update-system.service" + # Figure out the "real" invoking user, even under sudo. + INVOKER_USER="''${SUDO_USER:-$(id -un)}" + INVOKER_HOME="$(getent passwd "$INVOKER_USER" | cut -d: -f6)" + if [ -z "$INVOKER_HOME" ]; then + # fallback if getent is weird in some containers + INVOKER_HOME="''${HOME:-/home/$INVOKER_USER}" + fi + # Defaults for flagged mode DEFAULT_REMOTE_URL="https://git.factory.uga.edu/UGA-Innovation-Factory/athenix" REPO_MODE="default" # default | local | remote @@ -91,7 +99,7 @@ with lib; case "$1" in --local-repo) REPO_MODE="local" - LOCAL_PATH="''${HOME}/athenix" + LOCAL_PATH="$INVOKER_HOME/athenix" shift ;; --local-repo=*) @@ -134,7 +142,7 @@ with lib; # Build flake ref if [ "$REPO_MODE" = "local" ]; then - [ -n "$LOCAL_PATH" ] || LOCAL_PATH="''${HOME}/athenix" + [ -n "$LOCAL_PATH" ] || LOCAL_PATH="$INVOKER_HOME/athenix" # Clone default repo if missing if [ ! -d "$LOCAL_PATH" ]; then @@ -177,7 +185,14 @@ with lib; impureFlag="--impure" fi - exec nixos-rebuild switch --refresh --print-build-logs $impureFlag --flake "''${flakeRef}" + # If not root, re-exec via sudo to do the actual switch. + # Preserve our computed invoker context so sudo doesn't "helpfully" change it. + if ! is_root; then + exec sudo --preserve-env=HOME,USER,LOGNAME \ + nixos-rebuild switch --refresh --print-build-logs $impureFlag --flake "$flakeRef" + else + exec nixos-rebuild switch --refresh --print-build-logs $impureFlag --flake "$flakeRef" + fi '') ]; -- 2.39.5 From 899dfab866c50946f56a38100e7e69a667bb33b7 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 13:08:59 -0500 Subject: [PATCH 26/39] add a reference updater tool --- sw/default.nix | 1 + sw/update-ref.nix | 416 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 417 insertions(+) create mode 100644 sw/update-ref.nix diff --git a/sw/default.nix b/sw/default.nix index 97f8c64..6db06d4 100644 --- a/sw/default.nix +++ b/sw/default.nix @@ -29,6 +29,7 @@ in ./python.nix ./ghostty.nix ./updater.nix + ./update-ref.nix ]; options.athenix.sw = { diff --git a/sw/update-ref.nix b/sw/update-ref.nix new file mode 100644 index 0000000..b9c1a69 --- /dev/null +++ b/sw/update-ref.nix @@ -0,0 +1,416 @@ +{ pkgs, ... }: +{ + environment.systemPackages = [ + pkgs.python3 + (pkgs.writeShellScriptBin "update-ref" '' + set -euo pipefail + + RED='\033[31m'; YEL='\033[33m'; NC='\033[0m' + die() { printf "''${RED}error:''${NC} %s\n" "$*" >&2; exit 2; } + warn() { printf "''${YEL}warning:''${NC} %s\n" "$*" >&2; } + + # ---- Must be in a git repo (current dir) ---- + git rev-parse --is-inside-work-tree >/dev/null 2>&1 || die "This directory is not a git project" + CUR_REPO_ROOT="$(git rev-parse --show-toplevel)" + CUR_BRANCH="$(git rev-parse --abbrev-ref HEAD)" + + # ---- Athenix checkout ---- + ATHENIX_DIR="$HOME/athenix" + ATHENIX_REPO="" + ATHENIX_BRANCH="" + + # ---- Git automation options for CURRENT repo ---- + COMMIT_MSG="" + PUSH_SPEC="" + + # ---- Push + mode controls ---- + PUSH_SET=0 # user explicitly set push true/false + DO_PUSH=0 # final push decision + MODE_FORCE="" # "", "local", "remote" (from -l/-r) + + # ---- Required subcommand ---- + TARGET="" # user= OR system=: + + usage() { + cat >&2 <<'EOF' + usage: + update-ref [--athenix-repo=PATH|URL|-R PATH|URL] [--athenix-branch=BRANCH|-b BRANCH] + [-m "msg" | --message "msg"] + [-p[=false] [remote[=URL]] | --push[=false] [remote[=URL]]] + [--make-local|-l] [--make-remote|-r] + user= | system=: + + push default: + - determined from the existing builtins.fetchGit url in the target entry: + remote url -> push=true + file/local -> push=false + - if the entry doesn't exist: push=false unless --make-remote + + EOF + exit 2 + } + + is_remote_url() { + # https://, http://, ssh://, or scp-style git@host:org/repo + printf "%s" "$1" | grep -qE '^(https?|ssh)://|^[^/@:]+@[^/:]+:' + } + + derive_full_hostname() { + devtype="$1" + hostkey="$2" + if printf "%s" "$hostkey" | grep -q '-' || printf "%s" "$hostkey" | grep -q "^$devtype"; then + printf "%s" "$hostkey" + elif printf "%s" "$hostkey" | grep -qE '^[0-9]+$'; then + printf "%s" "$devtype$hostkey" + else + printf "%s" "$devtype-$hostkey" + fi + } + + extract_existing_fetch_url() { + # args: mode file username key + python3 - "$1" "$2" "$3" "$4" <<'PY' + import sys, re, pathlib + + mode = sys.argv[1] # user | system + path = pathlib.Path(sys.argv[2]) + username = sys.argv[3] # user mode + key = sys.argv[4] # system mode: exact string key + + text = path.read_text() + + def find_fetch_block_user(u): + m = re.search(r'(?s)\n\s*' + re.escape(u) + r'\.external\s*=\s*builtins\.fetchGit\s*\{(.*?)\n\s*\};', text) + return m.group(1) if m else None + + def find_fetch_block_system(k): + m = re.search(r'(?s)\n\s*"' + re.escape(k) + r'"\s*=\s*builtins\.fetchGit\s*\{(.*?)\n\s*\};', text) + return m.group(1) if m else None + + def extract_url(block): + if not block: + return "" + m = re.search(r'url\s*=\s*"([^"]+)"\s*;', block) + return m.group(1) if m else "" + + block = None + if mode == "user": + block = find_fetch_block_user(username) + else: + block = find_fetch_block_system(key) + + print(extract_url(block)) + PY + } + + # ---- Parse args ---- + while [ "$#" -gt 0 ]; do + case "$1" in + user=*|system=*) + [ -z "$TARGET" ] || die "Only one subcommand allowed (user=... or system=...)" + TARGET="$1" + shift + ;; + --athenix-repo=*) + ATHENIX_REPO="''${1#*=}" + shift + ;; + -R) + [ "$#" -ge 2 ] || usage + ATHENIX_REPO="$2" + shift 2 + ;; + --athenix-branch=*) + ATHENIX_BRANCH="''${1#*=}" + shift + ;; + -b) + [ "$#" -ge 2 ] || usage + ATHENIX_BRANCH="$2" + shift 2 + ;; + -m|--message) + [ "$#" -ge 2 ] || usage + COMMIT_MSG="$2" + shift 2 + ;; + + -p|--push) + PUSH_SET=1 + DO_PUSH=1 + # optional next token remote or remote=URL (only if it doesn't look like another flag/subcommand) + if [ "$#" -ge 2 ] && printf "%s" "$2" | grep -qvE '^(user=|system=|--|-l$|-r$)'; then + PUSH_SPEC="$2" + shift 2 + else + shift + fi + ;; + -p=*|--push=*) + PUSH_SET=1 + val="''${1#*=}" + case "$val" in + false|0|no|off) DO_PUSH=0 ;; + true|1|yes|on|"") DO_PUSH=1 ;; + *) die "Invalid value for --push: $val (use true/false)" ;; + esac + shift + ;; + + --make-local|-l) + MODE_FORCE="local" + shift + ;; + --make-remote|-r) + MODE_FORCE="remote" + shift + ;; + + -h|--help) usage ;; + *) die "Unknown argument: $1" ;; + esac + done + + [ -n "$TARGET" ] || die "Missing required subcommand: user= or system=:" + + # ---- Validate athenix checkout ---- + [ -d "$ATHENIX_DIR" ] || die "$ATHENIX_DIR does not exist" + git -C "$ATHENIX_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1 || die "$ATHENIX_DIR is not a git project" + if [ -z "$ATHENIX_BRANCH" ]; then + ATHENIX_BRANCH="$(git -C "$ATHENIX_DIR" rev-parse --abbrev-ref HEAD)" + fi + + # ---- Determine target file + identifiers ---- + FILE="" + MODE="" + USERNAME="" + DEVTYPE="" + HOSTKEY="" + + case "$TARGET" in + user=*) + MODE="user" + USERNAME="''${TARGET#user=}" + [ -n "$USERNAME" ] || die "user=: username missing" + FILE="$ATHENIX_DIR/users.nix" + ;; + system=*) + MODE="system" + RHS="''${TARGET#system=}" + printf "%s" "$RHS" | grep -q ':' || die "system=... must be system=:" + DEVTYPE="''${RHS%%:*}" + HOSTKEY="''${RHS#*:}" + [ -n "$DEVTYPE" ] || die "system=:: device-type missing" + [ -n "$HOSTKEY" ] || die "system=:: hostkey missing" + FILE="$ATHENIX_DIR/inventory.nix" + ;; + esac + + [ -f "$FILE" ] || die "File not found: $FILE" + + # ---- Determine existing fetchGit url in the entry (for push default) ---- + EXISTING_URL="" + ENTRY_EXISTS=0 + + if [ "$MODE" = "user" ]; then + EXISTING_URL="$(extract_existing_fetch_url user "$FILE" "$USERNAME" "")" + [ -n "$EXISTING_URL" ] && ENTRY_EXISTS=1 || true + else + FULL="$(derive_full_hostname "$DEVTYPE" "$HOSTKEY")" + EXISTING_URL="$(extract_existing_fetch_url system "$FILE" "" "$HOSTKEY")" + if [ -n "$EXISTING_URL" ]; then + ENTRY_EXISTS=1 + elif [ "$FULL" != "$HOSTKEY" ]; then + EXISTING_URL="$(extract_existing_fetch_url system "$FILE" "" "$FULL")" + [ -n "$EXISTING_URL" ] && ENTRY_EXISTS=1 || true + fi + fi + + # ---- Default push based on existing entry url unless user explicitly set it ---- + if [ "$PUSH_SET" -eq 0 ]; then + if [ "$ENTRY_EXISTS" -eq 1 ] && is_remote_url "$EXISTING_URL"; then + DO_PUSH=1 + else + # entry missing or local/file url + if [ "$MODE_FORCE" = "remote" ]; then + DO_PUSH=1 + else + DO_PUSH=0 + fi + fi + fi + + # If forcing local and user didn't explicitly demand push, push defaults off. + if [ "$MODE_FORCE" = "local" ] && [ "$PUSH_SET" -eq 0 ]; then + DO_PUSH=0 + fi + + # ---- Dirty check prompt ---- + if [ -n "$(git status --porcelain)" ]; then + warn "This branch has untracked or uncommitted changes. Would you like to add, commit''${DO_PUSH:+, and push}? [y/N] " + read -r ans || true + case "''${ans:-N}" in + y|Y|yes|YES) + git add -A + if ! git diff --cached --quiet; then + if [ -n "$COMMIT_MSG" ]; then + git commit -m "$COMMIT_MSG" + else + git commit + fi + else + warn "No staged changes to commit." + fi + ;; + *) + warn "Proceeding without committing. (rev will be last committed HEAD.)" + ;; + esac + elif [ -n "$COMMIT_MSG" ]; then + # clean tree but message provided: nothing to do + : + fi + + # ---- Push current repo if requested ---- + PUSH_REMOTE_URL="" + if [ "$DO_PUSH" -eq 1 ]; then + if [ -n "$PUSH_SPEC" ]; then + if printf "%s" "$PUSH_SPEC" | grep -q '='; then + REM_NAME="''${PUSH_SPEC%%=*}" + REM_URL="''${PUSH_SPEC#*=}" + [ -n "$REM_NAME" ] || die "--push remote-name=URL: remote-name missing" + [ -n "$REM_URL" ] || die "--push remote-name=URL: URL missing" + + if git remote get-url "$REM_NAME" >/dev/null 2>&1; then + git remote set-url "$REM_NAME" "$REM_URL" + else + git remote add "$REM_NAME" "$REM_URL" + fi + + git push -u "$REM_NAME" "$CUR_BRANCH" + PUSH_REMOTE_URL="$REM_URL" + else + REM_NAME="$PUSH_SPEC" + git push -u "$REM_NAME" "$CUR_BRANCH" + PUSH_REMOTE_URL="$(git remote get-url "$REM_NAME")" + fi + else + # default: push to upstream, error if none + if ! git rev-parse --abbrev-ref --symbolic-full-name @{u} >/dev/null 2>&1; then + die "No upstream is set. Set a default upstream with \"git branch -u /\"" + fi + git push + UPSTREAM_REMOTE="$(git rev-parse --abbrev-ref --symbolic-full-name @{u} | cut -d/ -f1)" + PUSH_REMOTE_URL="$(git remote get-url "$UPSTREAM_REMOTE")" + fi + fi + + # ---- Always get current HEAD hash (after optional commit/push) ---- + CUR_REV="$(git -C "$CUR_REPO_ROOT" rev-parse HEAD)" + + # ---- Determine url to write into fetchGit (respecting make-local/make-remote) ---- + if [ "$MODE_FORCE" = "local" ]; then + FETCH_URL="file://$CUR_REPO_ROOT" + elif [ "$MODE_FORCE" = "remote" ]; then + if [ "$DO_PUSH" -eq 1 ]; then + FETCH_URL="$PUSH_REMOTE_URL" + elif [ "$ENTRY_EXISTS" -eq 1 ] && [ -n "$EXISTING_URL" ] && is_remote_url "$EXISTING_URL"; then + FETCH_URL="$EXISTING_URL" + else + CUR_ORIGIN="$(git remote get-url origin 2>/dev/null || true)" + [ -n "$CUR_ORIGIN" ] && is_remote_url "$CUR_ORIGIN" || die "--make-remote requires a remote url (set origin or use -p remote=URL)" + FETCH_URL="$CUR_ORIGIN" + fi + else + if [ "$DO_PUSH" -eq 1 ]; then + FETCH_URL="$PUSH_REMOTE_URL" + else + FETCH_URL="file://$CUR_REPO_ROOT" + fi + fi + + # ---- Rewrite athenix file ---- + python3 - "$MODE" "$FILE" "$FETCH_URL" "$CUR_REV" "$USERNAME" "$DEVTYPE" "$HOSTKEY" <<'PY' + import sys, re, pathlib + + mode = sys.argv[1] + path = pathlib.Path(sys.argv[2]) + fetch_url = sys.argv[3] + rev = sys.argv[4] + username = sys.argv[5] + devtype = sys.argv[6] + hostkey = sys.argv[7] + + text = path.read_text() + + def mk_fetch_block(url, rev): + return ( + 'builtins.fetchGit {\n' + f' url = "{url}";\n' + f' rev = "{rev}";\n' + ' submodules=true;\n' + ' }' + ) + + fetch_block = mk_fetch_block(fetch_url, rev) + + def full_hostname(devtype, hostkey): + if hostkey.startswith(devtype) or "-" in hostkey: + return hostkey + if hostkey.isdigit(): + return f"{devtype}{hostkey}" + return f"{devtype}-{hostkey}" + + if mode == "user": + m = re.search(r'(?s)(athenix\.users\s*=\s*\{)(.*?)(\n\s*\};)', text) + if not m: + raise SystemExit("error: could not locate `athenix.users = { ... };` block") + + head, body, tail = m.group(1), m.group(2), m.group(3) + + key_re = re.compile(r'(?s)(\n\s*' + re.escape(username) + r'\.external\s*=\s*)builtins\.fetchGit\s*\{.*?\};') + if key_re.search(body): + body = key_re.sub(lambda mm: mm.group(1) + fetch_block + ';', body) + else: + body = body + f'\n {username}.external = {fetch_block};\n' + + new_text = text[:m.start()] + head + body + tail + text[m.end():] + path.write_text(new_text) + sys.exit(0) + + elif mode == "system": + block_re = re.compile(r'(?s)(\n\s*' + re.escape(devtype) + r'\s*=\s*\{)(.*?)(\n\s*\};)') + m = block_re.search(text) + if not m: + raise SystemExit(f"error: could not locate `{devtype} = {{ ... }};` block in inventory.nix") + + head, body, tail = m.group(1), m.group(2), m.group(3) + + candidates = [hostkey, full_hostname(devtype, hostkey)] + candidates = list(dict.fromkeys(candidates)) + + replaced = False + for k in candidates: + entry_re = re.compile(r'(?s)(\n\s*"' + re.escape(k) + r'"\s*=\s*)builtins\.fetchGit\s*\{.*?\};') + if entry_re.search(body): + body = entry_re.sub(lambda mm: mm.group(1) + fetch_block + ';', body) + replaced = True + break + + if not replaced: + body = body + f'\n "{hostkey}" = {fetch_block};\n' + + new_text = text[:m.start()] + head + body + tail + text[m.end():] + path.write_text(new_text) + sys.exit(0) + + else: + raise SystemExit("error: unknown mode") + PY + + printf "updated %s (athenix branch: %s)\n" "$FILE" "$ATHENIX_BRANCH" >&2 + printf " url = %s\n" "$FETCH_URL" >&2 + printf " rev = %s\n" "$CUR_REV" >&2 + '') + ]; +} \ No newline at end of file -- 2.39.5 From 2a52283e29e6ff809be189d2a620e84773256665 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 15:25:20 -0500 Subject: [PATCH 27/39] improve reference updater tool --- sw/builders/services.nix | 4 +- sw/update-ref.nix | 387 ++++++++++++++++++++++----------------- 2 files changed, 219 insertions(+), 172 deletions(-) diff --git a/sw/builders/services.nix b/sw/builders/services.nix index b34aa6e..93981d0 100644 --- a/sw/builders/services.nix +++ b/sw/builders/services.nix @@ -18,7 +18,7 @@ mkIf builderCfg.giteaRunner.enable { tokenFile = builderCfg.giteaRunner.tokenFile; labels = builderCfg.giteaRunner.extraLabels; name = builderCfg.giteaRunner.name; - + # Run as engr-ugaif user to access SSH keys settings = { runner = { @@ -38,7 +38,7 @@ mkIf builderCfg.giteaRunner.enable { # Run as engr-ugaif user User = mkForce "engr-ugaif"; Group = mkForce "users"; - + # Give the service more time to stop cleanly TimeoutStopSec = mkForce 60; diff --git a/sw/update-ref.nix b/sw/update-ref.nix index b9c1a69..7c2a81d 100644 --- a/sw/update-ref.nix +++ b/sw/update-ref.nix @@ -1,7 +1,8 @@ { pkgs, ... }: { - environment.systemPackages = [ - pkgs.python3 + environment.systemPackages = with pkgs; [ + python3 + git (pkgs.writeShellScriptBin "update-ref" '' set -euo pipefail @@ -9,46 +10,37 @@ die() { printf "''${RED}error:''${NC} %s\n" "$*" >&2; exit 2; } warn() { printf "''${YEL}warning:''${NC} %s\n" "$*" >&2; } - # ---- Must be in a git repo (current dir) ---- + usage() { + cat >&2 <<'EOF' + usage: + update-ref [-R PATH|--athenix-repo=PATH] [-b BRANCH|--athenix-branch=BRANCH] + [-m "msg"|--message "msg"] + [-p[=false] [remote[=URL]]|--push[=false] [remote[=URL]]] + [--make-local|-l] [--make-remote|-r] + user= | system=: + EOF + exit 2 + } + + # --- must be in a git repo (current dir) --- git rev-parse --is-inside-work-tree >/dev/null 2>&1 || die "This directory is not a git project" CUR_REPO_ROOT="$(git rev-parse --show-toplevel)" CUR_BRANCH="$(git rev-parse --abbrev-ref HEAD)" - # ---- Athenix checkout ---- + # --- athenix checkout (working tree) --- ATHENIX_DIR="$HOME/athenix" - ATHENIX_REPO="" ATHENIX_BRANCH="" - # ---- Git automation options for CURRENT repo ---- + # --- current repo automation --- COMMIT_MSG="" PUSH_SPEC="" - # ---- Push + mode controls ---- - PUSH_SET=0 # user explicitly set push true/false - DO_PUSH=0 # final push decision - MODE_FORCE="" # "", "local", "remote" (from -l/-r) + # --- push / url mode --- + PUSH_SET=0 + DO_PUSH=0 + MODE_FORCE="" # "", local, remote - # ---- Required subcommand ---- - TARGET="" # user= OR system=: - - usage() { - cat >&2 <<'EOF' - usage: - update-ref [--athenix-repo=PATH|URL|-R PATH|URL] [--athenix-branch=BRANCH|-b BRANCH] - [-m "msg" | --message "msg"] - [-p[=false] [remote[=URL]] | --push[=false] [remote[=URL]]] - [--make-local|-l] [--make-remote|-r] - user= | system=: - - push default: - - determined from the existing builtins.fetchGit url in the target entry: - remote url -> push=true - file/local -> push=false - - if the entry doesn't exist: push=false unless --make-remote - - EOF - exit 2 - } + TARGET="" is_remote_url() { # https://, http://, ssh://, or scp-style git@host:org/repo @@ -56,8 +48,7 @@ } derive_full_hostname() { - devtype="$1" - hostkey="$2" + devtype="$1"; hostkey="$2" if printf "%s" "$hostkey" | grep -q '-' || printf "%s" "$hostkey" | grep -q "^$devtype"; then printf "%s" "$hostkey" elif printf "%s" "$hostkey" | grep -qE '^[0-9]+$'; then @@ -71,77 +62,57 @@ # args: mode file username key python3 - "$1" "$2" "$3" "$4" <<'PY' import sys, re, pathlib + mode, file, username, key = sys.argv[1:5] + t = pathlib.Path(file).read_text() - mode = sys.argv[1] # user | system - path = pathlib.Path(sys.argv[2]) - username = sys.argv[3] # user mode - key = sys.argv[4] # system mode: exact string key - - text = path.read_text() - - def find_fetch_block_user(u): - m = re.search(r'(?s)\n\s*' + re.escape(u) + r'\.external\s*=\s*builtins\.fetchGit\s*\{(.*?)\n\s*\};', text) - return m.group(1) if m else None - - def find_fetch_block_system(k): - m = re.search(r'(?s)\n\s*"' + re.escape(k) + r'"\s*=\s*builtins\.fetchGit\s*\{(.*?)\n\s*\};', text) - return m.group(1) if m else None - - def extract_url(block): + def url_from_block(block: str) -> str: if not block: return "" m = re.search(r'url\s*=\s*"([^"]+)"\s*;', block) return m.group(1) if m else "" - block = None if mode == "user": - block = find_fetch_block_user(username) + m = re.search(r'(?s)\n\s*' + re.escape(username) + r'\.external\s*=\s*builtins\.fetchGit\s*\{(.*?)\n\s*\};', t) + block = m.group(1) if m else "" + print(url_from_block(block)) else: - block = find_fetch_block_system(key) - - print(extract_url(block)) + m = re.search(r'(?s)\n\s*"' + re.escape(key) + r'"\s*=\s*builtins\.fetchGit\s*\{(.*?)\n\s*\};', t) + block = m.group(1) if m else "" + print(url_from_block(block)) PY } - # ---- Parse args ---- + # --- parse args --- while [ "$#" -gt 0 ]; do case "$1" in user=*|system=*) [ -z "$TARGET" ] || die "Only one subcommand allowed (user=... or system=...)" - TARGET="$1" - shift + TARGET="$1"; shift ;; --athenix-repo=*) - ATHENIX_REPO="''${1#*=}" - shift + ATHENIX_DIR="''${1#*=}"; shift ;; -R) [ "$#" -ge 2 ] || usage - ATHENIX_REPO="$2" - shift 2 + ATHENIX_DIR="$2"; shift 2 ;; --athenix-branch=*) - ATHENIX_BRANCH="''${1#*=}" - shift + ATHENIX_BRANCH="''${1#*=}"; shift ;; -b) [ "$#" -ge 2 ] || usage - ATHENIX_BRANCH="$2" - shift 2 + ATHENIX_BRANCH="$2"; shift 2 ;; -m|--message) [ "$#" -ge 2 ] || usage - COMMIT_MSG="$2" - shift 2 + COMMIT_MSG="$2"; shift 2 ;; -p|--push) - PUSH_SET=1 - DO_PUSH=1 + PUSH_SET=1; DO_PUSH=1 # optional next token remote or remote=URL (only if it doesn't look like another flag/subcommand) - if [ "$#" -ge 2 ] && printf "%s" "$2" | grep -qvE '^(user=|system=|--|-l$|-r$)'; then - PUSH_SPEC="$2" - shift 2 + if [ "$#" -ge 2 ] && printf "%s" "$2" | grep -qvE '^(user=|system=|--|-l$|-r$)$'; then + PUSH_SPEC="$2"; shift 2 else shift fi @@ -157,15 +128,8 @@ shift ;; - --make-local|-l) - MODE_FORCE="local" - shift - ;; - --make-remote|-r) - MODE_FORCE="remote" - shift - ;; - + --make-local|-l) MODE_FORCE="local"; shift ;; + --make-remote|-r) MODE_FORCE="remote"; shift ;; -h|--help) usage ;; *) die "Unknown argument: $1" ;; esac @@ -173,20 +137,35 @@ [ -n "$TARGET" ] || die "Missing required subcommand: user= or system=:" - # ---- Validate athenix checkout ---- + # --- validate athenix working tree path --- [ -d "$ATHENIX_DIR" ] || die "$ATHENIX_DIR does not exist" - git -C "$ATHENIX_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1 || die "$ATHENIX_DIR is not a git project" - if [ -z "$ATHENIX_BRANCH" ]; then - ATHENIX_BRANCH="$(git -C "$ATHENIX_DIR" rev-parse --abbrev-ref HEAD)" + git -C "$ATHENIX_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1 || die "$ATHENIX_DIR is not a git project (athenix checkout)" + + # --- -b behavior: fork/switch athenix working tree into branch --- + if [ -n "$ATHENIX_BRANCH" ]; then + ATH_CUR_BRANCH="$(git -C "$ATHENIX_DIR" rev-parse --abbrev-ref HEAD)" + if [ "$ATH_CUR_BRANCH" != "$ATHENIX_BRANCH" ]; then + if git -C "$ATHENIX_DIR" show-ref --verify --quiet "refs/heads/$ATHENIX_BRANCH"; then + warn "Branch '$ATHENIX_BRANCH' already exists in $ATHENIX_DIR." + warn "Delete and recreate it from current branch '$ATH_CUR_BRANCH' state? [y/N] " + read -r ans || true + case "''${ans:-N}" in + y|Y|yes|YES) + git -C "$ATHENIX_DIR" branch -D "$ATHENIX_BRANCH" + git -C "$ATHENIX_DIR" switch -c "$ATHENIX_BRANCH" + ;; + *) + git -C "$ATHENIX_DIR" switch "$ATHENIX_BRANCH" + ;; + esac + else + git -C "$ATHENIX_DIR" switch -c "$ATHENIX_BRANCH" + fi + fi fi - # ---- Determine target file + identifiers ---- - FILE="" - MODE="" - USERNAME="" - DEVTYPE="" - HOSTKEY="" - + # --- target file + identifiers --- + MODE=""; FILE=""; USERNAME=""; DEVTYPE=""; HOSTKEY="" case "$TARGET" in user=*) MODE="user" @@ -205,13 +184,11 @@ FILE="$ATHENIX_DIR/inventory.nix" ;; esac - [ -f "$FILE" ] || die "File not found: $FILE" - # ---- Determine existing fetchGit url in the entry (for push default) ---- + # --- push default based on existing entry url in the target file --- EXISTING_URL="" ENTRY_EXISTS=0 - if [ "$MODE" = "user" ]; then EXISTING_URL="$(extract_existing_fetch_url user "$FILE" "$USERNAME" "")" [ -n "$EXISTING_URL" ] && ENTRY_EXISTS=1 || true @@ -226,26 +203,19 @@ fi fi - # ---- Default push based on existing entry url unless user explicitly set it ---- if [ "$PUSH_SET" -eq 0 ]; then if [ "$ENTRY_EXISTS" -eq 1 ] && is_remote_url "$EXISTING_URL"; then DO_PUSH=1 else - # entry missing or local/file url - if [ "$MODE_FORCE" = "remote" ]; then - DO_PUSH=1 - else - DO_PUSH=0 - fi + DO_PUSH=0 + [ "$MODE_FORCE" = "remote" ] && DO_PUSH=1 || true fi fi - - # If forcing local and user didn't explicitly demand push, push defaults off. if [ "$MODE_FORCE" = "local" ] && [ "$PUSH_SET" -eq 0 ]; then DO_PUSH=0 fi - # ---- Dirty check prompt ---- + # --- if current repo dirty, prompt --- if [ -n "$(git status --porcelain)" ]; then warn "This branch has untracked or uncommitted changes. Would you like to add, commit''${DO_PUSH:+, and push}? [y/N] " read -r ans || true @@ -253,25 +223,16 @@ y|Y|yes|YES) git add -A if ! git diff --cached --quiet; then - if [ -n "$COMMIT_MSG" ]; then - git commit -m "$COMMIT_MSG" - else - git commit - fi + if [ -n "$COMMIT_MSG" ]; then git commit -m "$COMMIT_MSG"; else git commit; fi else warn "No staged changes to commit." fi ;; - *) - warn "Proceeding without committing. (rev will be last committed HEAD.)" - ;; + *) warn "Proceeding without committing. (rev will be last committed HEAD.)" ;; esac - elif [ -n "$COMMIT_MSG" ]; then - # clean tree but message provided: nothing to do - : fi - # ---- Push current repo if requested ---- + # --- push current repo if requested --- PUSH_REMOTE_URL="" if [ "$DO_PUSH" -eq 1 ]; then if [ -n "$PUSH_SPEC" ]; then @@ -280,13 +241,11 @@ REM_URL="''${PUSH_SPEC#*=}" [ -n "$REM_NAME" ] || die "--push remote-name=URL: remote-name missing" [ -n "$REM_URL" ] || die "--push remote-name=URL: URL missing" - if git remote get-url "$REM_NAME" >/dev/null 2>&1; then git remote set-url "$REM_NAME" "$REM_URL" else git remote add "$REM_NAME" "$REM_URL" fi - git push -u "$REM_NAME" "$CUR_BRANCH" PUSH_REMOTE_URL="$REM_URL" else @@ -295,7 +254,6 @@ PUSH_REMOTE_URL="$(git remote get-url "$REM_NAME")" fi else - # default: push to upstream, error if none if ! git rev-parse --abbrev-ref --symbolic-full-name @{u} >/dev/null 2>&1; then die "No upstream is set. Set a default upstream with \"git branch -u /\"" fi @@ -305,10 +263,9 @@ fi fi - # ---- Always get current HEAD hash (after optional commit/push) ---- CUR_REV="$(git -C "$CUR_REPO_ROOT" rev-parse HEAD)" - # ---- Determine url to write into fetchGit (respecting make-local/make-remote) ---- + # --- choose URL to write into fetchGit --- if [ "$MODE_FORCE" = "local" ]; then FETCH_URL="file://$CUR_REPO_ROOT" elif [ "$MODE_FORCE" = "remote" ]; then @@ -322,14 +279,10 @@ FETCH_URL="$CUR_ORIGIN" fi else - if [ "$DO_PUSH" -eq 1 ]; then - FETCH_URL="$PUSH_REMOTE_URL" - else - FETCH_URL="file://$CUR_REPO_ROOT" - fi + if [ "$DO_PUSH" -eq 1 ]; then FETCH_URL="$PUSH_REMOTE_URL"; else FETCH_URL="file://$CUR_REPO_ROOT"; fi fi - # ---- Rewrite athenix file ---- + # --- rewrite users.nix or inventory.nix --- python3 - "$MODE" "$FILE" "$FETCH_URL" "$CUR_REV" "$USERNAME" "$DEVTYPE" "$HOSTKEY" <<'PY' import sys, re, pathlib @@ -340,77 +293,171 @@ username = sys.argv[5] devtype = sys.argv[6] hostkey = sys.argv[7] - text = path.read_text() - def mk_fetch_block(url, rev): - return ( - 'builtins.fetchGit {\n' - f' url = "{url}";\n' - f' rev = "{rev}";\n' - ' submodules=true;\n' - ' }' - ) + def find_matching_brace(s: str, start: int) -> int: + depth = 0 + i = start + in_str = False + while i < len(s): + ch = s[i] + if in_str: + if ch == '\\': + i += 2 + continue + if ch == '"': + in_str = False + i += 1 + continue + if ch == '"': + in_str = True + i += 1 + continue + if ch == '{': + depth += 1 + elif ch == '}': + depth -= 1 + if depth == 0: + return i + i += 1 + raise ValueError("Could not find matching '}'") - fetch_block = mk_fetch_block(fetch_url, rev) + def mk_fetch(entry_indent: str) -> str: + # entry_indent is indentation for the whole `"key" = ;` line. + # The attrset contents should be indented one level deeper. + inner = entry_indent + " " + return ( + 'builtins.fetchGit {\n' + f'{inner}url = "{fetch_url}";\n' + f'{inner}rev = "{rev}";\n' + f'{inner}submodules = true;\n' + f'{entry_indent}}}' + ) - def full_hostname(devtype, hostkey): + def full_hostname(devtype: str, hostkey: str) -> str: if hostkey.startswith(devtype) or "-" in hostkey: return hostkey if hostkey.isdigit(): return f"{devtype}{hostkey}" return f"{devtype}-{hostkey}" - if mode == "user": - m = re.search(r'(?s)(athenix\.users\s*=\s*\{)(.*?)(\n\s*\};)', text) - if not m: + def update_user(t: str) -> str: + mblock = re.search(r"(?s)athenix\.users\s*=\s*\{(.*?)\n\s*\};", t) + if not mblock: raise SystemExit("error: could not locate `athenix.users = { ... };` block") - head, body, tail = m.group(1), m.group(2), m.group(3) + # locate the full span of the users block to edit inside it + # (re-find with groups for reconstruction) + m2 = re.search(r"(?s)(athenix\.users\s*=\s*\{)(.*?)(\n\s*\};)", t) + head, body, tail = m2.group(1), m2.group(2), m2.group(3) - key_re = re.compile(r'(?s)(\n\s*' + re.escape(username) + r'\.external\s*=\s*)builtins\.fetchGit\s*\{.*?\};') - if key_re.search(body): - body = key_re.sub(lambda mm: mm.group(1) + fetch_block + ';', body) + entry_re = re.search( + r"(?s)(\n[ \t]*" + re.escape(username) + r"\.external\s*=\s*)builtins\.fetchGit\s*\{", + body + ) + if entry_re: + brace = body.rfind("{", 0, entry_re.end()) + end = find_matching_brace(body, brace) + semi = re.match(r"\s*;", body[end+1:]) + if not semi: + raise SystemExit("error: expected ';' after fetchGit attrset") + semi_end = end + 1 + semi.end() + + line_start = body.rfind("\n", 0, entry_re.start()) + 1 + indent = re.match(r"[ \t]*", body[line_start:entry_re.start()]).group(0) + + new_body = body[:entry_re.start()] + entry_re.group(1) + mk_fetch(indent) + ";" + body[semi_end:] else: - body = body + f'\n {username}.external = {fetch_block};\n' + indent = " " + new_body = body + f"\n{indent}{username}.external = {mk_fetch(indent)};\n" - new_text = text[:m.start()] + head + body + tail + text[m.end():] - path.write_text(new_text) - sys.exit(0) + return t[:m2.start()] + head + new_body + tail + t[m2.end():] - elif mode == "system": - block_re = re.compile(r'(?s)(\n\s*' + re.escape(devtype) + r'\s*=\s*\{)(.*?)(\n\s*\};)') - m = block_re.search(text) + def update_system(t: str) -> str: + # Find devtype block robustly: start-of-file or newline. + m = re.search(r"(?s)(^|\n)[ \t]*" + re.escape(devtype) + r"\s*=\s*\{", t) if not m: - raise SystemExit(f"error: could not locate `{devtype} = {{ ... }};` block in inventory.nix") + raise SystemExit(f"error: could not locate `{devtype} = {{ ... }};` block") - head, body, tail = m.group(1), m.group(2), m.group(3) + dev_open = t.find("{", m.end() - 1) + dev_close = find_matching_brace(t, dev_open) + dev = t[dev_open:dev_close+1] + + # Find devices attrset inside dev + dm = re.search(r"(?s)(^|\n)[ \t]*devices\s*=\s*\{", dev) + if not dm: + raise SystemExit(f"error: could not locate `devices = {{ ... }};` inside `{devtype}`") + + devices_open = dev.find("{", dm.end() - 1) + devices_close = find_matching_brace(dev, devices_open) + devices = dev[devices_open:devices_close+1] + + # indentation for entries in devices + # find indent of the 'devices' line, then add 2 spaces candidates = [hostkey, full_hostname(devtype, hostkey)] - candidates = list(dict.fromkeys(candidates)) + seen = set() + candidates = [c for c in candidates if not (c in seen or seen.add(c))] - replaced = False - for k in candidates: - entry_re = re.compile(r'(?s)(\n\s*"' + re.escape(k) + r'"\s*=\s*)builtins\.fetchGit\s*\{.*?\};') - if entry_re.search(body): - body = entry_re.sub(lambda mm: mm.group(1) + fetch_block + ';', body) - replaced = True - break + for key in candidates: + entry = re.search( + r'(?s)\n([ ]*)"' + re.escape(key) + r'"\s*=\s*builtins\.fetchGit\s*\{', + devices + ) + if entry: + entry_indent = entry.group(1) - if not replaced: - body = body + f'\n "{hostkey}" = {fetch_block};\n' + # find the '{' we matched + brace = devices.find("{", entry.end() - 1) + end = find_matching_brace(devices, brace) - new_text = text[:m.start()] + head + body + tail + text[m.end():] - path.write_text(new_text) - sys.exit(0) + semi = re.match(r"\s*;", devices[end+1:]) + if not semi: + raise SystemExit("error: expected ';' after fetchGit attrset in devices") + semi_end = end + 1 + semi.end() + # Reconstruct the prefix: newline + indent + "key" = + prefix = f'\n{entry_indent}"{key}" = ' + + new_devices = ( + devices[:entry.start()] + + prefix + + mk_fetch(entry_indent) + + ";" + + devices[semi_end:] + ) + new_dev = dev[:devices_open] + new_devices + dev[devices_close+1:] + + return t[:dev_open] + new_dev + t[dev_close+1:] + + # Not found: append into devices (exact hostkey) + # Indent for new entries: take indent of the closing '}' of devices, add 2 spaces. + close_line_start = devices.rfind("\n", 0, len(devices)-1) + 1 + close_indent = re.match(r"[ ]*", devices[close_line_start:]).group(0) + entry_indent = close_indent + " " + + insertion = f'\n{entry_indent}"{hostkey}" = {mk_fetch(entry_indent)};\n' + new_devices = devices[:-1].rstrip() + insertion + close_indent + "}" + new_dev = dev[:devices_open] + new_devices + dev[devices_close+1:] + return t[:dev_open] + new_dev + t[dev_close+1:] + + if mode == "user": + out = update_user(text) + elif mode == "system": + out = update_system(text) else: raise SystemExit("error: unknown mode") + + path.write_text(out) PY - printf "updated %s (athenix branch: %s)\n" "$FILE" "$ATHENIX_BRANCH" >&2 + cd $ATHENIX_DIR + nix fmt **/*.nix + cd $CUR_REPO_ROOT + + printf "updated %s\n" "$FILE" >&2 printf " url = %s\n" "$FETCH_URL" >&2 printf " rev = %s\n" "$CUR_REV" >&2 '') ]; -} \ No newline at end of file +} -- 2.39.5 From 69b16a2497deecfb718e886ccaa2864b45ff371f Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 15:52:39 -0500 Subject: [PATCH 28/39] improve reference updater tool --- sw/update-ref.nix | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/sw/update-ref.nix b/sw/update-ref.nix index 7c2a81d..19f5009 100644 --- a/sw/update-ref.nix +++ b/sw/update-ref.nix @@ -109,14 +109,38 @@ ;; -p|--push) - PUSH_SET=1; DO_PUSH=1 - # optional next token remote or remote=URL (only if it doesn't look like another flag/subcommand) - if [ "$#" -ge 2 ] && printf "%s" "$2" | grep -qvE '^(user=|system=|--|-l$|-r$)$'; then - PUSH_SPEC="$2"; shift 2 + PUSH_SET=1 + DO_PUSH=1 + PUSH_SPEC="" + + # If there is a next token, only consume it if it is a remote spec + # and not another flag or the subcommand. + if [ "$#" -ge 2 ]; then + nxt="$2" + + if printf "%s" "$nxt" | grep -qE '^(user=|system=)'; then + # next token is the subcommand; don't consume it + shift + elif printf "%s" "$nxt" | grep -qE '^-'; then + # next token is another flag; don't consume it + shift + elif printf "%s" "$nxt" | grep -qE '^[A-Za-z0-9._-]+$'; then + # remote name + PUSH_SPEC="$nxt" + shift 2 + elif printf "%s" "$nxt" | grep -qE '^[A-Za-z0-9._-]+=.+$'; then + # remote=URL + PUSH_SPEC="$nxt" + shift 2 + else + # unknown token; treat as not-a-push-spec and don't consume it + shift + fi else shift fi ;; + -p=*|--push=*) PUSH_SET=1 val="''${1#*=}" -- 2.39.5 From deb3deb66029e1b55b9c06d744de554e5a45bb64 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 16:08:56 -0500 Subject: [PATCH 29/39] usda dashboard external url in allowed hosts --- inventory.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inventory.nix b/inventory.nix index 8969d9a..1d01d86 100644 --- a/inventory.nix +++ b/inventory.nix @@ -121,8 +121,8 @@ }; }; "usda-dash" = builtins.fetchGit { - url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; - rev = "a52962b08d21a4c0b1f4f07dcb55146f2078a5d8"; + url = "git@factory.uga.edu:MODEL/usda-dash-config.git"; + rev = "fb65618abb70908f67df1ea8b9f9ed0d63e2a57b"; submodules = true; }; }; -- 2.39.5 From 4da218dafd9b90f257c3259834e0376cd5f8dff0 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 17:30:06 -0500 Subject: [PATCH 30/39] updated dark color scheme to show text on login for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 1d01d86..590109e 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "git@factory.uga.edu:MODEL/usda-dash-config.git"; - rev = "fb65618abb70908f67df1ea8b9f9ed0d63e2a57b"; + rev = "c22c60d940d885365a1e5bd5887854c6839fb6c6"; submodules = true; }; }; -- 2.39.5 From dcac78cc5aac7d02e0515b69438f3bb08aee171c Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 17:41:16 -0500 Subject: [PATCH 31/39] respect font import better with safari for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 590109e..7594b21 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "git@factory.uga.edu:MODEL/usda-dash-config.git"; - rev = "c22c60d940d885365a1e5bd5887854c6839fb6c6"; + rev = "100fdf3ba1077da42f4b1942b066c7ce1bccdecb"; submodules = true; }; }; -- 2.39.5 From 37a17b0ae6cf1f7538f4e177a2aaee3f33ab5ea3 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 17:53:19 -0500 Subject: [PATCH 32/39] apply text color to body for usda-vision --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 7594b21..34700a0 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "git@factory.uga.edu:MODEL/usda-dash-config.git"; - rev = "100fdf3ba1077da42f4b1942b066c7ce1bccdecb"; + rev = "11a602b954852fb256384ec83ba5204e448177d3"; submodules = true; }; }; -- 2.39.5 From b27143cec4dabba9dd523cb89ef44e1529989b68 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 18:08:37 -0500 Subject: [PATCH 33/39] actually fix text color for usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 34700a0..74a581d 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "git@factory.uga.edu:MODEL/usda-dash-config.git"; - rev = "11a602b954852fb256384ec83ba5204e448177d3"; + rev = "5d1878f4a84316514953103c8ffc87f3ef4351a8"; submodules = true; }; }; -- 2.39.5 From 733d36f943834e4481334341ce652c5b4f352fc4 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 18:12:03 -0500 Subject: [PATCH 34/39] fix for password too on usda-dash --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 74a581d..e00dc12 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "git@factory.uga.edu:MODEL/usda-dash-config.git"; - rev = "5d1878f4a84316514953103c8ffc87f3ef4351a8"; + rev = "e96ea849b8ac8d3dcf665315d289b33dfb9eeb15"; submodules = true; }; }; -- 2.39.5 From 53d1104bda02524d754470143f472f3fc5427f83 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 18:15:41 -0500 Subject: [PATCH 35/39] round bottom border on password --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index e00dc12..94e289c 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "git@factory.uga.edu:MODEL/usda-dash-config.git"; - rev = "e96ea849b8ac8d3dcf665315d289b33dfb9eeb15"; + rev = "36ed172dbd5d50d8e3cebf87841a9a66b5ca30f1"; submodules = true; }; }; -- 2.39.5 From 6e88e1ba699f82f23ae20f5e74b21942fab26ad2 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 18:23:19 -0500 Subject: [PATCH 36/39] round bottom border on password --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 94e289c..547dfc6 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "git@factory.uga.edu:MODEL/usda-dash-config.git"; - rev = "36ed172dbd5d50d8e3cebf87841a9a66b5ca30f1"; + rev = "9383365b0c916b31045373c2a0fb8896432a8df6"; submodules = true; }; }; -- 2.39.5 From 4632ae393cfac175d82823b5c4d8a9e79c6fda2a Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 18:36:22 -0500 Subject: [PATCH 37/39] update --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index 547dfc6..bd76bca 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "git@factory.uga.edu:MODEL/usda-dash-config.git"; - rev = "9383365b0c916b31045373c2a0fb8896432a8df6"; + rev = "73d007c58e9390d1c360e5f3a6d7ef4c00563f84"; submodules = true; }; }; -- 2.39.5 From 0e811ca4794597ea6bb4366ec1d0c403a3943a52 Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 18:47:53 -0500 Subject: [PATCH 38/39] use checksum for usda-dash src copy --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index bd76bca..feb0961 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "git@factory.uga.edu:MODEL/usda-dash-config.git"; - rev = "73d007c58e9390d1c360e5f3a6d7ef4c00563f84"; + rev = "21de5c571e9ab0421d801170e0e1ca5f2c9b792f"; submodules = true; }; }; -- 2.39.5 From ac82e868cf95983d17142bf92c4dd69551b4f96d Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Fri, 19 Dec 2025 18:49:16 -0500 Subject: [PATCH 39/39] use checksum for usda-dash src copy --- inventory.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory.nix b/inventory.nix index feb0961..ca8110c 100644 --- a/inventory.nix +++ b/inventory.nix @@ -122,7 +122,7 @@ }; "usda-dash" = builtins.fetchGit { url = "git@factory.uga.edu:MODEL/usda-dash-config.git"; - rev = "21de5c571e9ab0421d801170e0e1ca5f2c9b792f"; + rev = "49cded91cff4a956d4e01ac6b8fe4efa86f82182"; submodules = true; }; }; -- 2.39.5