story-kit: merge 259_story_move_story_kit_ignores_into_story_kit_gitignore
This commit is contained in:
16
.gitignore
vendored
16
.gitignore
vendored
@@ -4,24 +4,10 @@
|
|||||||
# Local environment (secrets)
|
# Local environment (secrets)
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# App specific
|
# App specific (root-level; story-kit subdirectory patterns live in .story_kit/.gitignore)
|
||||||
store.json
|
store.json
|
||||||
.story_kit_port
|
.story_kit_port
|
||||||
|
|
||||||
# Bot config (contains credentials)
|
|
||||||
.story_kit/bot.toml
|
|
||||||
|
|
||||||
# Matrix SDK state store
|
|
||||||
.story_kit/matrix_store/
|
|
||||||
.story_kit/matrix_device_id
|
|
||||||
|
|
||||||
# Agent worktrees and merge workspace (managed by the server, not tracked in git)
|
|
||||||
.story_kit/worktrees/
|
|
||||||
.story_kit/merge_workspace/
|
|
||||||
|
|
||||||
# Coverage reports (generated by cargo-llvm-cov, not tracked in git)
|
|
||||||
.story_kit/coverage/
|
|
||||||
|
|
||||||
# Rust stuff
|
# Rust stuff
|
||||||
target
|
target
|
||||||
|
|
||||||
|
|||||||
13
.story_kit/.gitignore
vendored
Normal file
13
.story_kit/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Bot config (contains credentials)
|
||||||
|
bot.toml
|
||||||
|
|
||||||
|
# Matrix SDK state store
|
||||||
|
matrix_store/
|
||||||
|
matrix_device_id
|
||||||
|
|
||||||
|
# Agent worktrees and merge workspace (managed by the server, not tracked in git)
|
||||||
|
worktrees/
|
||||||
|
merge_workspace/
|
||||||
|
|
||||||
|
# Coverage reports (generated by cargo-llvm-cov, not tracked in git)
|
||||||
|
coverage/
|
||||||
@@ -313,17 +313,61 @@ fn write_script_if_missing(path: &Path, content: &str) -> Result<(), String> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Append Story Kit entries to `.gitignore` (or create one if missing).
|
/// Write (or idempotently update) `.story_kit/.gitignore` with Story Kit–specific
|
||||||
/// Does not duplicate entries already present.
|
/// ignore patterns for files that live inside the `.story_kit/` directory.
|
||||||
fn append_gitignore_entries(root: &Path) -> Result<(), String> {
|
/// Patterns are relative to `.story_kit/` as git resolves `.gitignore` files
|
||||||
|
/// relative to the directory that contains them.
|
||||||
|
fn write_story_kit_gitignore(root: &Path) -> Result<(), String> {
|
||||||
|
// Entries that belong inside .story_kit/.gitignore (relative to .story_kit/).
|
||||||
let entries = [
|
let entries = [
|
||||||
".story_kit/worktrees/",
|
"bot.toml",
|
||||||
".story_kit/merge_workspace/",
|
"matrix_store/",
|
||||||
".story_kit/coverage/",
|
"matrix_device_id",
|
||||||
".story_kit_port",
|
"worktrees/",
|
||||||
"store.json",
|
"merge_workspace/",
|
||||||
|
"coverage/",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let gitignore_path = root.join(".story_kit").join(".gitignore");
|
||||||
|
let existing = if gitignore_path.exists() {
|
||||||
|
fs::read_to_string(&gitignore_path)
|
||||||
|
.map_err(|e| format!("Failed to read .story_kit/.gitignore: {}", e))?
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let missing: Vec<&str> = entries
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|e| !existing.lines().any(|l| l.trim() == *e))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if missing.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_content = existing;
|
||||||
|
if !new_content.is_empty() && !new_content.ends_with('\n') {
|
||||||
|
new_content.push('\n');
|
||||||
|
}
|
||||||
|
for entry in missing {
|
||||||
|
new_content.push_str(entry);
|
||||||
|
new_content.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::write(&gitignore_path, new_content)
|
||||||
|
.map_err(|e| format!("Failed to write .story_kit/.gitignore: {}", e))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append root-level Story Kit entries to the project `.gitignore`.
|
||||||
|
/// Only `store.json` and `.story_kit_port` remain here because they live at
|
||||||
|
/// the project root and git does not support `../` patterns in `.gitignore`
|
||||||
|
/// files, so they cannot be expressed in `.story_kit/.gitignore`.
|
||||||
|
fn append_root_gitignore_entries(root: &Path) -> Result<(), String> {
|
||||||
|
let entries = [".story_kit_port", "store.json"];
|
||||||
|
|
||||||
let gitignore_path = root.join(".gitignore");
|
let gitignore_path = root.join(".gitignore");
|
||||||
let existing = if gitignore_path.exists() {
|
let existing = if gitignore_path.exists() {
|
||||||
fs::read_to_string(&gitignore_path)
|
fs::read_to_string(&gitignore_path)
|
||||||
@@ -402,7 +446,8 @@ fn scaffold_story_kit(root: &Path) -> Result<(), String> {
|
|||||||
.map_err(|e| format!("Failed to create .claude/ directory: {}", e))?;
|
.map_err(|e| format!("Failed to create .claude/ directory: {}", e))?;
|
||||||
write_file_if_missing(&claude_dir.join("settings.json"), STORY_KIT_CLAUDE_SETTINGS)?;
|
write_file_if_missing(&claude_dir.join("settings.json"), STORY_KIT_CLAUDE_SETTINGS)?;
|
||||||
|
|
||||||
append_gitignore_entries(root)?;
|
write_story_kit_gitignore(root)?;
|
||||||
|
append_root_gitignore_entries(root)?;
|
||||||
|
|
||||||
// Run `git init` if the directory is not already a git repo, then make an initial commit
|
// Run `git init` if the directory is not already a git repo, then make an initial commit
|
||||||
if !root.join(".git").exists() {
|
if !root.join(".git").exists() {
|
||||||
@@ -1122,12 +1167,17 @@ mod tests {
|
|||||||
toml_content
|
toml_content
|
||||||
);
|
);
|
||||||
|
|
||||||
let gitignore = fs::read_to_string(dir.path().join(".gitignore")).unwrap();
|
let story_kit_gitignore =
|
||||||
let count = gitignore
|
fs::read_to_string(dir.path().join(".story_kit/.gitignore")).unwrap();
|
||||||
|
let count = story_kit_gitignore
|
||||||
.lines()
|
.lines()
|
||||||
.filter(|l| l.trim() == ".story_kit/worktrees/")
|
.filter(|l| l.trim() == "worktrees/")
|
||||||
.count();
|
.count();
|
||||||
assert_eq!(count, 1, ".gitignore should not have duplicate entries");
|
assert_eq!(
|
||||||
|
count,
|
||||||
|
1,
|
||||||
|
".story_kit/.gitignore should not have duplicate entries"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1173,53 +1223,56 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scaffold_creates_gitignore_with_story_kit_entries() {
|
fn scaffold_creates_story_kit_gitignore_with_relative_entries() {
|
||||||
let dir = tempdir().unwrap();
|
let dir = tempdir().unwrap();
|
||||||
scaffold_story_kit(dir.path()).unwrap();
|
scaffold_story_kit(dir.path()).unwrap();
|
||||||
|
|
||||||
let content = fs::read_to_string(dir.path().join(".gitignore")).unwrap();
|
// .story_kit/.gitignore must contain relative patterns for files under .story_kit/
|
||||||
assert!(content.contains(".story_kit/worktrees/"));
|
let sk_content =
|
||||||
assert!(content.contains(".story_kit/merge_workspace/"));
|
fs::read_to_string(dir.path().join(".story_kit/.gitignore")).unwrap();
|
||||||
assert!(content.contains(".story_kit/coverage/"));
|
assert!(sk_content.contains("worktrees/"));
|
||||||
assert!(content.contains(".story_kit_port"));
|
assert!(sk_content.contains("merge_workspace/"));
|
||||||
assert!(content.contains("store.json"));
|
assert!(sk_content.contains("coverage/"));
|
||||||
|
// Must NOT contain absolute .story_kit/ prefixed paths
|
||||||
|
assert!(!sk_content.contains(".story_kit/"));
|
||||||
|
|
||||||
|
// Root .gitignore must contain root-level story-kit entries
|
||||||
|
let root_content = fs::read_to_string(dir.path().join(".gitignore")).unwrap();
|
||||||
|
assert!(root_content.contains(".story_kit_port"));
|
||||||
|
assert!(root_content.contains("store.json"));
|
||||||
|
// Root .gitignore must NOT contain .story_kit/ sub-directory patterns
|
||||||
|
assert!(!root_content.contains(".story_kit/worktrees/"));
|
||||||
|
assert!(!root_content.contains(".story_kit/merge_workspace/"));
|
||||||
|
assert!(!root_content.contains(".story_kit/coverage/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scaffold_gitignore_does_not_duplicate_existing_entries() {
|
fn scaffold_story_kit_gitignore_does_not_duplicate_existing_entries() {
|
||||||
let dir = tempdir().unwrap();
|
let dir = tempdir().unwrap();
|
||||||
// Pre-create .gitignore with some Story Kit entries already present
|
// Pre-create .story_kit dir and .gitignore with some entries already present
|
||||||
|
fs::create_dir_all(dir.path().join(".story_kit")).unwrap();
|
||||||
fs::write(
|
fs::write(
|
||||||
dir.path().join(".gitignore"),
|
dir.path().join(".story_kit/.gitignore"),
|
||||||
".story_kit/worktrees/\n.story_kit/coverage/\n",
|
"worktrees/\ncoverage/\n",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
scaffold_story_kit(dir.path()).unwrap();
|
scaffold_story_kit(dir.path()).unwrap();
|
||||||
|
|
||||||
let content = fs::read_to_string(dir.path().join(".gitignore")).unwrap();
|
let content =
|
||||||
|
fs::read_to_string(dir.path().join(".story_kit/.gitignore")).unwrap();
|
||||||
let worktrees_count = content
|
let worktrees_count = content
|
||||||
.lines()
|
.lines()
|
||||||
.filter(|l| l.trim() == ".story_kit/worktrees/")
|
.filter(|l| l.trim() == "worktrees/")
|
||||||
.count();
|
.count();
|
||||||
assert_eq!(
|
assert_eq!(worktrees_count, 1, "worktrees/ should not be duplicated");
|
||||||
worktrees_count,
|
|
||||||
1,
|
|
||||||
".story_kit/worktrees/ should not be duplicated"
|
|
||||||
);
|
|
||||||
let coverage_count = content
|
let coverage_count = content
|
||||||
.lines()
|
.lines()
|
||||||
.filter(|l| l.trim() == ".story_kit/coverage/")
|
.filter(|l| l.trim() == "coverage/")
|
||||||
.count();
|
.count();
|
||||||
assert_eq!(
|
assert_eq!(coverage_count, 1, "coverage/ should not be duplicated");
|
||||||
coverage_count,
|
// The missing entry must have been added
|
||||||
1,
|
assert!(content.contains("merge_workspace/"));
|
||||||
".story_kit/coverage/ should not be duplicated"
|
|
||||||
);
|
|
||||||
// The missing entries must have been added
|
|
||||||
assert!(content.contains(".story_kit/merge_workspace/"));
|
|
||||||
assert!(content.contains(".story_kit_port"));
|
|
||||||
assert!(content.contains("store.json"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- CLAUDE.md scaffold ---
|
// --- CLAUDE.md scaffold ---
|
||||||
|
|||||||
Reference in New Issue
Block a user