Skip to content
Draft
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
62 changes: 13 additions & 49 deletions apps/native/src-tauri/src/commands/apply.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use super::helpers::capture_err;
use crate::bootstrap::default_config;
use crate::git;
use crate::storage::store;
use crate::system::nix;
use crate::{rebuild, shared_types};
use std::process::Command;
use tauri::AppHandle;

/// Starts a streaming darwin-rebuild switch operation.
Expand Down Expand Up @@ -60,60 +60,24 @@ pub async fn darwin_apply_stream_cancel(app: AppHandle) -> Result<shared_types::
let dir = store::ensure_config_dir_exists(&app)
.map_err(|e| capture_err("darwin_apply_stream_cancel", e))?;

let output = Command::new("git")
.args(["add", "."])
.current_dir(&dir)
.env("PATH", nix::get_nix_path())
.output()
.map_err(|e| capture_err("darwin_apply_stream_cancel", e))?;
if !output.status.success() {
return Err(format!(
"Failed to add files to git: {}",
String::from_utf8_lossy(&output.stderr)
));
}
git::run_command(&dir, ["add", "."])
.map_err(|e| format!("Failed to add files to git: {}", e))?;

let date = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string();
let output = Command::new("git")
.args(["checkout", "-b", &format!("canceled-{}", date)])
.current_dir(&dir)
.env("PATH", nix::get_nix_path())
.output()
.map_err(|e| capture_err("darwin_apply_stream_cancel", e))?;
if !output.status.success() {
return Err(format!(
"Failed to checkout canceled commit: {}",
String::from_utf8_lossy(&output.stderr)
));
}
let branch_name = format!("canceled-{}", date);
let output = git::run_command(&dir, ["checkout", "-b", &branch_name])
.map_err(|e| format!("Failed to checkout canceled commit: {}", e))?;

let commit_hash = String::from_utf8_lossy(&output.stdout).trim().to_string();
let output = Command::new("git")
.args(["commit", "-m", &format!("Canceled commit: {}", commit_hash)])
.current_dir(&dir)
.env("PATH", nix::get_nix_path())
.output()
.map_err(|e| capture_err("darwin_apply_stream_cancel", e))?;
if !output.status.success() {
return Err(format!(
"Failed to commit canceled commit: {}",
String::from_utf8_lossy(&output.stderr)
));
}
git::run_command(
&dir,
["commit", "-m", &format!("Canceled commit: {}", commit_hash)],
)
.map_err(|e| format!("Failed to commit canceled commit: {}", e))?;

// check out prev branch
let output = Command::new("git")
.args(["checkout", "-"])
.current_dir(&dir)
.env("PATH", nix::get_nix_path())
.output()
.map_err(|e| capture_err("darwin_apply_stream_cancel", e))?;
if !output.status.success() {
return Err(format!(
"Failed to checkout previous branch: {}",
String::from_utf8_lossy(&output.stderr)
));
}
git::run_command(&dir, ["checkout", "-"])
.map_err(|e| format!("Failed to checkout previous branch: {}", e))?;

// TODO: Implement actual cancellation by tracking the child process
Ok(shared_types::OkResult::yes())
Expand Down
43 changes: 41 additions & 2 deletions apps/native/src-tauri/src/git/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl GitCommand {
}

/// Identity and hooks injected so nixmac doesn't inherit user's config
fn git_command() -> GitCommand {
fn base_git_command() -> Command {
let mut cmd = Command::new("git");
cmd.env("PATH", crate::system::nix::get_nix_path());
cmd.args([
Expand All @@ -54,7 +54,21 @@ fn git_command() -> GitCommand {
"-c",
"core.hooksPath=/dev/null",
]);
GitCommand(cmd)
cmd
}

fn git_command() -> GitCommand {
GitCommand(base_git_command())
}

/// Runs a git command with nixmac's fixed identity and hook suppression.
pub fn run_command<I, S, P>(dir: P, args: I) -> Result<Output>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
P: AsRef<Path>,
{
git_command().args(args).current_dir(dir).output()
}

/// Checks if a directory is inside a git repository.
Expand Down Expand Up @@ -649,6 +663,31 @@ mod tests {
assert!(is_repo(&repo_dir.to_string_lossy()));
}

#[cfg(unix)]
#[test]
fn test_run_command_disables_repo_hooks() {
use std::os::unix::fs::PermissionsExt;

let temp_dir = TempDir::new().unwrap();
let repo_dir = temp_dir.path().join("repo");
init_repo(&repo_dir.to_string_lossy()).unwrap();

let hook_path = repo_dir.join(".git/hooks/pre-commit");
fs::write(&hook_path, "#!/bin/sh\nprintf ran > hook-ran\nexit 1\n").unwrap();
let mut permissions = fs::metadata(&hook_path).unwrap().permissions();
permissions.set_mode(0o755);
fs::set_permissions(&hook_path, permissions).unwrap();

fs::write(repo_dir.join("flake.nix"), "{ }").unwrap();
run_command(&repo_dir, ["add", "-A"]).unwrap();
run_command(&repo_dir, ["commit", "-m", "initial commit"]).unwrap();

assert!(
!repo_dir.join("hook-ran").exists(),
"repo hooks must not run from nixmac git commands"
);
}

#[test]
fn test_status() {
let temp_dir = TempDir::new().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion apps/native/src-tauri/src/git/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ pub use exec::{
cache_status, cached, checkout_files_at_commit, commit_all, commit_diff,
create_evolution_backup, current_branch, delete_backup_branch, get_full_diff, get_nix_diff,
get_ref_sha, init_repo, intent_add_untracked, is_repo, log, read_tags, restore_all,
restore_from_branch_ref, stash, status, status_and_cache, tag_commit, CommitInfo,
restore_from_branch_ref, run_command, stash, status, status_and_cache, tag_commit, CommitInfo,
};
Loading