Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions package/contents/tools/sh/generations
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#!/usr/bin/env bash
# Emit: active-store-path, booted-store-path, then one stat line per system-N-link,
# Emit: active-store-path, booted-store-path, then one line per system-N-link,
# then "current <store-path>" when /run/current-system resolves.
# Link lines are: <stat %y> '<link>' -> '<target>'. The target comes from readlink rather than
# stat's %N, whose quoting varies with QUOTING_STYLE and the installed coreutils (#44).
readlink -f /nix/var/nix/profiles/system 2>/dev/null
readlink -f /run/booted-system 2>/dev/null
find /nix/var/nix/profiles -maxdepth 1 -name 'system-*-link' -type l \
-exec stat --format='%y %N' {} + 2>/dev/null
for link in /nix/var/nix/profiles/system-*-link; do
[ -L "$link" ] || continue
printf "%s '%s' -> '%s'\n" "$(stat --format=%y "$link" 2>/dev/null)" "$link" "$(readlink "$link")"
done
current=$(readlink -e /run/current-system 2>/dev/null) && echo "current $current"
exit 0
5 changes: 3 additions & 2 deletions package/contents/ui/Engine.qml
Original file line number Diff line number Diff line change
Expand Up @@ -2570,7 +2570,7 @@ Item {
currentPath = lines[i].slice(8).trim();
continue;
}
const m = lines[i].match(/^([\d-]+ [\d:]+)\.\d+ [+-]\d+ '([^']*system-(\d+)-link)' -> '([^']+)'/);
const m = lines[i].match(/^([\d-]+ [\d:]+)(?:\.\d+)?(?: [+-]\d+)? '([^']*system-(\d+)-link)' -> '([^']+)'/);
if (!m)
continue;
const genNum = parseInt(m[3]);
Expand All @@ -2592,7 +2592,8 @@ Item {

list.sort((a, b) => b.number - a.number);
// `nixos-rebuild test` / `nh os test` activate a closure without registering a generation.
root.runningTestBuild = currentPath !== "" && !list.some(g => g.storePath === currentPath);
// An empty list means parsing failed, not a test build.
root.runningTestBuild = currentPath !== "" && list.length > 0 && !list.some(g => g.storePath === currentPath);
// Keep the preceding closure even when the displayed history is limited.
for (let i = 0; i + 1 < list.length; ++i) {
list[i].previousNumber = list[i + 1].number;
Expand Down
9 changes: 9 additions & 0 deletions tests/tst_Engine.qml
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,15 @@ Item {
compare(core.bootedGenNum, -1);
verify(!core.runningTestBuild, "An unresolvable current-system is not a test build");
}
function test_parse_generations_without_link_lines_is_not_a_test_build() {
core.parseGenerations(["/nix/store/162-sys", "/nix/store/162-sys", "current /nix/store/162-sys"].join("\n"));
compare(core.generations.length, 0);
verify(!core.runningTestBuild, "no parsed generations means a parse failure, not a test build");
}
function test_parse_generations_accepts_stat_without_fraction_or_zone() {
core.parseGenerations(["/nix/store/7-sys", "/nix/store/7-sys", "2026-10-11 12:39:05 '/nix/var/nix/profiles/system-7-link' -> '/nix/store/7-sys'"].join("\n"));
compare(core.activeGenNum, 7);
}
function test_terminal_completion_tracks_exit_not_generations() {
core.runCustomCommand("false", "Test");
verify(core.isBusy);
Expand Down
Loading