From fa740098771addad71a892db3de00b6f2e2f3481 Mon Sep 17 00:00:00 2001 From: Test Date: Mon, 16 Mar 2026 05:36:45 +0100 Subject: [PATCH] feat: ensure .DS_Store in .gitignore for all new vaults Extract ensure_gitignore from init_repo so it can be reused by clone_repo (GitHub "Create New" flow) and repair_vault. New vaults created via any path now get .DS_Store excluded by default. Co-Authored-By: Claude Opus 4.6 (1M context) --- src-tauri/src/commands.rs | 2 + src-tauri/src/git/mod.rs | 63 ++++++++++++++----- src-tauri/src/github/clone.rs | 3 + .../add-ds-store-files-to-gitignore.spec.ts | 15 +++++ 4 files changed, 66 insertions(+), 17 deletions(-) create mode 100644 tests/smoke/add-ds-store-files-to-gitignore.spec.ts diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index dcdbd6f9..c5e0acb7 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -556,6 +556,8 @@ pub fn repair_vault(vault_path: String) -> Result { theme::restore_default_themes(&vault_path)?; // Repair config files (config/agents.md, type/config.md, AGENTS.md stub) vault::repair_config_files(&vault_path)?; + // Ensure .gitignore with sensible defaults exists + git::ensure_gitignore(&vault_path)?; Ok("Vault repaired".to_string()) } diff --git a/src-tauri/src/git/mod.rs b/src-tauri/src/git/mod.rs index 6be92ebf..f8166c95 100644 --- a/src-tauri/src/git/mod.rs +++ b/src-tauri/src/git/mod.rs @@ -30,20 +30,7 @@ pub struct GitCommit { pub date: i64, } -/// Initialize a new git repository, stage all files, and create an initial commit. -pub fn init_repo(path: &str) -> Result<(), String> { - let dir = Path::new(path); - - run_git(dir, &["init"])?; - ensure_author_config(dir)?; - - // Write .gitignore before the first commit so machine-specific and - // macOS metadata files are never tracked and don't cause conflicts. - let gitignore_path = dir.join(".gitignore"); - if !gitignore_path.exists() { - std::fs::write( - &gitignore_path, - "# Laputa app files (machine-specific, never commit)\n\ +const DEFAULT_GITIGNORE: &str = "# Laputa app files (machine-specific, never commit)\n\ .laputa/settings.json\n\ \n\ # macOS\n\ @@ -58,10 +45,29 @@ pub fn init_repo(path: &str) -> Result<(), String> { .vscode/\n\ .idea/\n\ *.swp\n\ -*.swo\n", - ) - .map_err(|e| format!("Failed to write .gitignore: {}", e))?; +*.swo\n"; + +/// Ensure a `.gitignore` with sensible defaults exists in the vault directory. +/// Creates the file if missing; leaves existing `.gitignore` files untouched. +pub fn ensure_gitignore(path: &str) -> Result<(), String> { + let gitignore_path = Path::new(path).join(".gitignore"); + if !gitignore_path.exists() { + std::fs::write(&gitignore_path, DEFAULT_GITIGNORE) + .map_err(|e| format!("Failed to write .gitignore: {}", e))?; } + Ok(()) +} + +/// Initialize a new git repository, stage all files, and create an initial commit. +pub fn init_repo(path: &str) -> Result<(), String> { + let dir = Path::new(path); + + run_git(dir, &["init"])?; + ensure_author_config(dir)?; + + // Write .gitignore before the first commit so machine-specific and + // macOS metadata files are never tracked and don't cause conflicts. + ensure_gitignore(path)?; run_git(dir, &["add", "."])?; run_git(dir, &["commit", "-m", "Initial vault setup"])?; @@ -212,6 +218,29 @@ mod tests { (bare_dir, clone_a_dir, clone_b_dir) } + #[test] + fn test_ensure_gitignore_creates_file() { + let dir = TempDir::new().unwrap(); + let path = dir.path().to_str().unwrap(); + + ensure_gitignore(path).unwrap(); + + let content = fs::read_to_string(dir.path().join(".gitignore")).unwrap(); + assert!(content.contains(".DS_Store")); + assert!(content.contains(".laputa/settings.json")); + } + + #[test] + fn test_ensure_gitignore_preserves_existing() { + let dir = TempDir::new().unwrap(); + fs::write(dir.path().join(".gitignore"), "my-rule\n").unwrap(); + + ensure_gitignore(dir.path().to_str().unwrap()).unwrap(); + + let content = fs::read_to_string(dir.path().join(".gitignore")).unwrap(); + assert_eq!(content, "my-rule\n"); + } + #[test] fn test_init_repo_creates_git_directory() { let dir = TempDir::new().unwrap(); diff --git a/src-tauri/src/github/clone.rs b/src-tauri/src/github/clone.rs index c2483992..e6f8f56b 100644 --- a/src-tauri/src/github/clone.rs +++ b/src-tauri/src/github/clone.rs @@ -37,6 +37,9 @@ pub fn clone_repo(url: &str, token: &str, local_path: &str) -> Result { + test.beforeEach(async ({ page }) => { + await page.goto('/') + await page.waitForLoadState('networkidle') + }) + + test('Repair Vault command is available in the command palette', async ({ page }) => { + await openCommandPalette(page) + const found = await findCommand(page, 'Repair Vault') + expect(found).toBe(true) + }) +})