Skip to content

Conversation

andre4ik3
Copy link

@andre4ik3 andre4ik3 commented Sep 10, 2025

As part of supporting the new nix-darwin activation style in #238, there was a breaking change made to use darwin-rebuild instead of calling the activation scripts directly. Unfortunately this change meant it was no longer meant to use nh as a darwin-rebuild replacement, as it would depend on darwin-rebuild itself. So if darwin-rebuild was disabled (e.g. by setting system.tools.enable = false), nh would fail to activate the system with an error like the following:

> Comparing changes
<<< /run/current-system
>>> /var/folders/h7/34fnxyx94lv_hkg1fmg658fr0000gn/T/nh-osusP10v/result
No version or selection state changes.
Closure size: 2055 -> 2055 (0 paths added, 0 paths removed, delta +0, disk usage +0B).
> Activating configuration
env: ‘/var/folders/h7/34fnxyx94lv_hkg1fmg658fr0000gn/T/nh-osusP10v/result/sw/bin/darwin-rebuild’: No such file or directory

This PR simply adds a check whether darwin-rebuild exists, and if not, falls back to the old behavior.

(If it's undesirable for this configuration to be supported then perhaps a warning should be added when activate is used, but I think compatibility should be kept at least for now, because it was done in a minor release, and breaking changes should only happen in major releases according to semver (which is why marked it as a bug fix, since it's fixing a regression from a semver point of view))

Tested in the following cases:

  • Ensure it invokes darwin-rebuild by default
  • Ensure it falls back to activate if darwin-rebuild doesn't exist
  • Ensure it also invokes activate-user if it's the non-deprecated version and it invoked activate

Sanity Checking

  • I have updated the changelog as per my changes
  • I have tested, and self-reviewed my code
  • Style and consistency
    • I ran nix fmt to format my Nix code
    • I ran cargo fmt to format my Rust code
    • I have added appropriate docunentation to new code
    • My changes are consistent with the rest of the codebase
  • Correctness
    • I ran cargo clippy and fixed any new linter warnings.
  • If new changes are particularly complex:
    • My code includes comments in particularly complex areas to explain the
      logic
    • I have documented the motive for those changes in the PR body or commit
      description.
  • Tested on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin

Add a 👍 reaction to pull requests you find important.

Summary by CodeRabbit

  • Bug Fixes

    • macOS activation is more resilient: if the standard rebuild tool isn’t available, activation falls back to direct activation and may perform a user-level activation step.
    • Automatically selects appropriate activation method and elevation based on the environment.
    • Provides clearer, more specific error messages for activation and user activation failures.
  • Documentation

    • Added an Unreleased entry documenting the macOS activation fallback behavior.

Copy link

coderabbitai bot commented Sep 10, 2025

Walkthrough

Detects darwin-rebuild presence and new vs legacy activation in src/darwin.rs, conditionally runs either darwin-rebuild activate or out_path/activate, optionally runs activate-user for legacy flow, adjusts elevation logic and error contexts. Updates CHANGELOG.md to document the Darwin fallback. No public API changes.

Changes

Cohort / File(s) Summary
Documentation
CHANGELOG.md
Added Unreleased -> Fixed entry documenting Darwin-specific fallback: when darwin-rebuild is absent, activation falls back to executing activate (and activate-user when applicable). Documentation-only.
Darwin activation logic
src/darwin.rs
Added detection for darwin-rebuild (has_darwin_rebuild) and activation-style detection (uses_new_activation); select activation command conditionally (darwin-rebuild activate vs out_path/activate); compute should_elevate and apply elevation accordingly; run legacy activate-user when needed; improved error contexts and distinct failure messages. No public signatures changed.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as CLI
  participant FS as Filesystem
  participant Sudo as Elevation
  participant Shell as System Shell

  User->>CLI: trigger activation
  CLI->>FS: stat `darwin-rebuild`
  alt darwin-rebuild exists
    CLI->>FS: inspect `activate-user` (detect new activation)
    CLI->>CLI: set uses_new_activation, should_elevate
    alt should_elevate
      CLI->>Sudo: run `darwin-rebuild activate`
      Sudo->>Shell: execute activation
    else
      CLI->>Shell: run `darwin-rebuild activate`
    end
  else darwin-rebuild missing
    CLI->>FS: inspect `out_path/activate` and `activate-user`
    CLI->>CLI: set uses_new_activation=false, should_elevate=true
    CLI->>Sudo: run `out_path/activate`
    Sudo->>Shell: execute activation
    opt legacy user activation (not uses_new_activation)
      CLI->>Shell: run `out_path/activate-user`
    end
  end

  note over CLI,Shell: Errors include contextual messages for activation vs user activation failures
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–60 minutes

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “darwin: fix activation without darwin-rebuild” accurately and concisely captures the primary change—restoring activation fallback on macOS when the darwin-rebuild tool is unavailable—without extraneous detail, making it clear to reviewers scanning the pull request history.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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/darwin.rs (1)

176-182: Short-circuit file read is correct; consider non-UTF8 safety.

Using || short-circuits when file is absent. If you want to be extra defensive against unexpected non-UTF8 contents, switch to fs::read(..).map(|b| String::from_utf8_lossy(&b).contains("deprecated")) to avoid a hard error on encoding.

-      let uses_new_activation = !activate_user
-        .try_exists()
-        .context("Failed to check if activate-user file exists")?
-        || std::fs::read_to_string(&activate_user)
-          .context("Failed to read activate-user file")?
-          .contains("# nix-darwin: deprecated");
+      let uses_new_activation = !activate_user
+        .try_exists()
+        .context("Failed to check if activate-user file exists")?
+        || {
+          let bytes = std::fs::read(&activate_user)
+            .context("Failed to read activate-user file")?;
+          std::str::from_utf8(&bytes)
+            .map(|s| s.contains("# nix-darwin: deprecated"))
+            .unwrap_or_else(|_| String::from_utf8_lossy(&bytes).contains("# nix-darwin: deprecated"))
+        };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 440d3a6 and 34da5b0.

📒 Files selected for processing (2)
  • CHANGELOG.md (1 hunks)
  • src/darwin.rs (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Check formating & lints
src/darwin.rs

[error] 172-172: cargo fmt --check reported formatting differences in src/darwin.rs. Run 'cargo fmt' to fix the formatting.

⏰ 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). (4)
  • GitHub Check: Test NH on Darwin
  • GitHub Check: Build NH on Darwin
  • GitHub Check: Build NH on Linux
  • GitHub Check: Test NH on Linux
🔇 Additional comments (6)
CHANGELOG.md (1)

17-23: Changelog entry reads well and matches behavior.

Clear, user-facing description of the Darwin activation fallback. No issues.

src/darwin.rs (5)

170-174: Presence check for darwin-rebuild: LGTM.

Good use of try_exists with contextual errors.


183-188: Potential mutability requirement for builder variable.

If commands::Command builder methods take &mut self (common), activation should be mut. If they take self by value, current code is fine. Please confirm.

-      let activation = if has_darwin_rebuild {
+      let mut activation = if has_darwin_rebuild {
         Command::new(darwin_rebuild).arg("activate")
       } else {
         Command::new(out_path.join("activate"))
       };

189-199: Elevation logic looks correct and matches PR intent.

should_elevate = uses_new_activation || !has_darwin_rebuild aligns with root-only activation in newer nix-darwin and legacy script fallback. Nice use of then_some(elevation) to avoid unnecessary cloning.


200-208: Fallback to activate-user only when not using darwin-rebuild: LGTM.

This matches the stated requirement (“ensure activate-user is invoked when non-deprecated activate is used”). Error context is clear.


170-208: Normalize code formatting with cargo fmt

CI is failing due to formatting differences. Please run cargo fmt locally and commit the resulting changes to unblock CI.

@andre4ik3 andre4ik3 force-pushed the fix-no-darwin-rebuild branch from 34da5b0 to a7e5346 Compare September 10, 2025 10:32
@andre4ik3
Copy link
Author

Made the comment shorter so it passes the format CI check. Idk what all this AI stuff is I've never seen it before, not sure what I'm supposed to do with it exactly.

Copy link

@coderabbitai coderabbitai bot left a 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/darwin.rs (1)

190-191: Consider documenting the elevation logic.

The elevation condition uses_new_activation || !has_darwin_rebuild may not be immediately obvious. While the logic is correct (elevate for new-style activation OR when using the fallback path), a comment explaining this would improve maintainability.

Add a clarifying comment:

+// Elevate privileges when:
+// - Using new activation style (which requires elevation), OR
+// - Using fallback activation without darwin-rebuild (legacy behavior)
 let should_elevate = uses_new_activation || !has_darwin_rebuild;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 34da5b0 and a7e5346.

📒 Files selected for processing (2)
  • CHANGELOG.md (1 hunks)
  • src/darwin.rs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🧰 Additional context used
🧬 Code graph analysis (1)
src/darwin.rs (1)
src/commands.rs (3)
  • new (156-167)
  • new (597-605)
  • new (719-728)
⏰ 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: 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
🔇 Additional comments (5)
src/darwin.rs (5)

170-174: LGTM! Proper runtime detection of darwin-rebuild.

The code correctly checks for darwin-rebuild presence at runtime using try_exists() with appropriate error handling. This enables the fallback behavior when darwin-rebuild is not available.


175-183: LGTM! Well-implemented activation style detection.

The logic correctly identifies the new activation style by checking both the absence of activate-user and the presence of the deprecation marker. The error handling for file operations is appropriate.


184-189: LGTM! Clean conditional activation path selection.

The conditional logic properly selects between darwin-rebuild activate when available or falls back to the direct activate script. This maintains backward compatibility while supporting the new activation style.


192-200: LGTM! Proper activation execution with clear messaging.

The activation command is executed with appropriate elevation, dry-run support, and clear user messaging. The error context "Darwin activation failed" helps with debugging.


201-209: LGTM! Correct fallback for legacy user activation.

The code properly handles the legacy case where activate-user needs to be run separately when darwin-rebuild is not available and the old activation style is in use. The distinct error message helps differentiate between main and user activation failures.

@NotAShelf
Copy link
Member

You may disregard the AI comments. I'm currently looking to see if it provides helpful comments that help guide my reviews (as the sole maintainer) or if it creates useless noise. Outlook not so good.

I'll take a review later today. Please feel free to remind me with a ping if I forget. Also CC @khaneliman as someone more aware of nix-darwin's internals.

@khaneliman
Copy link
Contributor

I mean, it was a deliberate choice #238 (comment)

@andre4ik3
Copy link
Author

I see. So should I add a warning about it and mark that it will be removed in the future? (v5.0.0)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants