-
-
Notifications
You must be signed in to change notification settings - Fork 85
treewide: refactor --build-host to use remote build semantics
#497
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAdds a public SSH-based remote build and activation subsystem, threads elevation strategy and password-caching through build/activation flows, replaces ad-hoc tokenization with Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI as nh (local)
participant Build as Build Host
participant Target as Target Host
User->>CLI: nh os switch --build-host build.example
activate CLI
rect `#EAF5FF`
CLI->>CLI: evaluate drvPath (local nix eval)
end
rect `#E8FFE6`
CLI->>Build: init SSH control + nix-copy-closure (ssh)
Build->>Build: remote nix build (nom optional)
Build-->>CLI: copy result back (nix-copy-closure / nix copy)
end
rect `#FFF6E6`
alt target_host specified
CLI->>Target: copy result -> target_host
Target->>Target: validate & activate (switch/test/boot)
else local activation
CLI->>CLI: validate & activate using out_path (local)
end
end
CLI-->>User: completed
deactivate CLI
sequenceDiagram
participant CLI as nh
participant SSHCtrl as SSH Control
participant Signal as SIGINT handler
participant RemoteOp as Remote Operation
CLI->>SSHCtrl: init_ssh_control() (create control path)
CLI->>Signal: register SIGINT handler
SSHCtrl->>RemoteOp: reuse control socket for remote commands
RemoteOp->>RemoteOp: execute remote nix build / copy
alt user presses Ctrl+C
Signal->>RemoteOp: set interrupt flag
alt NH_REMOTE_CLEANUP enabled
RemoteOp->>RemoteOp: terminate remote Nix processes via SSH
end
end
SSHCtrl->>SSHCtrl: SshControlGuard::drop() cleans control path
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom Pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
🧰 Additional context used🧬 Code graph analysis (2)src/nixos.rs (1)
src/commands.rs (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
🔇 Additional comments (9)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Nitpick comments (2)
src/remote.rs (2)
18-37: Consider cleanup of SSH control directory on exit.The SSH control socket directory (
nh-ssh-{pid}) persists after the program terminates. While theControlPersist=60setting ensures connections close after 60 seconds of inactivity, the empty directory remains. This is a minor resource leak.You could implement cleanup using a
Dropguard oratexithandler, though this is low priority since the impact is minimal (just empty directories in/tmporXDG_RUNTIME_DIR).
368-409: Consider extracting common attribute-appending logic.The pattern of cloning the installable and appending
"drvPath"to the attribute is repeated forFlake,File, andExpressionvariants. This could be simplified with a helper method onInstallable.This is a minor refactor opportunity for future cleanup - the current implementation is correct and readable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/commands.rs(1 hunks)src/home.rs(1 hunks)src/interface.rs(2 hunks)src/lib.rs(1 hunks)src/main.rs(1 hunks)src/nixos.rs(2 hunks)src/remote.rs(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/interface.rs (1)
src/commands.rs (1)
arg(248-251)
src/remote.rs (1)
src/util.rs (1)
get_nix_variant(56-85)
src/nixos.rs (1)
src/remote.rs (2)
parse(67-107)build_remote(474-517)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Build NH on Linux
- GitHub Check: profile
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: Test NH on Darwin
- GitHub Check: Test NH on Linux
- GitHub Check: Build NH on Linux
- GitHub Check: Build NH on Darwin
🔇 Additional comments (10)
src/main.rs (1)
12-12: LGTM!Module declaration is correctly placed and follows the existing naming conventions.
src/lib.rs (1)
13-13: LGTM!Public module declaration is consistent with other modules in this file. The internal-use comment at line 1 applies to the entire library.
src/nixos.rs (2)
25-25: LGTM!Import of remote module types is correctly placed and all imported items are used in the
execute_build_commandmethod.
354-407: Well-structured remote build implementation matchingnixos-rebuildsemantics.The implementation correctly:
- Parses host specifications with proper error context
- Constructs the
RemoteBuildConfigwith all necessary parameters- Preserves the local build path as a fallback
- Passes extra args from both explicit args and passthrough options
The inline documentation clearly explains the workflow, which helps maintainability.
src/commands.rs (1)
654-752: LGTM! Clean removal of builder field from Build struct.The
Buildstruct and its tests have been correctly updated to remove thebuilderfield and related method. The test at line 1200 properly removes the.builder("user@host")call, and the struct now cleanly delegates remote build concerns to the newremotemodule.src/remote.rs (5)
67-107: LGTM! Robust host specification parsing with helpful error messages.The
RemoteHost::parsefunction handles various input formats correctly and provides actionable error messages (e.g., suggestingNIX_SSHOPTSfor port configuration). The validation of edge cases like empty usernames, hostnames, and invalid characters is comprehensive.
206-254: LGTM! Secure remote command execution.The function properly:
- Shell-quotes all arguments to prevent injection
- Uses
--separator to prevent argument injection to SSH- Includes stderr in error messages for debugging
- Wraps errors with context
256-362: LGTM! Well-structured closure copying functions.The three copy functions handle the different scenarios correctly:
copy_closure_toandcopy_closure_fromusenix-copy-closurefor localhost ↔ remote transferscopy_closure_between_remotesusesnix copywith SSH URIs for remote-to-remote transfers- Substitutes flags are correctly mapped (
--use-substitutesvs--substitute-on-destination)
577-653: LGTM! Clever approach for nom integration.The double-invocation pattern (build with nom consuming output, then query for output path) is a reasonable workaround since nom consumes the JSON output. The second build is a no-op due to Nix caching, so the overhead is minimal.
655-762: LGTM! Comprehensive unit tests for parsing and quoting logic.The tests provide good coverage for:
- Various
RemoteHostinput formats and error cases- Shell quoting edge cases including the critical
drv^*syntax- SSH options default values
The
unsafeblock at lines 753-755 for environment variable manipulation is acceptable in test code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/home.rs (1)
106-124: Consider extracting shared remote build configuration logic.The
RemoteBuildConfigconstruction here is nearly identical to the one insrc/darwin.rs(lines 119-137). A shared helper function could reduce duplication:// In a shared location (e.g., remote.rs or a new util) fn build_config_from_args( build_host: RemoteHost, no_nom: bool, use_substitutes: bool, extra_args: &[String], passthrough: &Passthrough, ) -> RemoteBuildConfig { ... }This is a minor refactoring opportunity that can be deferred.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/darwin.rs(2 hunks)src/home.rs(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/home.rs (1)
src/remote.rs (2)
parse(67-107)build_remote(474-517)
src/darwin.rs (1)
src/remote.rs (2)
parse(67-107)build_remote(474-517)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: Test NH on Darwin
- GitHub Check: Test NH on Linux
- GitHub Check: Build NH on Linux
- GitHub Check: Build NH on Darwin
- GitHub Check: treewide-checks
- GitHub Check: profile
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: Build NH on Darwin
- GitHub Check: Build NH on Linux
- GitHub Check: treewide-checks
🔇 Additional comments (4)
src/darwin.rs (2)
18-18: LGTM!The new imports for the remote build subsystem are correctly added and all three symbols (
remote,RemoteBuildConfig,RemoteHost) are used in the remote build logic below.
112-151: Well-structured remote build integration.The conditional remote build path correctly:
- Parses the host specification with proper error context
- Constructs the
RemoteBuildConfigwith appropriate field mappings- Passes
out_pathas the out-link sobuild_remotecreates the symlink there- Falls back to the local build flow when no
build_hostis specifiedThe subsequent code (line 153 onwards) correctly uses
out_pathwhich will be a valid symlink after either build path completes.src/home.rs (2)
14-14: Imports are now properly used.The previously flagged unused imports (
remote,RemoteBuildConfig,RemoteHost) are now correctly used in the remote build logic at lines 103, 106, and 126 respectively.
99-138: Remote build integration looks correct.The conditional remote build path properly:
- Parses the host specification with error context
- Builds
RemoteBuildConfigwith the correct field mappings- Invokes
build_remotewith the out-link for symlink creation- Falls back to local build when no
build_hostis providedThe post-build steps (activation, diffs, etc.) work correctly since
out_pathwill be a valid symlink after either path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/remote.rs (1)
604-612: Non-UTF-8 extra arguments are silently dropped.As flagged in a previous review,
filter_map(|s| s.to_str().map(String::from))silently discards anyOsStringarguments that aren't valid UTF-8. Consider logging a warning when this occurs:let extra_args_strings: Vec<String> = config .extra_args .iter() - .filter_map(|s| s.to_str().map(String::from)) + .filter_map(|s| { + match s.to_str() { + Some(str) => Some(str.to_string()), + None => { + tracing::warn!("Dropping non-UTF-8 argument: {:?}", s); + None + } + } + }) .collect();The same pattern exists at lines 649-656.
🧹 Nitpick comments (5)
src/commands.rs (1)
44-46: Silent failure on malformed input may hide user errors.Using
unwrap_or_default()silently returns an empty vector whenshlex::splitfails (e.g., unclosed quotes). While the test at line 1421-1424 documents this behavior, returning an empty vector could mask user input errors in contexts likeself_elevate_cmd(line 444), where an empty parse leads to a bail at line 447.Consider logging a warning when parsing fails to aid debugging:
fn parse_cmdline_with_quotes(cmdline: &str) -> Vec<String> { - shlex::split(cmdline).unwrap_or_default() + shlex::split(cmdline).unwrap_or_else(|| { + tracing::warn!("Failed to parse command line (unclosed quote?): {cmdline}"); + Vec::new() + }) }src/darwin.rs (1)
112-140: Consider adding SSH reachability check for consistency with NixOS module.The NixOS implementation (src/nixos.rs lines 374-385) performs an SSH reachability check before starting expensive evaluation. This provides early feedback if the build host is unreachable. The Darwin implementation omits this check.
if let Some(ref build_host_str) = self.build_host { info!("Building Darwin configuration"); let build_host = RemoteHost::parse(build_host_str) .wrap_err("Invalid build host specification")?; + // Check SSH connectivity before expensive evaluation + info!("Checking SSH connectivity to build host..."); + remote::check_ssh_reachability(&build_host) + .wrap_err(format!("Build host ({build_host}) is not reachable"))?; + let config = RemoteBuildConfig {src/home.rs (1)
99-127: Consider adding SSH reachability check for consistency.Similar to the Darwin module, the Home-Manager implementation omits the SSH reachability pre-check that exists in the NixOS module (src/nixos.rs lines 374-385). For consistency and better user experience, consider adding the check:
if let Some(ref build_host_str) = self.build_host { info!("Building Home-Manager configuration"); let build_host = RemoteHost::parse(build_host_str) .wrap_err("Invalid build host specification")?; + info!("Checking SSH connectivity to build host..."); + remote::check_ssh_reachability(&build_host) + .wrap_err(format!("Build host ({build_host}) is not reachable"))?; + let config = RemoteBuildConfig {src/nixos.rs (1)
286-291: Minor: Documentation formatting could be improved.The
Returnssection has inconsistent line breaks that affect readability:/// # Returns - /// - /// `Result` containing a tuple: - /// + /// + /// Returns a `Result` containing a tuple: + /// /// - `bool`: `true` if elevation is required, `false` otherwise. /// - `String`: The resolved target hostname.src/remote.rs (1)
18-48: SSH control directory is not cleaned up on exit.The SSH control directory created at
get_ssh_control_dir()persists after the program exits. While the directory itself is small, the SSH control sockets inside may linger. Consider documenting this behavior or implementing cleanup:+// Note: The SSH control directory and sockets are intentionally not cleaned up +// on exit to allow connection multiplexing across multiple nh invocations. +// SSH's ControlPersist=60 handles socket cleanup after idle timeout. fn get_ssh_control_dir() -> &'static PathBuf {Alternatively, you could register an atexit handler to clean up the directory if this is undesirable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/commands.rs(4 hunks)src/darwin.rs(3 hunks)src/home.rs(3 hunks)src/nixos.rs(6 hunks)src/remote.rs(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/home.rs (1)
src/remote.rs (2)
parse(78-118)build_remote(522-570)
src/nixos.rs (1)
src/remote.rs (3)
check_ssh_reachability(143-173)parse(78-118)build_remote(522-570)
src/darwin.rs (1)
src/remote.rs (2)
parse(78-118)build_remote(522-570)
src/remote.rs (1)
src/util.rs (1)
get_nix_variant(56-85)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: Build NH on Linux
- GitHub Check: Build NH on Darwin
- GitHub Check: treewide-checks
- GitHub Check: Test NH on Darwin
- GitHub Check: Test NH on Linux
- GitHub Check: Build NH on Darwin
- GitHub Check: Build NH on Linux
- GitHub Check: treewide-checks
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
🔇 Additional comments (9)
src/commands.rs (2)
690-733: LGTM! Correct handling of pipeline exit status.Using
popen()to access individual processes and checking the first process's (nix build) exit status is the right approach. This ensures build failures are properly detected even when nom always succeeds.
1377-1442: LGTM! Comprehensive test coverage for shell parsing.The new tests thoroughly cover edge cases including escaped quotes, backslash escapes, nix store paths, environment variables in quotes, unclosed quotes, and complex sudo commands. Good coverage of the shlex delegation.
src/darwin.rs (1)
141-151: LGTM! Clean conditional branching between remote and local builds.The remote/local build branching is well-structured. The local build path correctly preserves the existing behavior with
--out-link,extra_args, passthrough, and nom support.src/home.rs (1)
128-138: LGTM! Local build fallback is correctly preserved.The existing local build path is properly maintained as a fallback when no
build_hostis specified.src/nixos.rs (1)
349-421: LGTM! Well-structured remote build implementation.The
execute_buildmethod properly implements the remote build workflow:
- Validates both
build_hostandtarget_hostspecifications- Performs SSH reachability checks before expensive evaluation
- Constructs
RemoteBuildConfigwith all necessary fields- Falls back to local build when no
build_hostis specifiedThe early SSH connectivity check provides good user feedback before starting the evaluation.
src/remote.rs (4)
191-197: LGTM! Shell quoting uses shlex with appropriate fallback.The implementation correctly uses
shlex::try_quotewith a sensible fallback for edge cases (NUL bytes). The fallback uses proper single-quote escaping.
704-722: Re-running nix build to get output path is a reasonable workaround.Since nom consumes the build output, re-querying with
--print-out-pathsis a pragmatic solution. The second invocation is effectively a no-op since the derivation is already built.
522-569: LGTM! Well-structured remote build workflow.The
build_remotefunction correctly implements the four-step workflow:
- Evaluate drvPath locally
- Copy derivation to build host
- Build on remote
- Copy result to destination (target_host or localhost)
The logic correctly handles the case where both
out_linkis requested andtarget_hostis set, ensuring the closure is available locally for specialisation resolution.
725-1036: LGTM! Comprehensive test coverage.The test suite thoroughly covers:
- Host parsing (bare, user@host, URI schemes, error cases)
- Shell quoting (simple strings, special characters, roundtrip verification)
- SSH options (default, with NIX_SSHOPTS, quoted values)
- Control directory creation
The roundtrip tests at lines 894-926 are particularly valuable for ensuring shell quoting correctness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/remote.rs (1)
540-610: LGTM: Correct remote build orchestration with comprehensive transfer logic.The workflow correctly implements the nixos-rebuild-ng pattern (evaluate locally → copy derivation → build remotely → transfer results). The conditional logic for copying to localhost (lines 586-598) handles all edge cases correctly, though it's complex.
Optional: Extract helper for copy-to-localhost decision
The conditional at lines 586-598 is correct but complex. Consider extracting to a helper:
fn should_copy_to_localhost( out_link: Option<&std::path::Path>, target_host: Option<&RemoteHost>, build_host: &RemoteHost, ) -> bool { out_link.is_some() || target_host.is_none() || target_host .as_ref() .is_some_and(|th| th.hostname() != build_host.hostname()) }Then use:
if should_copy_to_localhost(out_link, config.target_host.as_ref(), build_host) { ... }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/remote.rs(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/remote.rs (1)
src/util.rs (1)
get_nix_variant(56-85)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: Build NH on Darwin
- GitHub Check: Build NH on Linux
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: treewide-checks
- GitHub Check: Test NH on Linux
- GitHub Check: Build NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: Build NH on Darwin
- GitHub Check: treewide-checks
🔇 Additional comments (11)
src/remote.rs (11)
1-49: LGTM: Robust SSH control directory initialization.The fallback chain (XDG_RUNTIME_DIR → /tmp/nh-ssh- → /tmp) ensures graceful degradation, and the process-specific suffix prevents socket conflicts between concurrent invocations.
51-139: LGTM: Comprehensive host parsing with helpful validation.The parsing handles multiple URI formats correctly, strips schemes appropriately, and provides clear error messages that guide users toward proper configuration (e.g., directing port specification to NIX_SSHOPTS).
141-181: LGTM: Effective pre-flight connectivity check.The 10-second timeout and BatchMode ensure this check fails fast on unreachable hosts without blocking on interactive prompts. The detailed error message helps users diagnose connection issues.
207-243: LGTM: Proper SSH options integration.The distinction between
get_ssh_opts(parsed withshlexfor direct SSH invocation) andget_nix_sshopts_env(whitespace-joined fornix-copy-closure) correctly handles the different parsing behaviors of these tools. The comment on Line 227-230 appropriately documents thenix-copy-closurelimitation.
245-310: LGTM: Correct flake flag handling and remote execution.The Nix variant detection properly accounts for Determinate Nix's stable feature set. The remote command execution correctly quotes arguments for shell interpretation on the remote host.
312-418: LGTM: Comprehensive closure copying with correct tool selection.The implementation appropriately uses
nix-copy-closurefor localhost ↔ remote transfers andnix copyfor remote-to-remote transfers. Substitute flags are correctly mapped to each tool's syntax.
420-495: LGTM: Correct derivation path evaluation.The installable type handling properly appends
.drvPathfor Flake, File, and Expression types, while correctly rejecting Store paths (which are already built) with a clear error message.
497-538: LGTM: Well-structured configuration and pre-flight checks.The
RemoteBuildConfigstructure clearly encapsulates all remote build parameters, andcheck_remote_connectivityprovides valuable early feedback before initiating expensive operations.
612-671: LGTM with improved UTF-8 error handling.The build execution correctly differentiates between simple and nom-based paths. The UTF-8 conversion (lines 645-656) now fails explicitly rather than silently dropping non-UTF-8 arguments, which is a clear improvement over the previous version.
The error message at line 651 could be slightly more helpful by guiding users:
- .ok_or_else(|| eyre!("Extra argument is not valid UTF-8: {:?}", s)) + .ok_or_else(|| eyre!("Extra argument is not valid UTF-8: {:?}. Remote builds require UTF-8 arguments.", s))
774-1125: LGTM: Comprehensive test coverage with property-based testing.The test suite combines property-based tests (for hostname extraction invariants), unit tests (for parsing and options), and roundtrip tests (for shell quoting). The use of
#[serial]correctly prevents test interference when manipulating environment variables.
183-205: Useshlex::try_quotefor shell quoting, fallback is insufficient.The code correctly uses
shlex::try_quote, but the error handling should be reconsidered. The only error that can be returned is QuoteError::Nul, which occurs when the input contains nul bytes. The fallback using basic quote escaping doesn't address nul bytes and will still produce unsafe output if they occur. Since SSH arguments rarely contain nul bytes, either handle the error explicitly (e.g., reject nul-containing inputs) or document why the fallback is acceptable for this context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/remote.rs (1)
636-642: Add nom availability check before using nix-output-monitor.When
use_nomis enabled, the code directly callsbuild_on_remote_with_nomwithout verifying thatnomis installed. Ifnomis missing, the error "Remote build with nom failed" masks the actual cause (tool not found).The codebase already uses the
whichcrate for tool availability checks (seesrc/commands.rs:16). Apply the same pattern here to provide clear diagnostics.Suggested fix:
Add a pre-flight check before the conditional at line 636:
fn build_on_remote( host: &RemoteHost, drv_path: &str, config: &RemoteBuildConfig, ) -> Result<String> { let drv_with_outputs = format!("{drv_path}^*"); if config.use_nom { + // Check that nom is available before attempting to use it + which::which("nom") + .wrap_err("nom (nix-output-monitor) is required but not found in PATH")?; // With nom: pipe through nix-output-monitor build_on_remote_with_nom(host, &drv_with_outputs, config) } else { // Without nom: simple remote execution build_on_remote_simple(host, &drv_with_outputs, config) } }Also add the import:
use which::which;at the top ofsrc/remote.rs.
🧹 Nitpick comments (1)
src/remote.rs (1)
614-621: Consider logging when out-link removal fails.Line 618 silently ignores errors when removing an existing out-link with
let _ = std::fs::remove_file(link). While this is often acceptable (the path might not exist, or might be a dangling symlink), it could mask permission issues or cases where the path is a directory.💡 Optional improvement
Consider adding a debug log when removal fails for non-ENOENT errors:
// Create local out-link if requested if let Some(link) = out_link { debug!("Creating out-link: {} -> {}", link.display(), out_path); // Remove existing symlink/file if present - let _ = std::fs::remove_file(link); + if let Err(e) = std::fs::remove_file(link) { + if e.kind() != std::io::ErrorKind::NotFound { + debug!("Failed to remove existing out-link: {}", e); + } + } std::os::unix::fs::symlink(&out_path, link) .wrap_err("Failed to create out-link")?; }This provides diagnostics without changing behavior for the common case (link doesn't exist).
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/remote.rs
🧰 Additional context used
🧬 Code graph analysis (1)
src/remote.rs (1)
src/util.rs (1)
get_nix_variant(56-85)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: treewide-checks
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: Build NH on Linux
- GitHub Check: Build NH on Darwin
- GitHub Check: treewide-checks
- GitHub Check: Build NH on Darwin
- GitHub Check: Build NH on Linux
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (2)
src/home.rs (1)
106-123: Extract duplicatedRemoteBuildConfigconstruction logic.The
extra_argschaining pattern (lines 111-123) is duplicated identically insrc/darwin.rs(lines 124-136) andsrc/nixos.rs(lines 383-395). Consider extracting this to a helper function or builder pattern in theremotemodule as suggested in the darwin.rs review.src/nixos.rs (1)
378-396: Extract duplicatedRemoteBuildConfigconstruction logic.The
extra_argschaining pattern (lines 383-395) is duplicated identically insrc/darwin.rs(lines 124-136) andsrc/home.rs(lines 111-123). Consider extracting this to a helper function or builder pattern in theremotemodule as suggested in the darwin.rs review.
🧹 Nitpick comments (1)
src/darwin.rs (1)
119-136: Extract duplicatedRemoteBuildConfigconstruction logic.The
extra_argschaining pattern (lines 124-136) is duplicated identically insrc/home.rs(lines 111-123) andsrc/nixos.rs(lines 383-395). This code constructs aRemoteBuildConfigby mergingextra_argsand passthrough-derived arguments.🔎 Proposed refactor
Consider extracting this logic to a helper function in the
remotemodule or a builder pattern:// In src/remote.rs impl RemoteBuildConfig { pub fn new( build_host: RemoteHost, target_host: Option<RemoteHost>, use_nom: bool, use_substitutes: bool, ) -> Self { Self { build_host, target_host, use_nom, use_substitutes, extra_args: Vec::new(), } } pub fn with_extra_args<I>(mut self, args: I) -> Self where I: IntoIterator, I::Item: Into<OsString>, { self.extra_args.extend(args.into_iter().map(Into::into)); self } }Then in darwin.rs, home.rs, and nixos.rs:
let config = RemoteBuildConfig::new( build_host, None, // or target_host for nixos !self.common.no_nom, self.common.passthrough.use_substitutes, ) .with_extra_args(&self.extra_args) .with_extra_args(self.common.passthrough.generate_passthrough_args());
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/darwin.rssrc/home.rssrc/nixos.rssrc/remote.rs
🧰 Additional context used
🧬 Code graph analysis (4)
src/darwin.rs (1)
src/remote.rs (5)
parse(174-214)config(682-690)config(731-739)init_ssh_control(95-98)build_remote(576-647)
src/home.rs (1)
src/remote.rs (5)
parse(174-214)config(682-690)config(731-739)init_ssh_control(95-98)build_remote(576-647)
src/nixos.rs (1)
src/remote.rs (5)
parse(174-214)config(682-690)config(731-739)init_ssh_control(95-98)build_remote(576-647)
src/remote.rs (1)
src/util.rs (1)
get_nix_variant(56-85)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: Test NH on Linux
- GitHub Check: Build NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: Build NH on Darwin
- GitHub Check: treewide-checks
- GitHub Check: Build NH on Linux
- GitHub Check: Build NH on Darwin
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: treewide-checks
🔇 Additional comments (6)
src/nixos.rs (2)
188-207: LGTM: Conditional copy prevents errors with remote builds.The guard
if out_path.exists()(line 191) appropriately prevents attempting to copy to the target host when the output is still remote (hasn't been materialized locally yet). This optimization avoids spurious copy operations and potential errors in the remote build flow.
440-456: LGTM: Conditional existence check handles remote builds correctly.The conditional check
if out_path.exists()(line 442) appropriately skips local existence verification when the output is on a remote host. This prevents false negatives in the remote build flow and includes helpful debug logging.src/remote.rs (4)
12-89: LGTM: SSH control cleanup properly implemented with RAII.The
SshControlGuardand cleanup logic correctly use the RAII pattern to ensure SSH ControlMaster sockets are cleaned up when remote operations complete. The error handling with debug logging is appropriate, and the use ofssh -O exitmatches standard SSH ControlMaster cleanup practices.
174-214: LGTM: RemoteHost parsing includes comprehensive validation.The
parsemethod provides thorough validation with clear, actionable error messages. The checks for empty usernames, invalid characters, and helpful guidance (e.g., suggestingNIX_SSHOPTSfor port configuration) ensure users receive good diagnostics for malformed host specifications.
245-251: LGTM: Shell quoting now uses theshlexcrate.The
shell_quotefunction correctly usesshlex::try_quotewith an appropriate fallback for edge cases. This addresses the previous FIXME and uses a well-tested library for shell quoting.
576-646: LGTM: build_remote orchestration includes smart copy optimizations.The
build_remotefunction implements sophisticated copy-path optimizations to avoid unnecessary transfers (lines 596-635). The hostname comparison logic correctly identifies whenbuild_hostandtarget_hostare the same machine, and the three-way conditional logic (copy to target, copy to local, or skip) appropriately minimizes data movement while ensuring the closure is available where needed.
b274b50 to
2e15d6a
Compare
Fixes #428 This is a large architectural change to NH, which lead to me extracting the remote build logic to its own file so that we may implement it for Darwin and Home-Manager as well. The `--builders` flag was dropped from `nh::commands`, and it was replaced with the new and shiny logic that hopefully avoids previous pitfalls. The new `nh::remote` module handles remote builds, including: - Parsing remote host specifications. - Copying derivations to remote hosts using `nix-copy-closure`. - Building derivations on remote hosts via `nix build`. - Copying results back to localhost or directly to a target host. Signed-off-by: NotAShelf <[email protected]> Change-Id: I236eb1e35dd645f2169462d207bc82e76a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: I946d8e54261e9136c83f6dfe38b046106a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: I0b82e84223c3df61cfa23464bd3d4bcc6a6a6964
Fixes a minor issue in how commands that are invalid or improperly handled are forwarded to the Nix command. Replaces `join()` with `popen()` to access individual processes in the pipeline. This way we can better check the exist status of the `nix build` process and properly propagate them. Also improves naming a little bit because why not? Signed-off-by: NotAShelf <[email protected]> Change-Id: I8a44abf924f9c9a1c06d102e5a3f40aa6a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: Ia8506cad3352243001a281e99b8162c26a6a6964
Tiny improvement to how remote connections are made. We now check BEFORE the connection is made, so that we can avoid all that expensive eval if it's not reachable. This is not infallible, but it is better. To fix some target-host quirks, we also have to deal with local symlinks so we enforce it locally either way. Signed-off-by: NotAShelf <[email protected]> Change-Id: I65fd7258828459ea82fe6739383567556a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: I2366fac6ca7a72fc73eecfc0b07bd2d76a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: If5d2072431348a8468150abf15a7a2a06a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: I9eb3904e832e58e0f4ac306d537f7dee6a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: Idf201a23f71795e0caea9813280084036a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: I73a94927d1270b4a499bb22b8220a1326a6a6964
Remove the redundant and poor connectivity checks that added overhead without any tangible benefit, and implement SSH ControlMaster cleanup on program exit. This reduces the number of SSH connections made during remote operations and makes sure SSH control processes are properly terminated Signed-off-by: NotAShelf <[email protected]> Change-Id: Ideb1825cb7e8302316d7d25b64e7859b6a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: Ia782562b87e2614a390d8f435114142b6a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: I296043d85fd74fc68013dc9f1f3761ea6a6a6964
Here's to you, Dami. Signed-off-by: NotAShelf <[email protected]> Change-Id: I49e84a3efab65791800348c92b1fc5da6a6a6964
I don't like NixOS' remote builds. Signed-off-by: NotAShelf <[email protected]> Change-Id: Iea646b3b47926536a1bb1a70e3d776fa6a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: Iffe2d63b55ee4a9bab41bb6184184add6a6a6964
b573386 to
ce1abee
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
CHANGELOG.md (1)
61-62: Remove duplicate content.Lines 61-62 duplicate the information already provided on lines 47-48 about
nh os infohiding empty fields by default. One of these should be removed to avoid redundancy.
♻️ Duplicate comments (1)
CHANGELOG.md (1)
39-39: Grammar: sentence missing subject.This issue was already flagged in previous reviews. The sentence "Can also be set via the
NH_NO_VALIDATEenvironment variable." is missing a subject.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
CHANGELOG.mdsrc/commands.rssrc/interface.rssrc/main.rssrc/nixos.rssrc/remote.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- src/interface.rs
🧰 Additional context used
🧬 Code graph analysis (3)
src/main.rs (1)
src/commands.rs (3)
args(310-319)from_str(129-135)arg(303-306)
src/nixos.rs (2)
src/remote.rs (5)
parse(386-451)copy_to_remote(993-1023)build_remote(1494-1594)essential_files(834-846)validate_closure_remote(827-924)src/util.rs (1)
ensure_ssh_key_login(201-219)
src/remote.rs (2)
src/commands.rs (2)
cache_password(64-73)get_cached_password(40-46)src/util.rs (1)
get_nix_variant(56-85)
🪛 LanguageTool
CHANGELOG.md
[style] ~39-~39: To form a complete sentence, be sure to include a subject.
Context: ...ctivation system validation checks. Can also be set via the NH_NO_VALIDATE en...
(MISSING_IT_THERE)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Build NH on Linux
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Linux
- GitHub Check: Build NH on Linux
- GitHub Check: Build NH on Darwin
🔇 Additional comments (9)
src/main.rs (1)
33-48: Good backward compatibility implementation.The fallback to
NH_ELEVATION_PROGRAMwith a deprecation warning provides a smooth migration path. The use of.ok()on line 46 is safe sinceElevationStrategyArg::from_strreturnsInfallible(line 127 in src/commands.rs).src/nixos.rs (1)
229-243: Verify remote build detection logic.Line 229 sets
is_remote_build = self.rebuild.target_host.is_some(), but a remote build can also be triggered when onlybuild_hostis set (withouttarget_host). This may lead to incorrect path resolution logic.Consider checking both:
let is_remote_build = self.rebuild.build_host.is_some() || self.rebuild.target_host.is_some();Or if the intent is to detect remote activation specifically, the current logic may be correct but the variable name is misleading.
src/commands.rs (2)
630-632: Good security improvement: empty password validation.Adding validation to reject empty passwords prevents invalid credential caching and potential authentication bypasses. This is especially important for remote operations where cached passwords are reused.
837-881: Proper handling of nom pipeline exit status.The code correctly uses
popen()to access individual process exit codes and checks the first process (nix build) rather than the last (nom). This ensures build failures are properly detected even when nom always exits successfully.src/remote.rs (5)
157-174: Safe interrupt handler registration with idempotency.The use of
OnceLockto ensure single registration andsignal_hook::flag::register(which is async-signal-safe) is the correct approach. The benign race condition comment on lines 169-170 correctly notes that duplicate registration is harmless since both handlers would set the same atomic flag.
630-683: Remote cleanup implementation with appropriate safeguards.The opt-in nature (
NH_REMOTE_CLEANUP), 5-second timeout, and graceful error handling (lines 632-682) make this fragile operation safe to attempt. The stderr parsing for common error patterns (lines 661-679) provides good diagnostics while avoiding false negatives.
827-924: Efficient batched validation with good error messages.The batched SSH checks (lines 834-861) using
test -ejoined with&&minimize SSH round-trips. When validation fails, individual checks (lines 872-894) identify exactly which files are missing. The context-aware error messages (lines 900-920) provide actionable guidance.
1678-1696: Proper interrupt handling during remote execution.The polling loop with 100ms intervals (lines 1678-1696) allows timely interrupt detection while waiting for SSH completion. Killing the local SSH process (line 1686) and attempting remote cleanup (line 1690) before bailing provides good cleanup behavior.
1783-1823: Careful interrupt handling in nom pipeline.The nested polling loop (lines 1791-1822) checks the interrupt flag before waiting on each process, ensuring responsive Ctrl+C handling. The cleanup sequence (kill all processes, wait to reap zombies, attempt remote cleanup) on lines 1797-1803 prevents resource leaks.
This is a massive change, and it primarily concerns `--elevation-*program*` not making any sense for remote operations. The change to *strategy* allows the flag to be more descriptive in terms of how it operates, and allows us to add new strategies to handle. Fixes #434 Signed-off-by: NotAShelf <[email protected]> Change-Id: I9e51838007feca7d2d914402d7f11dbc6a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: I8837714ede1884ef22c9c0f0c10016746a6a6964
d637bda to
1c44bc4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
CHANGELOG.md (1)
21-49: Tighten grammar and remove duplicatednh os infoentry.Two minor doc nits here:
- Line 39 is a sentence fragment: “Can also be set via the
NH_NO_VALIDATEenvironment variable.” It should have an explicit subject, e.g. “This flag can also be set via theNH_NO_VALIDATEenvironment variable.”- The bullets at 45–48 already describe that
nh os infohides empty columns by default. Lines 61–62 later repeat the same idea (“now hides empty fields by default…”), so that second mention can be dropped to avoid duplication.Suggested edits:
- A new `--no-validate` flag skips pre-activation system validation checks. - This flag can also be set via the `NH_NO_VALIDATE` environment variable. … - `nh os info` now supports `--fields` to select which field(s) to display ([#375](...)). - Empty columns are now hidden by default to avoid visual clutter. - A new, per-generation "Closure Size" column has been added … - `nh os info` now hides empty fields by default, they can be explicitly shown via the `--fields` flag. [DELETE this duplicated bullet]
🧹 Nitpick comments (3)
src/remote.rs (1)
1596-1865: Robust interrupt handling and optional remote cleanup; consider documenting NH_REMOTE_CLEANUP’s trade-offs.The
build_on_remote_simple/build_on_remote_with_nompaths plusregister_interrupt_handlerandattempt_remote_cleanupgive you:
- Periodic
wait_timeoutpolling with a sharedAtomicBoolset by SIGINT, so local SSH processes are killed promptly on Ctrl+C.- Optional best-effort remote cleanup via
NH_REMOTE_CLEANUP, sendingpkill -INT --full '<remote_cmd>'with a short timeout and diagnostic logging.This is a solid approach for mitigating “stuck waiting for lock” situations on the remote side, without risking hangs in the client. I’d suggest:
- Clearly documenting in user-facing docs (remote-build guide / manpage) that
NH_REMOTE_CLEANUPis opt‑in, what it does, and in which situations it might or might not succeed.- Encouraging users who reported lock hangs to try with
NH_REMOTE_CLEANUP=trueand see if the remote nix process is reliably terminated.No functional issues spotted here; just a note that better documentation will help users understand the new behavior and knobs.
src/nixos.rs (2)
211-224: Avoid redundantnix copywhen using remote builds with--build-hostand--target-host.In
activate_rebuilt_config, the block:if let Some(target_host) = &self.rebuild.target_host { if out_path.exists() { let target = RemoteHost::parse(target_host)?; remote::copy_to_remote(&target, target_profile, self.rebuild.common.passthrough.use_substitutes)?; } }runs regardless of whether the build was local or remote. For remote builds (
self.build_host.is_some()),remote::build_remotealready:
- Copies the result to
target_hostdirectly (copy_closure_between_remotes) or- Falls back to
build_host -> localhost -> target_host.Running an additional
nix copyfrom localhost to the sametarget_hostis redundant in the success case (it should be a no-op but still costs a round-trip).Consider restricting this copy to the “local build + remote activation” case, e.g.:
if self.rebuild.build_host.is_none() { // local build → remote target: we must push the closure now if out_path.exists() { let target = RemoteHost::parse(target_host)?; remote::copy_to_remote(&target, target_profile, self.rebuild.common.passthrough.use_substitutes)?; } }This keeps semantics the same while avoiding unnecessary extra copies for remote builds.
1070-1091:has_elevation_statusbehavior withElevationStrategy::Noneis intentional but worth noting.The updated
has_elevation_status:
- Immediately returns
Ok(false)whenelevationisElevationStrategy::None, bypassing both the “don’t run as root” guard and any attempt to escalate.- Otherwise, retains the previous behavior: if
bypass_root_checkis false and the effective UID is root, it errors out; ifbypass_root_checkis true, it logs a warning and proceeds without elevation.This means
--elevation-strategy nonefully opts the user out of both privilege escalation and the “don’t run as root” safety check. That’s consistent with an explicit “no elevation” choice, but you may want to document it clearly in the help text / manpage so users understand thatnonedisables this guard as well.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
CHANGELOG.mdsrc/commands.rssrc/interface.rssrc/main.rssrc/nixos.rssrc/remote.rs
🧰 Additional context used
🧬 Code graph analysis (3)
src/interface.rs (1)
src/commands.rs (1)
arg(309-312)
src/nixos.rs (1)
src/remote.rs (5)
copy_to_remote(993-1023)activate_remote(1151-1163)build_remote(1494-1594)essential_files(834-846)validate_closure_remote(827-924)
src/remote.rs (2)
src/commands.rs (5)
cache_password(64-73)get_cached_password(40-46)new(273-284)new(781-788)new(918-927)src/util.rs (1)
get_nix_variant(56-85)
🪛 LanguageTool
CHANGELOG.md
[style] ~39-~39: To form a complete sentence, be sure to include a subject.
Context: ...ctivation system validation checks. Can also be set via the NH_NO_VALIDATE en...
(MISSING_IT_THERE)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Build NH on Linux
- GitHub Check: Test NH on Linux
- GitHub Check: Build NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: Test NH on Linux
🔇 Additional comments (5)
src/remote.rs (1)
1440-1594: Remote build semantics forbuild_host/target_hostlook correct; verify they resolve the reported issues.The
RemoteBuildConfig+build_remoteflow now:
- Evaluates the drvPath locally, copies it to
build_host, builds onbuild_host, and computesout_path(<drv>^*).- Uses
hostname()comparison so whenbuild_hostandtarget_hostrefer to the same machine, it skips thecopy_closure_between_remoteshop and also skips copying the result back to localhost (vianeed_local_copy = false), which directly addresses the earlier “same host but still copied locally” complaint.- Honors
use_substitutesconsistently across:
copy_closure_to/copy_closure_from(nix-copy-closure --use-substitutes),copy_to_remote/copy_closure_between_remotes(nix copy --substitute-on-destination),- plus the
--use-substitutesflag passed into the remotenix buildviaextra_args.Given the subtleties involved, I recommend manually validating in real environments that:
--use-substitutesbehaves as expected for:
- local build + remote activation,
- remote build with
--build-hostonly,- remote build with both
--build-hostand--target-host.- When
build_hostandtarget_hostare identical, the build result is not unnecessarily copied back to the local machine (you should only see SSH traffic for the drv copy + the remote build itself).If you’d like, I can sketch a small matrix of
build_host/target_host/out_linkcombinations and the expected copy graph to use as a test checklist.src/interface.rs (2)
54-70: CLI elevation strategy wiring looks consistent with the new model.The
elevation_strategyoption is correctly exposed as a global clap flag, wired toNH_ELEVATION_STRATEGYwith anelevation-programalias, and typed asElevationStrategyArg, which main.rs maps into the runtimeElevationStrategy. Aside from theprogram:<path>parsing detail called out in other comments, this surface looks coherent and compatible with the new elevation API.
249-260: Remote-related rebuild flags (build_host,target_host,no_validate) are well-scoped.The new
target_host,build_host, andno_validatefields onOsRebuildArgsare cleanly introduced:
build_host/target_hostare documented as SSH-based remote build/activation knobs.no_validateis global for OS rebuilds and bound toNH_NO_VALIDATE, matching the behavior innixos.rswhere pre-activation closure validation can be skipped.The same
build_hostflag onHomeRebuildArgsandDarwinRebuildArgskeeps the CLI consistent across subcommands. No structural issues spotted here.src/nixos.rs (2)
226-283: Closure validation and switch-to-configuration path resolution handle local vs remote correctly.The new logic around
resolved_profileandswitch_to_configuration:
- Uses
actual_store_pathfromexecute_buildwhen available (remote builds), avoiding any localcanonicalize()on paths that may not exist, which fixes the earlier remote-only canonicalization failure.- Falls back to canonicalizing
target_profileonly for local builds, giving a stable store path for validation and activation.- Gates validation on
no_validateand dispatches correctly:
validate_system_closure_remotefor remote activations, passing the store path and both target/build host info for good error messages.validate_system_closurefor purely local activations.- Resolves
switch-to-configurationdirectly under the resolved store path and only canonicalizes it when we actually expect a local file, skipping canonicalization entirely for remote-only builds whereout_pathdoes not exist.This wiring looks sound and should prevent the remote-activation regressions seen previously, while preserving the stricter local validation path.
931-1013: Local and remote system-closure validation helpers are clear and minimal.
validate_system_closureandvalidate_system_closure_remotedefine a small, well-scoped set of essential files and:
- For local builds, check existence of
bin/switch-to-configuration,nixos-version,init, andsw/binunder the resolved system path, returning a detailed, action-oriented error if any are missing.- For remote builds, delegate to
remote::validate_closure_remote, which batches SSH checks using the shared SSH options and provides context about where the system was built (same host vs different build_host).The split between “simple local check” and “remote delegation” keeps nixos.rs focused on OS semantics while centralizing SSH details in
remote.rs. No issues spotted here.
nix: add sudo to `nativeCheckInputs`
faukah
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been using it a fair bit the last few days, it seems to just work now. Great job on this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/commands.rs (1)
520-521: Optional: Simplify map_or patternThe pattern
.map_or(None, |value| Some(...))can be simplified to.ok().map(|value| ...)for better readability:- std::env::var(key) - .map_or(None, |value| Some(format!("{key}={value}"))) + std::env::var(key) + .ok() + .map(|value| format!("{key}={value}"))Also applies to: 564-565
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/commands.rs
🧰 Additional context used
🧬 Code graph analysis (1)
src/commands.rs (1)
src/checks.rs (1)
new(348-357)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: Build NH on Linux
- GitHub Check: treewide-checks
- GitHub Check: Test NH on Darwin
- GitHub Check: Test NH on Linux
- GitHub Check: treewide-checks
- GitHub Check: Build NH on Darwin
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: Build NH on Linux
🔇 Additional comments (9)
src/commands.rs (9)
25-73: LGTM: Password cache API improvementsThe refactoring to Result-based error handling for poisoned locks is correct, and the comprehensive documentation enhances clarity.
131-148: LGTM: ElevationStrategyArg parsing correctly handles prefixThe
FromStrimplementation properly strips theprogram:prefix and provides sensible fallback behavior for raw paths.
180-221: LGTM: Elevation strategy resolution is well-implementedThe
resolve()method handles all variants appropriately with clear error messages. The shared resolution logic forAutoandPasswordlessis correct since both need to find an elevation program.
411-417: LGTM: macOS HOME handling aligns with NixSetting
HOMEto an empty string for elevated commands on macOS correctly matches Nix's behavior per the referenced source.
477-504: LGTM: Passwordless variant correctly skips askpassThe logic appropriately excludes the askpass mechanism for
Passwordlesselevation, enabling NOPASSWD sudo configurations as intended.
627-651: LGTM: Password handling improved with empty checkThe addition of empty password validation and proper Result-based error propagation enhances robustness. The comment appropriately notes sudo-specific stdin password support.
849-896: LGTM: nom pipeline exit status handling correctedThe refactoring properly checks the
nix buildexit status rather thannom's, addressing the issue from previous reviews. Usingpopen()to access individual processes is the correct approach.
1237-1272: LGTM: Comprehensive test coverage for new elevation featuresThe new tests effectively cover
Passwordlessresolution,program:prefix parsing, and sudo command construction behavior.
1578-1644: LGTM: Thorough shlex parsing test coverageThe extensive test suite covers edge cases including escaped quotes, nested quotes, backslashes, environment variables, and error conditions. Using
shlexis a solid choice over custom parsing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
package.nix (1)
82-96: The Darwin test exclusion logic is sound; consider consolidating skip flags using substring matching.The conditional logic correctly excludes sudo from Darwin builds and skips the relevant tests. The eight individual
--skipentries could be consolidated since Cargo's--skipflag performs substring matching.Consolidate skip flags for better maintainability
All skipped tests share the substring
test_build_sudo_cmd, so you can replace the eight individual skip flags with a single one:- checkFlags = lib.optionals stdenv.hostPlatform.isDarwin [ - # Tests that require sudo in PATH (not available on Darwin) - "--skip=commands::tests::test_build_sudo_cmd_basic" - "--skip=commands::tests::test_build_sudo_cmd_with_preserve_vars" - "--skip=commands::tests::test_build_sudo_cmd_with_preserve_vars_disabled" - "--skip=commands::tests::test_build_sudo_cmd_with_set_vars" - "--skip=commands::tests::test_build_sudo_cmd_passwordless_no_stdin" - "--skip=commands::tests::test_build_sudo_cmd_with_remove_vars" - "--skip=commands::tests::test_build_sudo_cmd_with_askpass" - "--skip=commands::tests::test_build_sudo_cmd_env_added_once" - ]; + checkFlags = lib.optionals stdenv.hostPlatform.isDarwin [ + # Tests that require sudo in PATH (not available on Darwin) + "--skip=test_build_sudo_cmd" + ];This approach is more maintainable and will automatically cover any new sudo-related tests that follow the same naming convention.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
package.nix
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: Build NH on Linux
- GitHub Check: Build NH on Darwin
- GitHub Check: treewide-checks
fee8fc0 to
d747315
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI Agents
In @src/commands.rs:
- Around line 1262-1273: The test named
test_build_sudo_cmd_passwordless_no_stdin is misleading because it constructs
Command::new("test").elevate(Some(ElevationStrategy::Force("sudo"))) but claims
to test Passwordless; update the test to either rename it (e.g.,
test_build_sudo_cmd_force_no_stdin) to reflect that it uses
ElevationStrategy::Force, or change the elevation variant to
ElevationStrategy::Passwordless("sudo") so the test actually validates the
Passwordless behavior; update any inline comment to match the chosen change and
keep the assertion using cmd.build_sudo_cmd() and sudo_exec.to_cmdline_lossy()
as-is.
🧹 Nitpick comments (2)
package.nix (1)
84-97: Platform-specific test handling looks correct, but note the FIXME.The conditional inclusion of
sudoinnativeCheckInputsfor non-Darwin platforms and the corresponding test skips for Darwin are appropriate given sudo's unavailability on that platform.However, the FIXME comment on line 86 highlights a maintainability concern: the
--separator incheckFlagsis nextest-specific, butuseNextestis currently hardcoded totrue(line 102). If someone later makesuseNextestconfigurable or changes its default, they must remember to makecheckFlagsconditional as well, or tests will fail on Darwin.Optional: Make the nextest separator conditional
If you'd like to make the code more maintainable, consider making the
--separator conditional:- checkFlags = lib.optionals stdenv.hostPlatform.isDarwin [ - "--" # required for nextest. FIXME: make optional based on useNextest + checkFlags = + lib.optionals stdenv.hostPlatform.isDarwin ( + lib.optional finalAttrs.useNextest "--" + ++ [ + # Tests that require sudo in PATH (not available on Darwin) + "--skip=commands::tests::test_build_sudo_cmd_basic" + # ... rest of skips + ] + );This would eliminate the coupling between
useNextestandcheckFlags.src/commands.rs (1)
564-566: Consider simplifying the environment variable mapping.The pattern
.map_or(None, |value| Some(...))is equivalent to.ok().map(...)and could be simplified for clarity:🔎 Proposed simplification
EnvAction::Preserve if preserve_env => { - std::env::var(key) - .map_or(None, |value| Some(format!("{key}={value}"))) + std::env::var(key) + .ok() + .map(|value| format!("{key}={value}")) },
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
package.nixsrc/commands.rs
🔇 Additional comments (12)
package.nix (2)
8-8: LGTM: sudo parameter additionThe
sudoparameter is properly declared and correctly wired into the derivation's test infrastructure.
13-80: Formatting improvements enhance readability.The formatting adjustments throughout the file (indentation, alignment, comment clarity) improve maintainability without changing semantics.
Also applies to: 98-118
src/commands.rs (10)
40-73: LGTM - password caching with proper error handling.The Result-based API correctly handles lock poisoning and uses
SecretStringfor secure memory handling. The lifetime behavior (passwords persist for program duration) is clearly documented.
131-148: LGTM - FromStr implementation correctly handlesprogram:prefix.The parsing logic properly strips the
program:prefix and falls back to treating unknown strings as program paths. This matches the documented behavior and addresses the previous review feedback.
199-221: LGTM - elevation strategy resolution logic is clear.The resolution logic appropriately handles each variant with helpful error messages. The
Passwordlessvariant correctly resolves to an elevation program (identical toAuto) since the difference is in password handling during execution, not program selection.
370-450: LGTM - comprehensive environment variable handling.The method properly handles platform-specific requirements (macOS elevated HOME), distinguishes between elevated and non-elevated contexts, and preserves necessary Nix and NH environment variables. The debug logging aids troubleshooting.
477-531: LGTM - elevation command construction handles Passwordless variant correctly.The method correctly skips askpass configuration for the
Passwordlessvariant (line 499), which is appropriate for NOPASSWD sudoers configurations. Environment handling respects theNH_PRESERVE_ENVsetting.
627-750: LGTM - password prompting and caching logic is sound.The method correctly:
- Checks for cached passwords before prompting
- Validates that passwords are non-empty (preventing silent failures)
- Handles Result returns from password cache operations
- Distinguishes between local and remote elevation contexts
850-894: LGTM - nom pipeline correctly checks Nix's exit status.The reworked implementation properly uses
popen()to access individual process handles and checks the first process (nix build) exit status rather than the last (nom). This ensures build failures are detected even when nom succeeds. The comments clearly explain the reasoning.
1579-1645: LGTM - comprehensive test coverage for shlex integration.The new tests thoroughly validate the switch from custom parsing to the
shlexlibrary, covering edge cases including escaped quotes, nested quotes, backslash escapes, unclosed quotes, and realistic command scenarios. This provides confidence in the parsing behavior.
1163-1174: LGTM - platform-agnostic test assertion.The updated test correctly handles platform differences where
sudomight be a full path or just the program name. The comment clearly explains the reasoning for the flexible assertion.
906-911: LGTM - appropriate lint exceptions for test code.The clippy allow attributes are properly scoped to the test module with clear reasoning. Using
expect_used,unwrap_used, andunreachablein tests is standard practice.
The program prefix was advertised as valid, but it actually wasn't because I regressed it during a different refactor. Strip the "program:" prefix when present to correctly parse elevation paths, and add a regression test so that I don't mess it up again. The deprecation warning advertised "program:<path>" as a valid value but the parser treated it as a literal path. Strip the "program:" prefix when present to correctly parse elevation program paths. Add unit test for this case. Signed-off-by: NotAShelf <[email protected]> Change-Id: I33de2df17654f95a656a4ee649cf7c9b6a6a6964
d747315 to
393d808
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/commands.rs (1)
850-897: Verify empty pipeline handling.The nom pipeline implementation correctly checks the first process's exit status rather than nom's. However, consider what happens if
processes.first()returnsNone(empty pipeline):if let Some(nix_proc) = processes.first() { // checks exit status } // Falls through without error if processes is emptyIf the pipeline is empty, the function returns
Ok(())without building anything. While this is unlikely in practice, you may want to add an explicit check.🔎 Suggested defensive check
let mut processes = pipeline.popen()?; +if processes.is_empty() { + bail!("Empty pipeline - no processes to execute"); +} + // Wait for all processes to finish for proc in &mut processes { proc.wait()?; }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
package.nixsrc/commands.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- package.nix
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: Test NH on Darwin
- GitHub Check: Test NH on Linux
- GitHub Check: Build NH on Linux
- GitHub Check: Build NH on Darwin
- GitHub Check: treewide-checks
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: Build NH on Darwin
- GitHub Check: Build NH on Linux
- GitHub Check: treewide-checks
🔇 Additional comments (9)
src/commands.rs (9)
25-73: LGTM!The password cache functions now properly return
Resulttypes to handle potential lock poisoning. The error handling is clear and the documentation is comprehensive.
109-148: LGTM!The
ElevationStrategyArgenum and itsFromStrimplementation correctly handle theprogram:prefix syntax as addressed in previous review feedback. The fallback to treating bare strings as program paths provides good backward compatibility.
150-221: LGTM!The
ElevationStrategyenum updates andresolve()method are well-structured. ThePasswordlessvariant correctly resolves to an elevation program (same asAuto) while deferring password-prompting logic to the command building phase.
478-531: LGTM!The
build_sudo_cmdmethod correctly handles thePasswordlesselevation variant by skipping the askpass configuration (line 499). This properly supports remote hosts with NOPASSWD sudo configurations as intended by the PR objectives.
628-652: Good addition of empty password validation.The password prompting logic correctly handles the new
Result-returning cache functions. The empty password check at lines 643-645 is a good defensive measure that prevents caching invalid passwords.
1238-1272: LGTM!The new tests provide good coverage for:
Passwordlessvariant resolution (lines 1238-1247)program:prefix parsing (lines 1250-1259)- Force elevation behavior (lines 1262-1272)
These tests validate the key functionality added in this PR.
1429-1644: Excellent shlex test coverage.The cmdline parsing tests have been properly updated to use
shlex::splitand comprehensive edge cases have been added:
- Escaped quotes (line 1579)
- Nested quotes (line 1587)
- Backslash escapes (line 1594)
- Nix store paths (line 1601)
- Environment variables in quotes (line 1615)
- Unclosed quotes (line 1623)
- Complex sudo commands (line 1630)
This provides robust validation of the shlex-based parsing behavior.
416-416: Minor style improvement.Using
String::new()instead of"".to_string()is slightly more idiomatic and explicit about creating an empty string.
520-523: Safer env var handling pattern.The change from direct
std::env::var(key)access to using.ok().map()provides better error handling for environment variable lookups. This prevents potential issues ifstd::env::varreturns an error for reasons other than the variable not being set.Also applies to: 565-567
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/nixos.rs (1)
479-545: Consider extending the SSH control guard's lifetime.The
_ssh_guardon line 525 is dropped whenexecute_buildreturns, which means subsequent SSH operations (validation, activation) won't benefit from connection multiplexing. For better performance, especially with high-latency connections, consider:
- Creating the guard in
rebuild_and_activateorsetup_build_contextand keeping it alive for the entire operation sequence, or- Returning the guard from
execute_buildso the caller can manage its lifetimeThis would allow validation and activation SSH calls to reuse the same control connection.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/nixos.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: treewide-checks
- GitHub Check: Build NH on Linux
- GitHub Check: Build NH on Darwin
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: Build NH on Linux
- GitHub Check: treewide-checks
- GitHub Check: Build NH on Darwin
- GitHub Check: Test NH on Darwin
- GitHub Check: Test NH on Linux
🔇 Additional comments (12)
src/nixos.rs (12)
1-29: LGTM!The added imports are appropriate for the remote build functionality and the type conversions in
execute_build.
36-44: LGTM!The essential files list is appropriate and aligns with nixos-rebuild-ng's validation approach. The descriptions will be useful for error messages.
142-198: LGTM!The refactored flow correctly distinguishes between remote and local builds by capturing the actual store path from remote builds via
execute_build, and passes it through to activation.
221-277: Path resolution and validation logic is well-structured.The three-way branching correctly handles:
- Remote builds with actual store path captured
- Remote builds without local result (defensive fallback)
- Local builds with canonicalized symlinks
The validation dispatches appropriately to remote or local checks based on the target host.
279-293: LGTM!The conditional canonicalization correctly addresses the issue where remote-only builds would fail on
canonicalize(). The fallback to uncanonicalized paths for SSH-executed commands is appropriate.
299-348: LGTM!The activation logic cleanly separates remote and local paths, with proper activation type mapping and comprehensive debug logging.
350-400: LGTM!Bootloader activation correctly handles both remote and local cases, with proper environment variable handling for
NIXOS_INSTALL_BOOTLOADER.
404-439: LGTM!The refactored
setup_build_contextcorrectly returnsResultand threads elevation through tohas_elevation_status. The SSH key login check is appropriately conditional on remote host usage.
547-588: LGTM!The conditional existence check correctly handles remote-only builds by skipping local validation when the output path doesn't exist locally.
941-979: LGTM!The validation function provides comprehensive error messages with actionable troubleshooting steps, and correctly checks all essential files before reporting failures.
981-1008: LGTM!The remote validation function provides useful context in error messages by indicating whether the target is also the build host. Clean delegation to the remote module maintains good separation of concerns.
1065-1086: LGTM!The refactored elevation check correctly respects
ElevationStrategy::Noneand maintains the existing root-user guard logic.
1b7b1cc to
a2cbed9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI Agents
In @package.nix:
- Around line 86-104: The checkFlags currently injects repeated "--skip" entries
which work for cargo test but not for nextest; update the checkFlags assignment
used when stdenv.hostPlatform.isDarwin to use nextest filter syntax instead of
"--skip": replace the array of "--skip" + test names with a two-element array
["-E", "not (test(test_build_sudo_cmd_basic) or
test(test_build_sudo_cmd_with_preserve_vars) or
test(test_build_sudo_cmd_with_preserve_vars_disabled) or
test(test_build_sudo_cmd_with_set_vars) or
test(test_build_sudo_cmd_force_no_stdin) or
test(test_build_sudo_cmd_with_remove_vars) or
test(test_build_sudo_cmd_with_askpass) or
test(test_build_sudo_cmd_env_added_once))"] so nextest interprets the excluded
tests correctly; keep the lib.optionals stdenv.hostPlatform.isDarwin wrapper and
ensure this branch is used when useNextest = true.
🧹 Nitpick comments (3)
src/commands.rs (2)
643-646: Reconsider empty password rejection for remote sudo scenarios.Line 643-646 rejects empty passwords, but some remote systems may have passwordless sudo configured (NOPASSWD in sudoers). In such cases, users should use
--elevation-strategy=passwordlessinstead, but the error message doesn't guide them to this solution.Consider adding a hint to the error message:
if password.is_empty() { - bail!("Password cannot be empty"); + bail!( + "Password cannot be empty. If the remote host has passwordless \ + sudo configured, use --elevation-strategy=passwordless instead." + ); }
520-523: Simplify the environment variable preservation pattern.Lines 520-523 use a verbose pattern that can be simplified for readability:
EnvAction::Preserve if preserve_env => { - std::env::var(key) - .ok() - .map(|value| format!("{key}={value}")) + std::env::var(key).ok().map(|value| format!("{key}={value}")) },The multiline chain adds visual noise without improving clarity.
src/nixos.rs (1)
240-253: Path resolution logic is comprehensive.The resolved_profile calculation properly handles three cases:
- Remote build with captured store path → use store path directly
- Remote build without local result and no store path → use target_profile as-is
- Local build → canonicalize to get store path
However, case 2 (lines 244-247) should be unreachable if execute_build always returns Some for remote builds. Consider adding an assertion or warning:
} else if is_remote_build && !out_path.exists() { - // Remote build with no local result and no store path captured - // (shouldn't happen, but fallback) + warn!("Remote build completed but no store path captured; using target_profile as fallback"); target_profile.to_path_buf() } else {
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
package.nixsrc/commands.rssrc/nixos.rs
🧰 Additional context used
🧬 Code graph analysis (1)
src/nixos.rs (2)
src/remote.rs (6)
parse(386-451)copy_to_remote(993-1023)activate_remote(1151-1163)init_ssh_control(259-262)build_remote(1494-1594)validate_closure_remote(827-924)src/util.rs (1)
ensure_ssh_key_login(201-219)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: treewide-checks
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: Build NH on Linux
- GitHub Check: Build NH on Darwin
- GitHub Check: Build NH on Darwin
- GitHub Check: Build NH on Linux
- GitHub Check: treewide-checks
- GitHub Check: Test NH on Darwin
- GitHub Check: Test NH on Linux
🔇 Additional comments (13)
src/commands.rs (5)
131-148: LGTM! Properprogram:prefix parsing implemented.The FromStr implementation correctly handles the documented
program:<path>syntax and falls back to treating bare strings as program paths. This addresses the previous review feedback.
199-221: LGTM! Elevation strategy resolution handles all variants correctly.The resolve() method properly handles all ElevationStrategy variants:
- Auto and Passwordless both resolve to an available elevation program
- Prefer falls back to auto-detection if the preferred program isn't found
- Force strictly requires the specified program
- None appropriately fails with a clear error message
850-897: LGTM! Correct handling of nom pipeline exit status.The implementation properly checks the exit status of the
nix buildcommand (first process) rather thannom(last process), which is critical since nom always succeeds even when nix fails. The comments clearly explain the issue and solution.Both the nom and non-nom paths correctly propagate exit status through ExitError.
1238-1272: LGTM! Tests cover new elevation strategy functionality.The new tests properly verify:
- Passwordless strategy resolves to an elevation program
program:prefix parsing extracts the path correctly- Force strategy behavior with sudo
These tests address the previous review feedback about test naming and coverage.
1578-1644: LGTM! Comprehensive shlex parsing test coverage.The new tests thoroughly exercise shlex parsing with realistic edge cases:
- Escaped and nested quotes
- Backslash escaping
- Nix store paths (common in the codebase)
- Environment variable syntax preservation
- Unclosed quote error handling
- Complex sudo command scenarios
This provides good confidence in the shlex integration.
src/nixos.rs (6)
36-44: LGTM! Well-defined essential files for validation.The ESSENTIAL_FILES constant provides a clear set of critical system files to validate, with helpful descriptions for error messages. This aligns with nixos-rebuild-ng's validation approach.
479-545: LGTM! Remote build integration is well-structured.The execute_build method properly distinguishes between remote and local builds:
- Remote builds return the actual store path for later use
- Local builds return None (store path is resolved via symlink)
- SSH control guard ensures connection cleanup
- Error messages provide clear context
The implementation aligns with the PR's goal of implementing proper remote-build semantics.
569-585: LGTM! Proper handling of remote-only build paths.The updated logic correctly handles remote-only builds by:
- Checking if out_path exists locally before validating target_profile
- Skipping local existence checks for remote builds
- Providing clear debug logging for troubleshooting
This addresses the previous review feedback about canonicalize() failures for remote paths.
954-1008: LGTM! Validation functions provide clear, actionable error messages.Both validation functions:
- Check essential system files systematically
- Provide detailed error messages listing missing files
- Explain common causes and solutions
- Handle local and remote scenarios appropriately
The remote validation delegates to
remote::validate_closure_remote, which properly uses SSH connection multiplexing (per the relevant code snippet).
299-391: LGTM! Remote and local activation paths are properly separated.The activation logic cleanly separates remote and local scenarios:
Remote activation:
- Delegates to
remote::activate_remotewith platform, activation type, and elevation- Properly handles Test, Switch, and Boot operations
- Threads elevation strategy through to remote execution
Local activation:
- Uses existing Command::new pattern with elevation
- Preserves required environment variables
- Maintains backward compatibility
The dual paths ensure proper SSH-based remote activation while keeping local activation unchanged.
221-234: No action needed. The copy operation at lines 221-234 is already protected by theif out_path.exists()check, which correctly prevents redundant copying when build_host and target_host are the same. Theremote::build_remotefunction skips copying the result back to localhost in that case, ensuring out_path won't exist locally and thus the copy block won't execute.package.nix (2)
8-8: LGTM:sudoadded as test dependency.Adding
sudoas a derivation input is appropriate for the new sudo-dependent tests introduced in this PR.
82-85: LGTM: Conditional test dependency handling.The conditional addition of
sudotonativeCheckInputsonly on non-Darwin platforms is correct and well-commented. This approach properly handles platform differences in package availability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
src/nixos.rs (4)
71-83: Consider consistent elevation parameter passing.Lines 71-78 pass
elevationby value torebuild_and_activate, while line 83 passes&elevationby reference tobuild_only. This inconsistency in the API surface could be confusing for maintainers.Consider updating
rebuild_and_activateto accept&ElevationStrategyfor consistency, or document why the difference is necessary.
249-249: Misleading variable name:is_remote_buildonly checks target host.Line 249 defines
is_remote_build = self.rebuild.target_host.is_some(), but this only checks if there's a target host, not if the build itself was remote. A configuration withbuild_hostset but notarget_hostwould haveis_remote_build = false, even though the build happened remotely.Consider renaming to
has_target_hostor checking bothbuild_host.is_some() || target_host.is_some()if the intent is to detect any remote operation.
254-257: Verify if fallback case should be an error.Lines 254-257 handle "Remote build with no local result and no store path captured" as a fallback by using
target_profile.to_path_buf(). The comment indicates "shouldn't happen, but fallback."If this case genuinely shouldn't occur, consider making it an error with a clear message rather than silently falling back. This would help catch bugs earlier during development and testing.
289-307: Potential inconsistency in path canonicalization.Lines 295-302 conditionally canonicalize
switch_to_configuration_pathbased on whether the output exists locally. However, line 305 assigns this tocanonical_out_pathand treats it as canonical in subsequent code.When
is_remote_build && !out_path.exists()is true (lines 295-297), the path is NOT canonicalized, creating an inconsistency wherecanonical_out_pathmay not actually be canonical.Consider renaming
canonical_out_pathtoswitch_to_configurationoractivation_script_pathto avoid implying it's always canonical.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/nixos.rs
🧰 Additional context used
🧬 Code graph analysis (1)
src/nixos.rs (1)
src/remote.rs (3)
copy_to_remote(993-1023)build_remote(1494-1594)validate_closure_remote(827-924)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: Build NH on Darwin
- GitHub Check: Build NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: Test NH on Linux
- GitHub Check: treewide-checks
- GitHub Check: treewide-checks
- GitHub Check: Build NH on Linux
- GitHub Check: Build NH on Darwin
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
🔇 Additional comments (5)
src/nixos.rs (5)
36-44: LGTM! Well-documented essential files constant.The
ESSENTIAL_FILESconstant is clearly documented and will help with consistent validation across local and remote builds.
169-176: Good practice: SSH control guard initialization.Initializing the SSH control guard early ensures connection multiplexing is available for both build and activation phases, improving performance for remote operations.
489-552: LGTM! Clean separation of remote and local build paths.The refactored
execute_buildmethod clearly separates remote and local build logic. Remote builds properly return the actual store path, while local builds returnNone, allowing callers to distinguish between the two cases.
948-1015: LGTM! Validation functions are well-implemented.Both local and remote validation functions provide clear error messages with actionable guidance. The remote validation properly delegates to
remote::validate_closure_remote, which handles SSH connection multiplexing and batched checks (as seen in the relevant code snippets).The context-building logic (lines 999-1006) helps users understand which host failed validation.
1072-1093: LGTM! Elevation status check properly updated.The signature change to accept
&ElevationStrategyis consistent with the broader refactoring. The logic correctly handles theElevationStrategy::Nonecase before checking root status.
Signed-off-by: NotAShelf <[email protected]> Change-Id: Id2ae10eae0c6aa66034c5bf725c9447f6a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: If55c758a3ef8022ac2a9798be31a0ffa6a6a6964
Signed-off-by: NotAShelf <[email protected]> Change-Id: Ib42fe0268a9852055e283deeac4606f66a6a6964
9b8f6f2 to
ed1e21a
Compare
|
Alright, let's ball. |
Fixes #428, Fixes #434
This is a large architectural change to NH, which lead to me extracting the remote build logic to its own file so that we may implement it for Darwin and Home-Manager as well. The
--buildersflag was dropped fromnh::commands, and it was replaced with the new and shiny logic that hopefully avoids previous pitfalls.The new
nh::remotemodule handles remote builds, including:nix-copy-closure.nix build.As planned, this implements similar remote build semantics to add addsupport for remote builds via SSH for Home Manager and Darwin configurations.
Change-Id: I236eb1e35dd645f2169462d207bc82e76a6a6964
Summary by CodeRabbit
New Features
Options
Bug Fixes
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.