From 5e5c3e2534df29f5aa3279af542effe744f493d6 Mon Sep 17 00:00:00 2001 From: alexion Date: Sat, 26 Sep 2026 23:20:00 -0400 Subject: [PATCH 1/5] feat(natu): report expected let order --- src/natu/ast.rs | 1 + src/natu/natu.rs | 40 +++++++++++++++++++++++++++++++++++++ src/natu/natu_test.rs | 13 +++++++++++- src/natu/parser.rs | 1 + src/natu/rules.rs | 6 +++++- src/natu/rules/let_order.rs | 40 ++++++++++++++++++------------------- src/natu/scope_graph.rs | 11 +++++++--- 7 files changed, 87 insertions(+), 25 deletions(-) diff --git a/src/natu/ast.rs b/src/natu/ast.rs index c24b52c..61038d9 100644 --- a/src/natu/ast.rs +++ b/src/natu/ast.rs @@ -49,6 +49,7 @@ pub enum Expr { Ident(Ident), Select(Select), Let { + range: SourceRange, bindings: Vec, body: Box, }, diff --git a/src/natu/natu.rs b/src/natu/natu.rs index d942ea5..94ea9b2 100644 --- a/src/natu/natu.rs +++ b/src/natu/natu.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + mod ast; mod file_store; mod parser; @@ -23,7 +25,45 @@ impl Report { impl std::fmt::Display for Report { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut reported_let_order_files = HashSet::new(); + for diagnostic in &self.diagnostics { + if diagnostic.finding.rule_id == "let_order" { + if !reported_let_order_files.insert(diagnostic.file_id) { + continue; + } + + let file = self.files.get(diagnostic.file_id); + writeln!( + formatter, + "{} [let_order] Let bindings must follow dependency order", + file.path.display() + )?; + + let mut findings = self + .diagnostics + .iter() + .filter(|candidate| { + candidate.file_id == diagnostic.file_id + && candidate.finding.rule_id == "let_order" + }) + .collect::>(); + findings.sort_by_key(|finding| finding.finding.range.start); + + for finding in findings { + let (start, _) = line_range(&file.source, finding.finding.range); + let expected_order = + finding.finding.expected_order.as_ref().expect("let order"); + writeln!( + formatter, + " let at line {start}: {}", + expected_order.join(", ") + )?; + } + + continue; + } + let file = self.files.get(diagnostic.file_id); let (start, end) = line_range(&file.source, diagnostic.finding.range); let location = if start == end { diff --git a/src/natu/natu_test.rs b/src/natu/natu_test.rs index 45087bd..bc7c81e 100644 --- a/src/natu/natu_test.rs +++ b/src/natu/natu_test.rs @@ -50,7 +50,18 @@ fn natu_reports_out_of_order_let_bindings() -> googletest::Result<()> { .iter() .filter(|diagnostic| diagnostic.finding.rule_id == "let_order") .count(); - expect_that!(let_order_count, eq(4)); + expect_that!(let_order_count, eq(2)); + expect_that!( + report.to_string(), + eq(concat!( + "testdata/lint_findings.nix:11 [manual_inherit] Use `inherit` for this assignment\n", + "testdata/lint_findings.nix:12 [manual_inherit_from] Use `inherit` for this selected attribute\n", + "testdata/lint_findings.nix:13 [unquoted_uri] URI literals should be quoted\n", + "testdata/lint_findings.nix [let_order] Let bindings must follow dependency order\n", + " let at line 1: first, second, nested\n", + " let at line 4: nestedSecond, nestedFirst\n", + )) + ); Ok(()) } diff --git a/src/natu/parser.rs b/src/natu/parser.rs index 419188c..9ece9db 100644 --- a/src/natu/parser.rs +++ b/src/natu/parser.rs @@ -43,6 +43,7 @@ fn convert_expr(expr: rnix::ast::Expr) -> ast::Expr { match expr { rnix::ast::Expr::Ident(ident) => ast::Expr::Ident(ident.into()), rnix::ast::Expr::LetIn(let_in) => ast::Expr::Let { + range: let_in.syntax().text_range().into(), bindings: let_in .attrpath_values() .filter_map(convert_binding_ast) diff --git a/src/natu/rules.rs b/src/natu/rules.rs index 558e302..7d0e6b8 100644 --- a/src/natu/rules.rs +++ b/src/natu/rules.rs @@ -22,6 +22,7 @@ pub struct Finding { pub range: ast::SourceRange, pub message: &'static str, pub binding: Option, + pub expected_order: Option>, } impl std::fmt::Display for Diagnostic { @@ -79,7 +80,8 @@ pub fn run_rules(file: &ast::ParsedFile, graph: &ScopeGraph) -> Vec rule_id: "let_order", range: finding.range, message: "Let bindings must follow dependency order", - binding: Some(finding.binding), + binding: None, + expected_order: Some(finding.expected_order), }); node_diagnostics @@ -104,6 +106,7 @@ where range: node.range, message, binding: None, + expected_order: None, }) } @@ -119,5 +122,6 @@ where range: token.range, message, binding: None, + expected_order: None, }) } diff --git a/src/natu/rules/let_order.rs b/src/natu/rules/let_order.rs index 7d01ebf..92dc6d6 100644 --- a/src/natu/rules/let_order.rs +++ b/src/natu/rules/let_order.rs @@ -5,7 +5,7 @@ use crate::{ pub struct Finding { pub range: ast::SourceRange, - pub binding: String, + pub expected_order: Vec, } pub fn check(graph: &ScopeGraph) -> Vec { @@ -14,26 +14,26 @@ pub fn check(graph: &ScopeGraph) -> Vec { .iter() .flat_map(|let_scope| { let expected = expected_order(graph, let_scope); - let positions = expected - .iter() - .enumerate() - .map(|(position, declaration)| (*declaration, position)) - .collect::>(); + let is_ordered = expected.iter().enumerate().all(|(position, declaration)| { + let_scope + .bindings + .get(position) + .is_some_and(|binding| binding.declaration == *declaration) + }); - let_scope - .bindings - .iter() - .enumerate() - .filter_map(move |(position, binding)| { - positions - .iter() - .find(|(declaration, _)| *declaration == binding.declaration) - .filter(|(_, expected_position)| position != *expected_position) - .map(|_| Finding { - range: binding.range, - binding: binding.name.clone(), - }) - }) + (!is_ordered).then(|| Finding { + range: let_scope.range, + expected_order: expected + .iter() + .filter_map(|declaration| { + let_scope + .bindings + .iter() + .find(|binding| binding.declaration == *declaration) + .map(|binding| binding.name.clone()) + }) + .collect(), + }) }) .collect() } diff --git a/src/natu/scope_graph.rs b/src/natu/scope_graph.rs index 6795458..46dff26 100644 --- a/src/natu/scope_graph.rs +++ b/src/natu/scope_graph.rs @@ -26,7 +26,11 @@ impl ScopeGraph { fn build_expr(&mut self, expr: &ast::Expr, scope: NodeId) -> Vec { match expr { ast::Expr::Ident(ident) => vec![self.add_reference(&ident.name, scope)], - ast::Expr::Let { bindings, body } => self.build_let(bindings, body, scope), + ast::Expr::Let { + range, + bindings, + body, + } => self.build_let(*range, bindings, body, scope), ast::Expr::AttrSet(bindings) => bindings .iter() .flat_map(|binding| self.build_expr(&binding.value, scope)) @@ -48,6 +52,7 @@ impl ScopeGraph { fn build_let( &mut self, + range: ast::SourceRange, bindings: &[ast::Binding], body: &ast::Expr, parent: NodeId, @@ -59,7 +64,6 @@ impl ScopeGraph { declaration: self.add_declaration(&binding.key.name, scope), name: binding.key.name.clone(), references: Vec::new(), - range: binding.range, }) .collect::>(); @@ -74,6 +78,7 @@ impl ScopeGraph { .chain(entry_references.iter().copied()) .collect(); self.lets.push(LetScope { + range, bindings: graph_bindings, entry_references, }); @@ -181,6 +186,7 @@ struct Edge { #[derive(Debug)] pub(crate) struct LetScope { + pub(crate) range: ast::SourceRange, pub(crate) bindings: Vec, pub(crate) entry_references: Vec, } @@ -190,5 +196,4 @@ pub(crate) struct Binding { pub(crate) declaration: NodeId, pub(crate) name: String, pub(crate) references: Vec, - pub(crate) range: ast::SourceRange, } From ac5cc8d65916a0ac87a59876cb30103544995014 Mon Sep 17 00:00:00 2001 From: alexion Date: Sat, 26 Sep 2026 23:24:54 -0400 Subject: [PATCH 2/5] fix(natu): preserve NixOS cfg bindings --- src/natu/natu.rs | 2 +- src/natu/natu_test.rs | 12 ++++++++++++ src/natu/rules.rs | 22 +++++++++++++-------- src/natu/rules/let_order.rs | 38 +++++++++++++++++++++---------------- 4 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/natu/natu.rs b/src/natu/natu.rs index 94ea9b2..a4df7ba 100644 --- a/src/natu/natu.rs +++ b/src/natu/natu.rs @@ -111,7 +111,7 @@ pub fn future_sight(files: Vec) -> status::Result { } let graph = ScopeGraph::from_file(&parsed); - diagnostics.extend(rules::run_rules(&parsed, &graph)); + diagnostics.extend(rules::run_rules(&parsed, &graph, &file.path)); } Ok(Report { diff --git a/src/natu/natu_test.rs b/src/natu/natu_test.rs index bc7c81e..3f2f47f 100644 --- a/src/natu/natu_test.rs +++ b/src/natu/natu_test.rs @@ -65,6 +65,18 @@ fn natu_reports_out_of_order_let_bindings() -> googletest::Result<()> { Ok(()) } +#[gtest] +fn natu_preserves_nixos_cfg_bindings() -> googletest::Result<()> { + let report = natu::future_sight(vec![SourceFile { + path: PathBuf::from("configs/nixos/test.nix"), + source: "{ config, ... }: let\n cfg = config.example;\n value = cfg;\nin value\n".into(), + }]) + .or_fail()?; + + expect_that!(report.is_clean(), is_true()); + Ok(()) +} + #[gtest] fn natu_reports_unquoted_uri() -> googletest::Result<()> { let report = natu::future_sight(vec![fixture_source("testdata/lint_findings.nix")?]).or_fail()?; diff --git a/src/natu/rules.rs b/src/natu/rules.rs index 7d0e6b8..cf8183d 100644 --- a/src/natu/rules.rs +++ b/src/natu/rules.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use crate::FileId; use crate::ast; use crate::rules::manual_inherit::ManualInherit; @@ -59,7 +61,7 @@ pub trait TokenRule { fn check(token: &Self::Token) -> Option<&'static str>; } -pub fn run_rules(file: &ast::ParsedFile, graph: &ScopeGraph) -> Vec { +pub fn run_rules(file: &ast::ParsedFile, graph: &ScopeGraph, path: &Path) -> Vec { let node_diagnostics = file .nodes .iter() @@ -76,13 +78,17 @@ pub fn run_rules(file: &ast::ParsedFile, graph: &ScopeGraph) -> Vec .flat_map(|token| [run_token_rule::(token)]) .flatten(); - let let_order_diagnostics = let_order::check(graph).into_iter().map(|finding| Finding { - rule_id: "let_order", - range: finding.range, - message: "Let bindings must follow dependency order", - binding: None, - expected_order: Some(finding.expected_order), - }); + let ignore_cfg = + path.starts_with("configs/nixos") || path.to_string_lossy().contains("/configs/nixos/"); + let let_order_diagnostics = let_order::check(graph, ignore_cfg) + .into_iter() + .map(|finding| Finding { + rule_id: "let_order", + range: finding.range, + message: "Let bindings must follow dependency order", + binding: None, + expected_order: Some(finding.expected_order), + }); node_diagnostics .chain(token_diagnostics) diff --git a/src/natu/rules/let_order.rs b/src/natu/rules/let_order.rs index 92dc6d6..ae8e77d 100644 --- a/src/natu/rules/let_order.rs +++ b/src/natu/rules/let_order.rs @@ -8,36 +8,42 @@ pub struct Finding { pub expected_order: Vec, } -pub fn check(graph: &ScopeGraph) -> Vec { +pub fn check(graph: &ScopeGraph, ignore_cfg: bool) -> Vec { graph .let_scopes() .iter() .flat_map(|let_scope| { - let expected = expected_order(graph, let_scope); - let is_ordered = expected.iter().enumerate().all(|(position, declaration)| { - let_scope - .bindings - .get(position) - .is_some_and(|binding| binding.declaration == *declaration) - }); + let expected = expected_order(graph, let_scope) + .into_iter() + .filter(|declaration| !ignore_cfg || binding_name(let_scope, *declaration) != "cfg") + .collect::>(); + let actual = let_scope + .bindings + .iter() + .filter(|binding| !ignore_cfg || binding.name != "cfg") + .map(|binding| binding.declaration) + .collect::>(); - (!is_ordered).then(|| Finding { + (!actual.starts_with(&expected)).then(|| Finding { range: let_scope.range, expected_order: expected .iter() - .filter_map(|declaration| { - let_scope - .bindings - .iter() - .find(|binding| binding.declaration == *declaration) - .map(|binding| binding.name.clone()) - }) + .map(|declaration| binding_name(let_scope, *declaration).to_owned()) .collect(), }) }) .collect() } +fn binding_name(let_scope: &LetScope, declaration: NodeId) -> &str { + &let_scope + .bindings + .iter() + .find(|binding| binding.declaration == declaration) + .expect("let declaration") + .name +} + fn expected_order(graph: &ScopeGraph, let_scope: &LetScope) -> Vec { let mut order = Vec::new(); visit_references(graph, &let_scope.entry_references, let_scope, &mut order); From dd82367dcbf1f8df447085f5d1c8db24c8600fda Mon Sep 17 00:00:00 2001 From: alexion Date: Sat, 26 Sep 2026 23:30:01 -0400 Subject: [PATCH 3/5] feat(natu): support inline suppressions --- src/natu/natu.rs | 2 +- src/natu/natu_test.rs | 6 ++--- src/natu/rules.rs | 44 +++++++++++++++++++++++++------------ src/natu/rules/let_order.rs | 16 +++++++++++--- src/natu/scope_graph.rs | 2 ++ 5 files changed, 49 insertions(+), 21 deletions(-) diff --git a/src/natu/natu.rs b/src/natu/natu.rs index a4df7ba..6197a54 100644 --- a/src/natu/natu.rs +++ b/src/natu/natu.rs @@ -111,7 +111,7 @@ pub fn future_sight(files: Vec) -> status::Result { } let graph = ScopeGraph::from_file(&parsed); - diagnostics.extend(rules::run_rules(&parsed, &graph, &file.path)); + diagnostics.extend(rules::run_rules(&parsed, &graph, &file.source)); } Ok(Report { diff --git a/src/natu/natu_test.rs b/src/natu/natu_test.rs index 3f2f47f..7584791 100644 --- a/src/natu/natu_test.rs +++ b/src/natu/natu_test.rs @@ -66,10 +66,10 @@ fn natu_reports_out_of_order_let_bindings() -> googletest::Result<()> { } #[gtest] -fn natu_preserves_nixos_cfg_bindings() -> googletest::Result<()> { +fn natu_preserves_suppressed_bindings() -> googletest::Result<()> { let report = natu::future_sight(vec![SourceFile { - path: PathBuf::from("configs/nixos/test.nix"), - source: "{ config, ... }: let\n cfg = config.example;\n value = cfg;\nin value\n".into(), + path: PathBuf::from("testdata/nolint.nix"), + source: "{ config, ... }: let\n cfg = config.example; # NOLINT(let_order)\n value = cfg;\nin value\n".into(), }]) .or_fail()?; diff --git a/src/natu/rules.rs b/src/natu/rules.rs index cf8183d..b295ecd 100644 --- a/src/natu/rules.rs +++ b/src/natu/rules.rs @@ -1,5 +1,3 @@ -use std::path::Path; - use crate::FileId; use crate::ast; use crate::rules::manual_inherit::ManualInherit; @@ -61,7 +59,7 @@ pub trait TokenRule { fn check(token: &Self::Token) -> Option<&'static str>; } -pub fn run_rules(file: &ast::ParsedFile, graph: &ScopeGraph, path: &Path) -> Vec { +pub fn run_rules(file: &ast::ParsedFile, graph: &ScopeGraph, source: &str) -> Vec { let node_diagnostics = file .nodes .iter() @@ -78,21 +76,21 @@ pub fn run_rules(file: &ast::ParsedFile, graph: &ScopeGraph, path: &Path) -> Vec .flat_map(|token| [run_token_rule::(token)]) .flatten(); - let ignore_cfg = - path.starts_with("configs/nixos") || path.to_string_lossy().contains("/configs/nixos/"); - let let_order_diagnostics = let_order::check(graph, ignore_cfg) - .into_iter() - .map(|finding| Finding { - rule_id: "let_order", - range: finding.range, - message: "Let bindings must follow dependency order", - binding: None, - expected_order: Some(finding.expected_order), - }); + let let_order_diagnostics = + let_order::check(graph, source) + .into_iter() + .map(|finding| Finding { + rule_id: "let_order", + range: finding.range, + message: "Let bindings must follow dependency order", + binding: None, + expected_order: Some(finding.expected_order), + }); node_diagnostics .chain(token_diagnostics) .chain(let_order_diagnostics) + .filter(|finding| !is_suppressed(source, finding.range, finding.rule_id)) .map(|finding| Diagnostic { file_id: file.file_id, finding, @@ -100,6 +98,24 @@ pub fn run_rules(file: &ast::ParsedFile, graph: &ScopeGraph, path: &Path) -> Vec .collect() } +pub fn is_suppressed(source: &str, range: ast::SourceRange, rule_id: &str) -> bool { + let line = source[range.start..].lines().next().unwrap_or_default(); + let Some(comment) = line.split_once('#').map(|(_, comment)| comment.trim()) else { + return false; + }; + if comment == "NOLINT" { + return true; + } + comment + .strip_prefix("NOLINT(") + .and_then(|rules| rules.strip_suffix(')')) + .is_some_and(|rules| { + rules + .split(',') + .any(|candidate| candidate.trim() == rule_id) + }) +} + fn run_node_rule(node: &ast::Node) -> Option where R: NodeRule, diff --git a/src/natu/rules/let_order.rs b/src/natu/rules/let_order.rs index ae8e77d..ca7daf0 100644 --- a/src/natu/rules/let_order.rs +++ b/src/natu/rules/let_order.rs @@ -1,5 +1,6 @@ use crate::{ ast, + rules::is_suppressed, scope_graph::{LetScope, NodeId, ScopeGraph}, }; @@ -8,19 +9,19 @@ pub struct Finding { pub expected_order: Vec, } -pub fn check(graph: &ScopeGraph, ignore_cfg: bool) -> Vec { +pub fn check(graph: &ScopeGraph, source: &str) -> Vec { graph .let_scopes() .iter() .flat_map(|let_scope| { let expected = expected_order(graph, let_scope) .into_iter() - .filter(|declaration| !ignore_cfg || binding_name(let_scope, *declaration) != "cfg") + .filter(|declaration| !binding_suppressed(let_scope, *declaration, source)) .collect::>(); let actual = let_scope .bindings .iter() - .filter(|binding| !ignore_cfg || binding.name != "cfg") + .filter(|binding| !is_suppressed(source, binding.range, "let_order")) .map(|binding| binding.declaration) .collect::>(); @@ -35,6 +36,15 @@ pub fn check(graph: &ScopeGraph, ignore_cfg: bool) -> Vec { .collect() } +fn binding_suppressed(let_scope: &LetScope, declaration: NodeId, source: &str) -> bool { + let binding = let_scope + .bindings + .iter() + .find(|binding| binding.declaration == declaration) + .expect("let declaration"); + is_suppressed(source, binding.range, "let_order") +} + fn binding_name(let_scope: &LetScope, declaration: NodeId) -> &str { &let_scope .bindings diff --git a/src/natu/scope_graph.rs b/src/natu/scope_graph.rs index 46dff26..e509f6d 100644 --- a/src/natu/scope_graph.rs +++ b/src/natu/scope_graph.rs @@ -64,6 +64,7 @@ impl ScopeGraph { declaration: self.add_declaration(&binding.key.name, scope), name: binding.key.name.clone(), references: Vec::new(), + range: binding.range, }) .collect::>(); @@ -196,4 +197,5 @@ pub(crate) struct Binding { pub(crate) declaration: NodeId, pub(crate) name: String, pub(crate) references: Vec, + pub(crate) range: ast::SourceRange, } From 324d40c85dd580f4391db71a4406b59f04c13f35 Mon Sep 17 00:00:00 2001 From: alexion Date: Sat, 26 Sep 2026 23:32:34 -0400 Subject: [PATCH 4/5] style(nixos): suppress cfg order findings --- configs/nixos/guests/actualbudget.nix | 2 +- configs/nixos/guests/copyparty.nix | 2 +- configs/nixos/guests/homepage.nix | 2 +- configs/nixos/guests/palworld.nix | 2 +- configs/nixos/guests/valheim.nix | 2 +- configs/nixos/modules/desktop/apps/alacritty.nix | 2 +- configs/nixos/modules/desktop/apps/firefox.nix | 2 +- configs/nixos/modules/desktop/apps/ghostty.nix | 2 +- configs/nixos/modules/desktop/apps/obsidian.nix | 2 +- configs/nixos/modules/desktop/apps/r2modman.nix | 2 +- configs/nixos/modules/desktop/apps/signal.nix | 2 +- configs/nixos/modules/desktop/apps/steam.nix | 2 +- configs/nixos/modules/desktop/apps/vencord.nix | 2 +- configs/nixos/modules/desktop/default.nix | 2 +- configs/nixos/modules/desktop/session/hyprland/default.nix | 2 +- configs/nixos/modules/desktop/session/hyprland/hypridle.nix | 2 +- configs/nixos/modules/desktop/session/hyprland/hyprlock.nix | 2 +- configs/nixos/modules/desktop/session/login.nix | 2 +- configs/nixos/modules/desktop/session/mako.nix | 2 +- configs/nixos/modules/desktop/session/portals.nix | 2 +- configs/nixos/modules/desktop/session/rofi.nix | 2 +- configs/nixos/modules/desktop/session/waybar.nix | 2 +- configs/nixos/modules/desktop/utilities/audio.nix | 2 +- configs/nixos/modules/desktop/utilities/clipboard.nix | 2 +- configs/nixos/modules/desktop/utilities/osd.nix | 2 +- configs/nixos/modules/desktop/utilities/recording.nix | 2 +- configs/nixos/modules/desktop/utilities/screenshot.nix | 2 +- configs/nixos/modules/desktop/utilities/theming.nix | 2 +- configs/nixos/modules/desktop/utilities/userdirs.nix | 2 +- configs/nixos/modules/guest.nix | 2 +- configs/nixos/modules/host.nix | 2 +- configs/nixos/modules/monorepo.nix | 2 +- configs/nixos/modules/network/ssh.nix | 2 +- configs/nixos/modules/network/vlans.nix | 2 +- configs/nixos/modules/network/vpn-client.nix | 2 +- configs/nixos/modules/services/actualbudget.nix | 2 +- configs/nixos/modules/services/copyparty.nix | 2 +- configs/nixos/modules/services/homepage.nix | 2 +- configs/nixos/modules/services/navidrome.nix | 2 +- configs/nixos/modules/services/reverse-proxy.nix | 2 +- configs/nixos/modules/shell/bazel.nix | 2 +- configs/nixos/modules/shell/direnv/default.nix | 2 +- configs/nixos/modules/shell/fish/default.nix | 2 +- configs/nixos/modules/shell/git.nix | 2 +- configs/nixos/modules/shell/gitea-axi.nix | 2 +- configs/nixos/modules/shell/herdr.nix | 2 +- configs/nixos/modules/shell/nvim/default.nix | 2 +- configs/nixos/modules/shell/omp.nix | 2 +- configs/nixos/modules/shell/pi/default.nix | 2 +- configs/nixos/modules/shell/python.nix | 2 +- configs/nixos/modules/shell/rust.nix | 2 +- configs/nixos/modules/shell/tmux/default.nix | 2 +- configs/nixos/modules/shell/yazi.nix | 2 +- configs/nixos/modules/zfs.nix | 2 +- 54 files changed, 54 insertions(+), 54 deletions(-) diff --git a/configs/nixos/guests/actualbudget.nix b/configs/nixos/guests/actualbudget.nix index 10a973a..5bd3157 100644 --- a/configs/nixos/guests/actualbudget.nix +++ b/configs/nixos/guests/actualbudget.nix @@ -4,7 +4,7 @@ ... }: let - cfg = config.guests.actualbudget; + cfg = config.guests.actualbudget; # NOLINT(let_order) dataDir = "/data"; in { diff --git a/configs/nixos/guests/copyparty.nix b/configs/nixos/guests/copyparty.nix index 63c57f3..fd4d9b7 100644 --- a/configs/nixos/guests/copyparty.nix +++ b/configs/nixos/guests/copyparty.nix @@ -4,7 +4,7 @@ ... }: let - cfg = config.guests.copyparty; + cfg = config.guests.copyparty; # NOLINT(let_order) volumeType = lib.types.submodule { options = { diff --git a/configs/nixos/guests/homepage.nix b/configs/nixos/guests/homepage.nix index da3e0b8..7461642 100644 --- a/configs/nixos/guests/homepage.nix +++ b/configs/nixos/guests/homepage.nix @@ -4,7 +4,7 @@ ... }: let - cfg = config.guests.homepage; + cfg = config.guests.homepage; # NOLINT(let_order) environmentFile = "/run/secrets/homepage.env"; in { diff --git a/configs/nixos/guests/palworld.nix b/configs/nixos/guests/palworld.nix index 9ae2208..057a90b 100644 --- a/configs/nixos/guests/palworld.nix +++ b/configs/nixos/guests/palworld.nix @@ -4,7 +4,7 @@ ... }: let - cfg = config.guests.palworld; + cfg = config.guests.palworld; # NOLINT(let_order) steamDir = "/data/steamcmd"; installDir = "/data/server"; stateDir = "/data/state"; diff --git a/configs/nixos/guests/valheim.nix b/configs/nixos/guests/valheim.nix index 6c38233..c1085d1 100644 --- a/configs/nixos/guests/valheim.nix +++ b/configs/nixos/guests/valheim.nix @@ -4,7 +4,7 @@ ... }: let - cfg = config.guests.valheim; + cfg = config.guests.valheim; # NOLINT(let_order) steamDir = "/data/steamcmd"; installDir = "/data/server"; stateDir = "/data/state"; diff --git a/configs/nixos/modules/desktop/apps/alacritty.nix b/configs/nixos/modules/desktop/apps/alacritty.nix index 785a71e..f8ce8d5 100644 --- a/configs/nixos/modules/desktop/apps/alacritty.nix +++ b/configs/nixos/modules/desktop/apps/alacritty.nix @@ -6,7 +6,7 @@ }: # Alacritty as the desktop terminal. let - cfg = config.modules.desktop.apps.alacritty; + cfg = config.modules.desktop.apps.alacritty; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/desktop/apps/firefox.nix b/configs/nixos/modules/desktop/apps/firefox.nix index d47e3bd..4693a03 100644 --- a/configs/nixos/modules/desktop/apps/firefox.nix +++ b/configs/nixos/modules/desktop/apps/firefox.nix @@ -7,7 +7,7 @@ }: # Firefox as the desktop browser: stock mainline, hardened and de-monetized by policy. let - cfg = config.modules.desktop.apps.firefox; + cfg = config.modules.desktop.apps.firefox; # NOLINT(let_order) user = config.user.name; firefoxAddons = inputs.firefox-addons.packages.${pkgs.stdenv.hostPlatform.system}; in diff --git a/configs/nixos/modules/desktop/apps/ghostty.nix b/configs/nixos/modules/desktop/apps/ghostty.nix index c39451e..a3b7853 100644 --- a/configs/nixos/modules/desktop/apps/ghostty.nix +++ b/configs/nixos/modules/desktop/apps/ghostty.nix @@ -6,7 +6,7 @@ }: # Ghostty as the desktop terminal. let - cfg = config.modules.desktop.apps.ghostty; + cfg = config.modules.desktop.apps.ghostty; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/desktop/apps/obsidian.nix b/configs/nixos/modules/desktop/apps/obsidian.nix index 38b0deb..bc1f190 100644 --- a/configs/nixos/modules/desktop/apps/obsidian.nix +++ b/configs/nixos/modules/desktop/apps/obsidian.nix @@ -6,7 +6,7 @@ }: # Obsidian as the desktop note-taking vault. let - cfg = config.modules.desktop.apps.obsidian; + cfg = config.modules.desktop.apps.obsidian; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/desktop/apps/r2modman.nix b/configs/nixos/modules/desktop/apps/r2modman.nix index 6607dfd..326f6b6 100644 --- a/configs/nixos/modules/desktop/apps/r2modman.nix +++ b/configs/nixos/modules/desktop/apps/r2modman.nix @@ -6,7 +6,7 @@ }: # r2modman, a Thunderstore mod manager. let - cfg = config.modules.desktop.apps.r2modman; + cfg = config.modules.desktop.apps.r2modman; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/desktop/apps/signal.nix b/configs/nixos/modules/desktop/apps/signal.nix index 2c534d4..8d92ef4 100644 --- a/configs/nixos/modules/desktop/apps/signal.nix +++ b/configs/nixos/modules/desktop/apps/signal.nix @@ -6,7 +6,7 @@ }: # Signal desktop messenger. let - cfg = config.modules.desktop.apps.signal; + cfg = config.modules.desktop.apps.signal; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/desktop/apps/steam.nix b/configs/nixos/modules/desktop/apps/steam.nix index 0bafc21..f2ca917 100644 --- a/configs/nixos/modules/desktop/apps/steam.nix +++ b/configs/nixos/modules/desktop/apps/steam.nix @@ -6,7 +6,7 @@ }: # Steam game launcher and runtime integration. let - cfg = config.modules.desktop.apps.steam; + cfg = config.modules.desktop.apps.steam; # NOLINT(let_order) in { options.modules.desktop.apps.steam.enable = lib.mkEnableOption "Steam game launcher"; diff --git a/configs/nixos/modules/desktop/apps/vencord.nix b/configs/nixos/modules/desktop/apps/vencord.nix index 53b6ecf..95a7f2c 100644 --- a/configs/nixos/modules/desktop/apps/vencord.nix +++ b/configs/nixos/modules/desktop/apps/vencord.nix @@ -6,7 +6,7 @@ }: # Vesktop, a Discord client with Vencord built in. let - cfg = config.modules.desktop.apps.vencord; + cfg = config.modules.desktop.apps.vencord; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/desktop/default.nix b/configs/nixos/modules/desktop/default.nix index c9bf014..43e53a2 100644 --- a/configs/nixos/modules/desktop/default.nix +++ b/configs/nixos/modules/desktop/default.nix @@ -1,7 +1,7 @@ { config, lib, ... }: # The desktop aggregator: one flag brings up the whole graphical session. let - cfg = config.modules.desktop; + cfg = config.modules.desktop; # NOLINT(let_order) in { options.modules.desktop.enable = lib.mkEnableOption "the keyboard-driven Hyprland desktop"; diff --git a/configs/nixos/modules/desktop/session/hyprland/default.nix b/configs/nixos/modules/desktop/session/hyprland/default.nix index 445f293..cdb462a 100644 --- a/configs/nixos/modules/desktop/session/hyprland/default.nix +++ b/configs/nixos/modules/desktop/session/hyprland/default.nix @@ -6,7 +6,7 @@ }: # The Hyprland compositor. let - cfg = config.modules.desktop.session.hyprland; + cfg = config.modules.desktop.session.hyprland; # NOLINT(let_order) user = config.user.name; cursor = config.stylix.cursor; diff --git a/configs/nixos/modules/desktop/session/hyprland/hypridle.nix b/configs/nixos/modules/desktop/session/hyprland/hypridle.nix index 96411e3..e87b078 100644 --- a/configs/nixos/modules/desktop/session/hyprland/hypridle.nix +++ b/configs/nixos/modules/desktop/session/hyprland/hypridle.nix @@ -6,7 +6,7 @@ }: # hypridle: idle-triggered locking and display power management. let - cfg = config.modules.desktop.session.hyprland.hypridle; + cfg = config.modules.desktop.session.hyprland.hypridle; # NOLINT(let_order) user = config.user.name; hyprctl = "${config.programs.hyprland.package}/bin/hyprctl"; diff --git a/configs/nixos/modules/desktop/session/hyprland/hyprlock.nix b/configs/nixos/modules/desktop/session/hyprland/hyprlock.nix index 50a9782..ce7e8ec 100644 --- a/configs/nixos/modules/desktop/session/hyprland/hyprlock.nix +++ b/configs/nixos/modules/desktop/session/hyprland/hyprlock.nix @@ -6,7 +6,7 @@ }: # The lock screen: a hyprlock session-lock surface the compositor owns. let - cfg = config.modules.desktop.session.hyprland.hyprlock; + cfg = config.modules.desktop.session.hyprland.hyprlock; # NOLINT(let_order) user = config.user.name; # The hyprlock this module installs, used by the lock keybind below. diff --git a/configs/nixos/modules/desktop/session/login.nix b/configs/nixos/modules/desktop/session/login.nix index d3f6cfd..b1aa8fb 100644 --- a/configs/nixos/modules/desktop/session/login.nix +++ b/configs/nixos/modules/desktop/session/login.nix @@ -7,7 +7,7 @@ # Text login: greetd running the tuigreet greeter, which starts the session # through the universal Wayland session manager. let - cfg = config.modules.desktop.session.login; + cfg = config.modules.desktop.session.login; # NOLINT(let_order) in { options.modules.desktop.session.login.enable = diff --git a/configs/nixos/modules/desktop/session/mako.nix b/configs/nixos/modules/desktop/session/mako.nix index fc1829f..0c4703c 100644 --- a/configs/nixos/modules/desktop/session/mako.nix +++ b/configs/nixos/modules/desktop/session/mako.nix @@ -6,7 +6,7 @@ }: # mako as the notification daemon: toasts, a do-not-disturb mode, and history recall. let - cfg = config.modules.desktop.session.mako; + cfg = config.modules.desktop.session.mako; # NOLINT(let_order) user = config.user.name; makoctl = "${pkgs.mako}/bin/makoctl"; diff --git a/configs/nixos/modules/desktop/session/portals.nix b/configs/nixos/modules/desktop/session/portals.nix index cd156a8..6132146 100644 --- a/configs/nixos/modules/desktop/session/portals.nix +++ b/configs/nixos/modules/desktop/session/portals.nix @@ -1,7 +1,7 @@ { config, lib, ... }: # XDG desktop portal routing for in-app screen sharing and file dialogs. let - cfg = config.modules.desktop.session.portals; + cfg = config.modules.desktop.session.portals; # NOLINT(let_order) in { options.modules.desktop.session.portals.enable = diff --git a/configs/nixos/modules/desktop/session/rofi.nix b/configs/nixos/modules/desktop/session/rofi.nix index 8a62c56..a39d0d3 100644 --- a/configs/nixos/modules/desktop/session/rofi.nix +++ b/configs/nixos/modules/desktop/session/rofi.nix @@ -6,7 +6,7 @@ }: # rofi as the desktop launcher and dmenu-style menu frontend. let - cfg = config.modules.desktop.session.rofi; + cfg = config.modules.desktop.session.rofi; # NOLINT(let_order) user = config.user.name; # A session menu built on the same themed rofi, so the power actions look diff --git a/configs/nixos/modules/desktop/session/waybar.nix b/configs/nixos/modules/desktop/session/waybar.nix index 330903f..d13ba8d 100644 --- a/configs/nixos/modules/desktop/session/waybar.nix +++ b/configs/nixos/modules/desktop/session/waybar.nix @@ -6,7 +6,7 @@ }: # Waybar: a top status bar reading system state at a glance. let - cfg = config.modules.desktop.session.waybar; + cfg = config.modules.desktop.session.waybar; # NOLINT(let_order) vpnClient = config.modules.network.vpn-client; user = config.user.name; diff --git a/configs/nixos/modules/desktop/utilities/audio.nix b/configs/nixos/modules/desktop/utilities/audio.nix index 0412bc9..375060b 100644 --- a/configs/nixos/modules/desktop/utilities/audio.nix +++ b/configs/nixos/modules/desktop/utilities/audio.nix @@ -1,7 +1,7 @@ { config, lib, ... }: # PipeWire as the desktop audio server. let - cfg = config.modules.desktop.utilities.audio; + cfg = config.modules.desktop.utilities.audio; # NOLINT(let_order) in { options.modules.desktop.utilities.audio.enable = lib.mkEnableOption "the PipeWire audio server"; diff --git a/configs/nixos/modules/desktop/utilities/clipboard.nix b/configs/nixos/modules/desktop/utilities/clipboard.nix index 612255f..981426b 100644 --- a/configs/nixos/modules/desktop/utilities/clipboard.nix +++ b/configs/nixos/modules/desktop/utilities/clipboard.nix @@ -6,7 +6,7 @@ }: # Clipboard history: cliphist records every copy, picked back through rofi. let - cfg = config.modules.desktop.utilities.clipboard; + cfg = config.modules.desktop.utilities.clipboard; # NOLINT(let_order) user = config.user.name; cliphist = "${pkgs.cliphist}/bin/cliphist"; diff --git a/configs/nixos/modules/desktop/utilities/osd.nix b/configs/nixos/modules/desktop/utilities/osd.nix index d6227c5..031fa7c 100644 --- a/configs/nixos/modules/desktop/utilities/osd.nix +++ b/configs/nixos/modules/desktop/utilities/osd.nix @@ -6,7 +6,7 @@ }: # SwayOSD: a transient on-screen popup for volume and brightness changes. let - cfg = config.modules.desktop.utilities.osd; + cfg = config.modules.desktop.utilities.osd; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/desktop/utilities/recording.nix b/configs/nixos/modules/desktop/utilities/recording.nix index 7fa9ccc..15102a6 100644 --- a/configs/nixos/modules/desktop/utilities/recording.nix +++ b/configs/nixos/modules/desktop/utilities/recording.nix @@ -6,7 +6,7 @@ }: # Keybound screen recording: pick a region, then toggle a video-only capture. let - cfg = config.modules.desktop.utilities.recording; + cfg = config.modules.desktop.utilities.recording; # NOLINT(let_order) user = config.user.name; slurp = "${pkgs.slurp}/bin/slurp"; diff --git a/configs/nixos/modules/desktop/utilities/screenshot.nix b/configs/nixos/modules/desktop/utilities/screenshot.nix index ba18c98..ff33392 100644 --- a/configs/nixos/modules/desktop/utilities/screenshot.nix +++ b/configs/nixos/modules/desktop/utilities/screenshot.nix @@ -7,7 +7,7 @@ # Keyboard-driven screenshots that open in satty for annotation, then land in # both the clipboard and a dated file. let - cfg = config.modules.desktop.utilities.screenshot; + cfg = config.modules.desktop.utilities.screenshot; # NOLINT(let_order) user = config.user.name; grimblast = "${pkgs.grimblast}/bin/grimblast"; diff --git a/configs/nixos/modules/desktop/utilities/theming.nix b/configs/nixos/modules/desktop/utilities/theming.nix index 6c7768d..2fb9f80 100644 --- a/configs/nixos/modules/desktop/utilities/theming.nix +++ b/configs/nixos/modules/desktop/utilities/theming.nix @@ -6,7 +6,7 @@ }: # Nord theming for the graphical layer, driven from one base16 scheme by Stylix. let - cfg = config.modules.desktop.utilities.theming; + cfg = config.modules.desktop.utilities.theming; # NOLINT(let_order) user = config.user.name; # A single static Nord wallpaper, drawn as a vertical gradient across the diff --git a/configs/nixos/modules/desktop/utilities/userdirs.nix b/configs/nixos/modules/desktop/utilities/userdirs.nix index 7909f10..0b7a0f0 100644 --- a/configs/nixos/modules/desktop/utilities/userdirs.nix +++ b/configs/nixos/modules/desktop/utilities/userdirs.nix @@ -2,7 +2,7 @@ # Declares the XDG user directories, so a tool that queries them lands in a # well-known folder a host can relocate from one place. let - cfg = config.modules.desktop.utilities.userdirs; + cfg = config.modules.desktop.utilities.userdirs; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/guest.nix b/configs/nixos/modules/guest.nix index b786050..588614e 100644 --- a/configs/nixos/modules/guest.nix +++ b/configs/nixos/modules/guest.nix @@ -1,6 +1,6 @@ { config, lib, ... }: let - cfg = config.modules.guest; + cfg = config.modules.guest; # NOLINT(let_order) in { options.modules.guest.enable = lib.mkEnableOption "nested-guest base configuration"; diff --git a/configs/nixos/modules/host.nix b/configs/nixos/modules/host.nix index ca4ce4d..3805c92 100644 --- a/configs/nixos/modules/host.nix +++ b/configs/nixos/modules/host.nix @@ -4,7 +4,7 @@ ... }: let - cfg = config.modules.host; + cfg = config.modules.host; # NOLINT(let_order) inherit (config) user; hostSecrets = ../secrets + "/${config.networking.hostName}.yaml"; passwordSecret = "${user.name}-password"; diff --git a/configs/nixos/modules/monorepo.nix b/configs/nixos/modules/monorepo.nix index 38c963e..b46806a 100644 --- a/configs/nixos/modules/monorepo.nix +++ b/configs/nixos/modules/monorepo.nix @@ -5,7 +5,7 @@ }: # Tools for developing this monorepo. let - cfg = config.modules.monorepo; + cfg = config.modules.monorepo; # NOLINT(let_order) in { options.modules.monorepo.enable = lib.mkEnableOption "tools for developing this monorepo"; diff --git a/configs/nixos/modules/network/ssh.nix b/configs/nixos/modules/network/ssh.nix index 45e8e75..1571352 100644 --- a/configs/nixos/modules/network/ssh.nix +++ b/configs/nixos/modules/network/ssh.nix @@ -6,7 +6,7 @@ }: # SSH on this machine, in both directions. let - cfg = config.modules.network.ssh; + cfg = config.modules.network.ssh; # NOLINT(let_order) user = config.user.name; inherit (lib) diff --git a/configs/nixos/modules/network/vlans.nix b/configs/nixos/modules/network/vlans.nix index c945821..25d75de 100644 --- a/configs/nixos/modules/network/vlans.nix +++ b/configs/nixos/modules/network/vlans.nix @@ -6,7 +6,7 @@ }: # The host networking foundation: per-VLAN bridges over a tagged trunk. let - cfg = config.modules.network.vlans; + cfg = config.modules.network.vlans; # NOLINT(let_order) inherit (util) bridgeName; diff --git a/configs/nixos/modules/network/vpn-client.nix b/configs/nixos/modules/network/vpn-client.nix index 495b95a..19d4669 100644 --- a/configs/nixos/modules/network/vpn-client.nix +++ b/configs/nixos/modules/network/vpn-client.nix @@ -4,7 +4,7 @@ ... }: let - cfg = config.modules.network.vpn-client; + cfg = config.modules.network.vpn-client; # NOLINT(let_order) in { options.modules.network.vpn-client = { diff --git a/configs/nixos/modules/services/actualbudget.nix b/configs/nixos/modules/services/actualbudget.nix index 73e7bdd..eaddda6 100644 --- a/configs/nixos/modules/services/actualbudget.nix +++ b/configs/nixos/modules/services/actualbudget.nix @@ -4,7 +4,7 @@ ... }: let - cfg = config.modules.services.actualbudget; + cfg = config.modules.services.actualbudget; # NOLINT(let_order) in { options.modules.services.actualbudget = { diff --git a/configs/nixos/modules/services/copyparty.nix b/configs/nixos/modules/services/copyparty.nix index e1d0ce3..488ec45 100644 --- a/configs/nixos/modules/services/copyparty.nix +++ b/configs/nixos/modules/services/copyparty.nix @@ -5,7 +5,7 @@ ... }: let - cfg = config.modules.services.copyparty; + cfg = config.modules.services.copyparty; # NOLINT(let_order) volumeType = lib.types.submodule { options = { diff --git a/configs/nixos/modules/services/homepage.nix b/configs/nixos/modules/services/homepage.nix index 4017f88..9d63977 100644 --- a/configs/nixos/modules/services/homepage.nix +++ b/configs/nixos/modules/services/homepage.nix @@ -5,7 +5,7 @@ ... }: let - cfg = config.modules.services.homepage; + cfg = config.modules.services.homepage; # NOLINT(let_order) mkRegistryPath = registryName: host: entry: diff --git a/configs/nixos/modules/services/navidrome.nix b/configs/nixos/modules/services/navidrome.nix index 9bcacaa..c90fac5 100644 --- a/configs/nixos/modules/services/navidrome.nix +++ b/configs/nixos/modules/services/navidrome.nix @@ -4,7 +4,7 @@ ... }: let - cfg = config.modules.services.navidrome; + cfg = config.modules.services.navidrome; # NOLINT(let_order) in { options.modules.services.navidrome = { diff --git a/configs/nixos/modules/services/reverse-proxy.nix b/configs/nixos/modules/services/reverse-proxy.nix index 8ce9fb4..806187c 100644 --- a/configs/nixos/modules/services/reverse-proxy.nix +++ b/configs/nixos/modules/services/reverse-proxy.nix @@ -5,7 +5,7 @@ ... }: let - cfg = config.modules.services.reverse-proxy; + cfg = config.modules.services.reverse-proxy; # NOLINT(let_order) mkRegistryPath = registryName: host: entry: diff --git a/configs/nixos/modules/shell/bazel.nix b/configs/nixos/modules/shell/bazel.nix index e984bec..c97cf99 100644 --- a/configs/nixos/modules/shell/bazel.nix +++ b/configs/nixos/modules/shell/bazel.nix @@ -6,7 +6,7 @@ }: # Bazel for the primary user's shell environment. let - cfg = config.modules.shell.bazel; + cfg = config.modules.shell.bazel; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/shell/direnv/default.nix b/configs/nixos/modules/shell/direnv/default.nix index c88953c..418093b 100644 --- a/configs/nixos/modules/shell/direnv/default.nix +++ b/configs/nixos/modules/shell/direnv/default.nix @@ -5,7 +5,7 @@ }: # direnv for the primary user, configured through home-manager. let - cfg = config.modules.shell.direnv; + cfg = config.modules.shell.direnv; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/shell/fish/default.nix b/configs/nixos/modules/shell/fish/default.nix index 4f8ef12..b3122de 100644 --- a/configs/nixos/modules/shell/fish/default.nix +++ b/configs/nixos/modules/shell/fish/default.nix @@ -6,7 +6,7 @@ }: # fish for the primary user, configured through home-manager. let - cfg = config.modules.shell.fish; + cfg = config.modules.shell.fish; # NOLINT(let_order) user = config.user.name; shortcuts = lib.attrByPath [ config.networking.hostName ] { } config.registry.shell-shortcut; envVars = lib.attrByPath [ config.networking.hostName ] { } config.registry.env-var; diff --git a/configs/nixos/modules/shell/git.nix b/configs/nixos/modules/shell/git.nix index 88b6bcf..6e59530 100644 --- a/configs/nixos/modules/shell/git.nix +++ b/configs/nixos/modules/shell/git.nix @@ -5,7 +5,7 @@ }: # git for the primary user, configured through home-manager. let - cfg = config.modules.shell.git; + cfg = config.modules.shell.git; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/shell/gitea-axi.nix b/configs/nixos/modules/shell/gitea-axi.nix index b35d4e6..98c9ed5 100644 --- a/configs/nixos/modules/shell/gitea-axi.nix +++ b/configs/nixos/modules/shell/gitea-axi.nix @@ -6,7 +6,7 @@ }: # gitea-axi for the primary user, installed through its own home-manager module. let - cfg = config.modules.shell.gitea-axi; + cfg = config.modules.shell.gitea-axi; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/shell/herdr.nix b/configs/nixos/modules/shell/herdr.nix index b91e03c..9849be4 100644 --- a/configs/nixos/modules/shell/herdr.nix +++ b/configs/nixos/modules/shell/herdr.nix @@ -6,7 +6,7 @@ }: # Herdr, a terminal multiplexer for coding agents. let - cfg = config.modules.shell.herdr; + cfg = config.modules.shell.herdr; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/shell/nvim/default.nix b/configs/nixos/modules/shell/nvim/default.nix index 97dfa1e..9defd01 100644 --- a/configs/nixos/modules/shell/nvim/default.nix +++ b/configs/nixos/modules/shell/nvim/default.nix @@ -7,7 +7,7 @@ }: # Neovim for the primary user, configured declaratively through nixvim. let - cfg = config.modules.shell.nvim; + cfg = config.modules.shell.nvim; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/shell/omp.nix b/configs/nixos/modules/shell/omp.nix index 557bf25..db599f5 100644 --- a/configs/nixos/modules/shell/omp.nix +++ b/configs/nixos/modules/shell/omp.nix @@ -6,7 +6,7 @@ }: # Oh My Pi, installed alongside upstream Pi as the `omp` command. let - cfg = config.modules.shell.omp; + cfg = config.modules.shell.omp; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/shell/pi/default.nix b/configs/nixos/modules/shell/pi/default.nix index 26be720..60e9340 100644 --- a/configs/nixos/modules/shell/pi/default.nix +++ b/configs/nixos/modules/shell/pi/default.nix @@ -8,7 +8,7 @@ # home-manager, which ships the package and manages global settings. # The login credential is left unmanaged, so it survives rebuilds. let - cfg = config.modules.shell.pi; + cfg = config.modules.shell.pi; # NOLINT(let_order) user = config.user.name; piDir = "${config.users.users.${user}.home}/.pi/agent"; vendoredPi = pkgs.pi-coding-agent.overrideAttrs { diff --git a/configs/nixos/modules/shell/python.nix b/configs/nixos/modules/shell/python.nix index c1c5f43..b33bedf 100644 --- a/configs/nixos/modules/shell/python.nix +++ b/configs/nixos/modules/shell/python.nix @@ -6,7 +6,7 @@ }: # Python tools for the primary user's shell environment. let - cfg = config.modules.shell.python; + cfg = config.modules.shell.python; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/shell/rust.nix b/configs/nixos/modules/shell/rust.nix index 0d12702..766015b 100644 --- a/configs/nixos/modules/shell/rust.nix +++ b/configs/nixos/modules/shell/rust.nix @@ -6,7 +6,7 @@ }: # Rust tools for the primary user's shell environment. let - cfg = config.modules.shell.rust; + cfg = config.modules.shell.rust; # NOLINT(let_order) user = config.user.name; toolchain = pkgs.symlinkJoin { name = "rust-toolchain"; diff --git a/configs/nixos/modules/shell/tmux/default.nix b/configs/nixos/modules/shell/tmux/default.nix index 1ff81d3..3b9bbfc 100644 --- a/configs/nixos/modules/shell/tmux/default.nix +++ b/configs/nixos/modules/shell/tmux/default.nix @@ -6,7 +6,7 @@ # tmux for the primary user, configured through home-manager. # Settings without a home-manager option are read from ./extra.conf. let - cfg = config.modules.shell.tmux; + cfg = config.modules.shell.tmux; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/shell/yazi.nix b/configs/nixos/modules/shell/yazi.nix index 8e57089..382fcc9 100644 --- a/configs/nixos/modules/shell/yazi.nix +++ b/configs/nixos/modules/shell/yazi.nix @@ -6,7 +6,7 @@ }: # Yazi, a terminal file manager. let - cfg = config.modules.shell.yazi; + cfg = config.modules.shell.yazi; # NOLINT(let_order) user = config.user.name; in { diff --git a/configs/nixos/modules/zfs.nix b/configs/nixos/modules/zfs.nix index 728641a..8c340d8 100644 --- a/configs/nixos/modules/zfs.nix +++ b/configs/nixos/modules/zfs.nix @@ -5,7 +5,7 @@ }: # The host-level ZFS pool import: durable service state a host mounts, never rebuilds. let - cfg = config.modules.zfs; + cfg = config.modules.zfs; # NOLINT(let_order) in { options.modules.zfs = { From e5b76515f15152619ecda3d28c8f5b0e793e5e97 Mon Sep 17 00:00:00 2001 From: alexion Date: Sat, 26 Sep 2026 23:43:45 -0400 Subject: [PATCH 5/5] refactor(nix): order let bindings --- .../nixos/modules/desktop/apps/firefox.nix | 2 +- .../desktop/session/hyprland/default.nix | 3 +- .../desktop/session/hyprland/hypridle.nix | 7 +- .../nixos/modules/desktop/session/waybar.nix | 3 +- .../modules/desktop/utilities/theming.nix | 2 +- configs/nixos/modules/network/ssh.nix | 136 ++++++++-------- configs/nixos/modules/services/copyparty.nix | 50 +++--- configs/nixos/modules/services/homepage.nix | 96 +++++------ .../nixos/modules/services/reverse-proxy.nix | 150 +++++++++--------- configs/nixos/modules/services/steamcmd.nix | 4 +- configs/nixos/modules/shell/fish/default.nix | 6 +- configs/nixos/modules/shell/pi/default.nix | 4 +- configs/nixos/modules/shell/rust.nix | 2 +- configs/nixos/registry/stable-uid.nix | 13 +- src/dotfiles/guests.nix | 12 +- src/dotfiles/hosts.nix | 4 +- 16 files changed, 245 insertions(+), 249 deletions(-) diff --git a/configs/nixos/modules/desktop/apps/firefox.nix b/configs/nixos/modules/desktop/apps/firefox.nix index 4693a03..e29035a 100644 --- a/configs/nixos/modules/desktop/apps/firefox.nix +++ b/configs/nixos/modules/desktop/apps/firefox.nix @@ -8,8 +8,8 @@ # Firefox as the desktop browser: stock mainline, hardened and de-monetized by policy. let cfg = config.modules.desktop.apps.firefox; # NOLINT(let_order) - user = config.user.name; firefoxAddons = inputs.firefox-addons.packages.${pkgs.stdenv.hostPlatform.system}; + user = config.user.name; in { options.modules.desktop.apps.firefox = { diff --git a/configs/nixos/modules/desktop/session/hyprland/default.nix b/configs/nixos/modules/desktop/session/hyprland/default.nix index cdb462a..7cc25db 100644 --- a/configs/nixos/modules/desktop/session/hyprland/default.nix +++ b/configs/nixos/modules/desktop/session/hyprland/default.nix @@ -7,7 +7,6 @@ # The Hyprland compositor. let cfg = config.modules.desktop.session.hyprland; # NOLINT(let_order) - user = config.user.name; cursor = config.stylix.cursor; # Numbered-workspace switch and move for 1..9, the operator's i3 muscle memory. @@ -16,6 +15,8 @@ let "$mod SHIFT, ${toString n}, movetoworkspace, ${toString n}" ]) (lib.range 1 9); + user = config.user.name; + # Flip the tiling strategy between the two built-in layouts, since neither a # dispatcher nor a keyword toggles it on its own. toggleLayout = pkgs.writeShellScript "hypr-toggle-layout" '' diff --git a/configs/nixos/modules/desktop/session/hyprland/hypridle.nix b/configs/nixos/modules/desktop/session/hyprland/hypridle.nix index e87b078..a77e501 100644 --- a/configs/nixos/modules/desktop/session/hyprland/hypridle.nix +++ b/configs/nixos/modules/desktop/session/hyprland/hypridle.nix @@ -7,14 +7,15 @@ # hypridle: idle-triggered locking and display power management. let cfg = config.modules.desktop.session.hyprland.hypridle; # NOLINT(let_order) + + # The guard drops the call when a locker is already up, so no idle trigger + # stacks a second hyprlock over the first. + lockCmd = "${pkgs.procps}/bin/pidof hyprlock || ${hyprlock}"; user = config.user.name; hyprctl = "${config.programs.hyprland.package}/bin/hyprctl"; hyprlock = "${config.home-manager.users.${user}.programs.hyprlock.package}/bin/hyprlock"; - # The guard drops the call when a locker is already up, so no idle trigger - # stacks a second hyprlock over the first. - lockCmd = "${pkgs.procps}/bin/pidof hyprlock || ${hyprlock}"; in { options.modules.desktop.session.hyprland.hypridle = { diff --git a/configs/nixos/modules/desktop/session/waybar.nix b/configs/nixos/modules/desktop/session/waybar.nix index d13ba8d..3015552 100644 --- a/configs/nixos/modules/desktop/session/waybar.nix +++ b/configs/nixos/modules/desktop/session/waybar.nix @@ -8,11 +8,12 @@ let cfg = config.modules.desktop.session.waybar; # NOLINT(let_order) vpnClient = config.modules.network.vpn-client; - user = config.user.name; # A Nerd Font Private-Use-Area codepoint as a literal character, decoded # through JSON so the glyph survives as bytes rather than an editor paste. g = code: builtins.fromJSON ''"\u${code}"''; + user = config.user.name; + sleepOffIcon = builtins.fromJSON ''"\udb81\udcb3"''; # Renders and toggles mako's do-not-disturb mode. diff --git a/configs/nixos/modules/desktop/utilities/theming.nix b/configs/nixos/modules/desktop/utilities/theming.nix index 2fb9f80..e1278fb 100644 --- a/configs/nixos/modules/desktop/utilities/theming.nix +++ b/configs/nixos/modules/desktop/utilities/theming.nix @@ -7,7 +7,6 @@ # Nord theming for the graphical layer, driven from one base16 scheme by Stylix. let cfg = config.modules.desktop.utilities.theming; # NOLINT(let_order) - user = config.user.name; # A single static Nord wallpaper, drawn as a vertical gradient across the # Polar Night shades so it needs no committed binary and no network fetch. @@ -16,6 +15,7 @@ let -size 2560x1440 gradient:'#2E3440'-'#3B4252' \ "$out" ''; + user = config.user.name; in { options.modules.desktop.utilities.theming.enable = diff --git a/configs/nixos/modules/network/ssh.nix b/configs/nixos/modules/network/ssh.nix index 1571352..4d7c2bb 100644 --- a/configs/nixos/modules/network/ssh.nix +++ b/configs/nixos/modules/network/ssh.nix @@ -7,32 +7,6 @@ # SSH on this machine, in both directions. let cfg = config.modules.network.ssh; # NOLINT(let_order) - user = config.user.name; - - inherit (lib) - concatLists - concatStringsSep - elem - filter - genAttrs - hasAttr - imap0 - listToAttrs - mapAttrs - mapAttrsToList - mkIf - mkMerge - mkOption - nameValuePair - optional - optionalAttrs - types - unique - ; - - hostKeySecret = type: "ssh-host-${type}-key"; - userKeySecret = "ssh-user-ed25519-key"; - targetType = types.submodule ( { name, ... }: { @@ -90,54 +64,18 @@ let } ); - keyWithoutComment = key: concatStringsSep " " (lib.take 2 (lib.splitString " " key)); - - targetNamesIn = names: filter (name: hasAttr name cfg.targets) names; - - workstationNames = targetNamesIn cfg.hosts.workstations; - serverNames = targetNamesIn cfg.hosts.servers; - - clientKeysFor = names: filter (key: key != null) (map (name: cfg.targets.${name}.clientKey) names); - - currentHost = config.networking.hostName; - isServer = elem currentHost cfg.hosts.servers; - isWorkstation = elem currentHost cfg.hosts.workstations; - defaultAuthorizedKeys = lib.flatten ( clientKeysFor workstationNames ++ optional isServer (clientKeysFor serverNames) ); - outboundTargetNames = - let - groupTargets = - if isServer then - [ "gitea" ] ++ cfg.hosts.servers - else if isWorkstation then - [ "gitea" ] ++ cfg.hosts.servers ++ cfg.hosts.workstations - else - [ "gitea" ]; - in - filter (name: name != currentHost) (targetNamesIn groupTargets); + clientKeysFor = names: filter (key: key != null) (map (name: cfg.targets.${name}.clientKey) names); - sshSettingsFor = - name: - let - target = cfg.targets.${name}; - in - { - header = "Host ${concatStringsSep " " target.aliases}"; - HostName = target.hostName; - User = target.user; - } - // optionalAttrs (target.port != 22) { Port = target.port; }; + workstationNames = targetNamesIn cfg.hosts.workstations; + targetNamesIn = names: filter (name: hasAttr name cfg.targets) names; - knownHostNamesFor = - target: - let - names = unique (target.aliases ++ [ target.hostName ]); - withPort = name: if target.port == 22 then name else "[${name}]:${toString target.port}"; - in - map withPort names; + isServer = elem currentHost cfg.hosts.servers; + currentHost = config.networking.hostName; + serverNames = targetNamesIn cfg.hosts.servers; knownHosts = listToAttrs ( concatLists ( @@ -153,6 +91,68 @@ let ) cfg.targets ) ); + knownHostNamesFor = + target: + let + withPort = name: if target.port == 22 then name else "[${name}]:${toString target.port}"; + names = unique (target.aliases ++ [ target.hostName ]); + in + map withPort names; + + keyWithoutComment = key: concatStringsSep " " (lib.take 2 (lib.splitString " " key)); + + sshSettingsFor = + name: + let + target = cfg.targets.${name}; + in + { + header = "Host ${concatStringsSep " " target.aliases}"; + HostName = target.hostName; + User = target.user; + } + // optionalAttrs (target.port != 22) { Port = target.port; }; + + outboundTargetNames = + let + groupTargets = + if isServer then + [ "gitea" ] ++ cfg.hosts.servers + else if isWorkstation then + [ "gitea" ] ++ cfg.hosts.servers ++ cfg.hosts.workstations + else + [ "gitea" ]; + in + filter (name: name != currentHost) (targetNamesIn groupTargets); + + isWorkstation = elem currentHost cfg.hosts.workstations; + + hostKeySecret = type: "ssh-host-${type}-key"; + user = config.user.name; + + inherit (lib) + concatLists + concatStringsSep + elem + filter + genAttrs + hasAttr + imap0 + listToAttrs + mapAttrs + mapAttrsToList + mkIf + mkMerge + mkOption + nameValuePair + optional + optionalAttrs + types + unique + ; + + userKeySecret = "ssh-user-ed25519-key"; + in { options.modules.network.ssh = { diff --git a/configs/nixos/modules/services/copyparty.nix b/configs/nixos/modules/services/copyparty.nix index 488ec45..25c1459 100644 --- a/configs/nixos/modules/services/copyparty.nix +++ b/configs/nixos/modules/services/copyparty.nix @@ -38,6 +38,31 @@ let }; }; + configScript = pkgs.writeShellScript "copyparty-config" '' + set -eu + + umask 077 + password="$(${pkgs.coreutils}/bin/tr -d '\n' < "$CREDENTIALS_DIRECTORY/password")" + + cat > /run/copyparty/copyparty.conf < /run/copyparty/copyparty.conf < 1) (serviceNamesByUid entries); serviceNamesByUid = entries: @@ -13,10 +10,6 @@ let groupBy (entry: toString entry.value) entries ); - duplicateUids = - entries: - filterAttrs (_uid: serviceNames: builtins.length serviceNames > 1) (serviceNamesByUid entries); - in { # Next stable UID: 20005 diff --git a/src/dotfiles/guests.nix b/src/dotfiles/guests.nix index 4085661..ba4cad9 100644 --- a/src/dotfiles/guests.nix +++ b/src/dotfiles/guests.nix @@ -8,6 +8,7 @@ util, }: let + files = util.collectNixFiles root; mkGuest = name: { config, lib, ... }: @@ -167,22 +168,22 @@ let config = lib.mkIf cfg.enable { assertions = let - hasDirectorySettings = mount: mount.owner != null || mount.group != null || mount.mode != null; - hasCompleteDirectorySettings = - mount: mount.owner != null && mount.group != null && mount.mode != null; - hasOneSource = mount: (mount.directory != null) != (mount.file != null); mountsWithInvalidSource = lib.attrNames ( lib.filterAttrs (_: mount: !hasOneSource mount) cfg.mounts ); + hasOneSource = mount: (mount.directory != null) != (mount.file != null); fileMountsWithHostSettings = lib.attrNames ( lib.filterAttrs (_: mount: mount.file != null && hasDirectorySettings mount) cfg.mounts ); + hasDirectorySettings = mount: mount.owner != null || mount.group != null || mount.mode != null; directoryMountsWithPartialHostSettings = lib.attrNames ( lib.filterAttrs ( _: mount: mount.directory != null && hasDirectorySettings mount && !hasCompleteDirectorySettings mount ) cfg.mounts ); + hasCompleteDirectorySettings = + mount: mount.owner != null && mount.group != null && mount.mode != null; in [ { @@ -275,11 +276,10 @@ let }; }; - files = util.collectNixFiles root; + names = map (file: lib.removeSuffix ".nix" file) (lib.attrNames guestFiles); guestFiles = lib.filterAttrs (file: type: type == "regular" && lib.hasSuffix ".nix" file) ( builtins.readDir root ); - names = map (file: lib.removeSuffix ".nix" file) (lib.attrNames guestFiles); in { imports = files ++ map mkGuest names; diff --git a/src/dotfiles/hosts.nix b/src/dotfiles/hosts.nix index 09ff2dd..5ce6e53 100644 --- a/src/dotfiles/hosts.nix +++ b/src/dotfiles/hosts.nix @@ -9,6 +9,8 @@ registryModule, }: let + names = lib.attrNames directories; + directories = lib.filterAttrs (_name: type: type == "directory") (builtins.readDir root); mkHost = { root, @@ -36,7 +38,5 @@ let ]; }; - directories = lib.filterAttrs (_name: type: type == "directory") (builtins.readDir root); - names = lib.attrNames directories; in lib.genAttrs names (name: mkHost { inherit root name; })