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) <noreply@anthropic.com>
This commit is contained in:
@@ -556,6 +556,8 @@ pub fn repair_vault(vault_path: String) -> Result<String, String> {
|
||||
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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -37,6 +37,9 @@ pub fn clone_repo(url: &str, token: &str, local_path: &str) -> Result<String, St
|
||||
// Configure the remote to use token auth for future pushes
|
||||
configure_remote_auth(local_path, url, token)?;
|
||||
|
||||
// Ensure sensible .gitignore defaults (especially .DS_Store on macOS)
|
||||
crate::git::ensure_gitignore(local_path)?;
|
||||
|
||||
Ok(format!("Cloned to {}", local_path))
|
||||
}
|
||||
|
||||
|
||||
15
tests/smoke/add-ds-store-files-to-gitignore.spec.ts
Normal file
15
tests/smoke/add-ds-store-files-to-gitignore.spec.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { openCommandPalette, findCommand } from './helpers'
|
||||
|
||||
test.describe('DS_Store gitignore for new vaults', () => {
|
||||
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)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user