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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ functionality, under the "Removed" section.

## Unreleased

### Added

- `nh darwin` now displays Homebrew package changes alongside Nix package
changes when using nix-darwin configurations with Homebrew management enabled.
This shows formulae, casks, taps, and Mac App Store apps that will be
added or removed during a configuration switch.
Comment on lines +21 to +24
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Clarify Mac App Store behavior (removals aren’t shown/uninstalled).

brewdiff/mas won’t uninstall MAS apps; removals aren’t reported. The current wording implies added or removed for MAS as well.

Apply this diff:

-  This shows formulae, casks, taps, and Mac App Store apps that will be
-  added or removed during a configuration switch.
+  This shows formulae, casks, taps, and Mac App Store apps that will be
+  added during a configuration switch (Mac App Store removals are not
+  uninstalled by `mas` and are therefore not shown).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- `nh darwin` now displays Homebrew package changes alongside Nix package
changes when using nix-darwin configurations with Homebrew management enabled.
This shows formulae, casks, taps, and Mac App Store apps that will be
added or removed during a configuration switch.
- `nh darwin` now displays Homebrew package changes alongside Nix package
changes when using nix-darwin configurations with Homebrew management enabled.
This shows formulae, casks, taps, and Mac App Store apps that will be
added during a configuration switch (Mac App Store removals are not
uninstalled by `mas` and are therefore not shown).
🤖 Prompt for AI Agents
In CHANGELOG.md around lines 21 to 24, the current entry implies Mac App Store
(MAS) apps will be both added and removed when using `nh darwin` with Homebrew
management; however brewdiff/mas does not uninstall MAS apps and does not report
removals. Update the text to explicitly state that MAS apps will be listed when
they will be added but removals are not reported or performed (i.e., MAS apps
are only shown for additions, not removals/uninstalls), and adjust the sentence
to mention formulae, casks, taps, and MAS apps with a parenthetical or clause
clarifying MAS removal behavior.


### Changed

- Nh checks are now more robust in the sense that unnecessary features will not
Expand Down
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ tracing-subscriber = { features = [ "env-filter", "registry", "std" ], version =
which = "8.0.0"

[target.'cfg(target_os="macos")'.dependencies]
brewdiff = "0.2.0"
system-configuration = "0.6.1"

[dev-dependencies]
Expand Down
4 changes: 3 additions & 1 deletion src/darwin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
},
nixos::toplevel_for,
update::update,
util::{get_hostname, print_dix_diff},
util::{get_hostname, print_dix_diff, print_homebrew_diff},
};

const SYSTEM_PROFILE: &str = "/nix/var/nix/profiles/system";
Expand Down Expand Up @@ -142,6 +142,8 @@ impl DarwinRebuildArgs {
target_profile.display()
);
let _ = print_dix_diff(&PathBuf::from(CURRENT_PROFILE), &target_profile);
let _ =
print_homebrew_diff(&PathBuf::from(CURRENT_PROFILE), &target_profile);
}

if self.common.ask && !self.common.dry && !matches!(variant, Build) {
Expand Down
83 changes: 83 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ impl<W: io::Write> fmt::Write for WriteFmt<W> {
self.0.write_all(string.as_bytes()).map_err(|_| fmt::Error)
}
}

/// Get the Nix variant (cached)
pub fn get_nix_variant() -> &'static NixVariant {
NIX_VARIANT.get_or_init(|| {
Expand Down Expand Up @@ -336,3 +337,85 @@ pub fn print_dix_diff(
}
Ok(())
}

/// Prints the difference between Homebrew packages in darwin generations.
///
/// # Arguments
///
/// * `old_generation` - A reference to the path of the old/current generation.
/// * `new_generation` - A reference to the path of the new generation.
///
/// # Returns
///
/// Returns `Ok(())` if the operation completed successfully, or an error
/// wrapped in `eyre::Result` if something went wrong. Silently returns Ok(())
/// if Homebrew is not available or not configured.
#[cfg(target_os = "macos")]
pub fn print_homebrew_diff(
_old_generation: &Path,
new_generation: &Path,
) -> Result<()> {
if !homebrew_available() {
debug!("Homebrew not found, skipping Homebrew diff");
return Ok(());
}

// Try to extract the nix-darwin Homebrew intent from the new profile
// If this fails, it likely means Homebrew isn't configured in the profile
let nix_intent = match brewdiff::extract_nix_darwin_intent(new_generation) {
Ok(intent) => intent,
Err(e) => {
debug!("Could not extract Homebrew intent from profile: {}", e);
return Ok(());
},
};

if !nix_intent.has_packages() {
debug!("No Homebrew packages configured in profile, skipping diff");
return Ok(());
}

let mut out = WriteFmt(io::stdout());

let diff_handle = brewdiff::spawn_homebrew_diff(new_generation.to_path_buf());
let diff_data = match diff_handle.join() {
Ok(Ok(data)) => data,
Ok(Err(e)) => {
debug!("Failed to compute Homebrew diff: {}", e);
return Ok(());
},
Err(_) => {
debug!("Homebrew diff thread panicked");
return Ok(());
},
};

if diff_data.has_changes() {
// Separator from dix' output
println!();

// Print statistics first to make it clear the details below belong to
// Homebrew
brewdiff::write_homebrew_stats(&mut out, &diff_data)?;
brewdiff::display::write_diff(&mut out, &diff_data)?;
} else {
info!("No Homebrew package changes.");
}

Ok(())
}

/// Checks if Homebrew is available on the system
#[cfg(target_os = "macos")]
fn homebrew_available() -> bool {
which::which("brew").is_ok()
}

/// Stub for non-macOS platforms
#[cfg(not(target_os = "macos"))]
pub fn print_homebrew_diff(
_old_generation: &Path,
_new_generation: &Path,
) -> Result<()> {
Ok(())
}