huskies: merge 1219 story Add runtime artifacts to the default project scaffold .gitignore
This commit is contained in:
@@ -35,3 +35,6 @@ double_timmy_log.md
|
|||||||
pipeline.db
|
pipeline.db
|
||||||
pipeline.db.bak*
|
pipeline.db.bak*
|
||||||
session_store.json
|
session_store.json
|
||||||
|
|
||||||
|
# Full untruncated merge attempt reports (one file per attempt, pruned automatically)
|
||||||
|
merge_reports/
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use serde_json::json;
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use super::scaffold::scaffold_story_kit;
|
use super::scaffold::{ensure_gitignore_entries, scaffold_story_kit};
|
||||||
|
|
||||||
const KEY_LAST_PROJECT: &str = "last_project_path";
|
const KEY_LAST_PROJECT: &str = "last_project_path";
|
||||||
const KEY_KNOWN_PROJECTS: &str = "known_projects";
|
const KEY_KNOWN_PROJECTS: &str = "known_projects";
|
||||||
@@ -36,6 +36,10 @@ pub(crate) async fn ensure_project_root_with_story_kit(
|
|||||||
}
|
}
|
||||||
if !path.join(".huskies").is_dir() {
|
if !path.join(".huskies").is_dir() {
|
||||||
scaffold_story_kit(&path, port)?;
|
scaffold_story_kit(&path, port)?;
|
||||||
|
} else {
|
||||||
|
// Already-adopted project: pick up any Story Kit gitignore entries
|
||||||
|
// added since this project was first scaffolded.
|
||||||
|
ensure_gitignore_entries(&path)?;
|
||||||
}
|
}
|
||||||
// Always update .mcp.json with the current port so the bot connects to
|
// Always update .mcp.json with the current port so the bot connects to
|
||||||
// the right endpoint even when HUSKIES_PORT changes between restarts.
|
// the right endpoint even when HUSKIES_PORT changes between restarts.
|
||||||
@@ -449,6 +453,34 @@ mod tests {
|
|||||||
assert!(project_dir.join(".huskies").is_dir());
|
assert!(project_dir.join(".huskies").is_dir());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Regression test for story 1219: projects scaffolded before
|
||||||
|
/// `merge_reports/` and `session_store.json` were added to the ignore
|
||||||
|
/// list must pick up those entries the next time the project is opened,
|
||||||
|
/// without needing to be re-scaffolded from scratch.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn open_project_retrofits_gitignore_entries_for_existing_project() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let project_dir = dir.path().join("myproject");
|
||||||
|
let sk_dir = project_dir.join(".huskies");
|
||||||
|
fs::create_dir_all(&sk_dir).unwrap();
|
||||||
|
fs::write(sk_dir.join(".gitignore"), "worktrees/\n").unwrap();
|
||||||
|
let store = make_store(&dir);
|
||||||
|
let state = SessionState::default();
|
||||||
|
|
||||||
|
open_project(
|
||||||
|
project_dir.to_string_lossy().to_string(),
|
||||||
|
&state,
|
||||||
|
&store,
|
||||||
|
3001,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let content = fs::read_to_string(sk_dir.join(".gitignore")).unwrap();
|
||||||
|
assert!(content.contains("merge_reports/"));
|
||||||
|
assert!(content.contains("session_store.json"));
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn open_project_does_not_overwrite_existing_story_kit() {
|
async fn open_project_does_not_overwrite_existing_story_kit() {
|
||||||
let dir = tempdir().unwrap();
|
let dir = tempdir().unwrap();
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ pub(super) fn write_story_kit_gitignore(root: &Path) -> Result<(), String> {
|
|||||||
"store.json",
|
"store.json",
|
||||||
"pipeline.db",
|
"pipeline.db",
|
||||||
"*.db",
|
"*.db",
|
||||||
|
"merge_reports/",
|
||||||
|
"session_store.json",
|
||||||
];
|
];
|
||||||
|
|
||||||
let gitignore_path = root.join(".huskies").join(".gitignore");
|
let gitignore_path = root.join(".huskies").join(".gitignore");
|
||||||
|
|||||||
@@ -20,6 +20,16 @@ use templates::{
|
|||||||
STORY_KIT_STACK,
|
STORY_KIT_STACK,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Retrofit the Story Kit `.gitignore` entries onto a project that was
|
||||||
|
/// scaffolded before those entries existed (e.g. `merge_reports/` or
|
||||||
|
/// `session_store.json` added in a later release). Idempotent — only
|
||||||
|
/// appends lines that are missing, so it is safe to call on every project
|
||||||
|
/// open regardless of how old the project's `.huskies/` directory is.
|
||||||
|
pub(crate) fn ensure_gitignore_entries(root: &Path) -> Result<(), String> {
|
||||||
|
write_story_kit_gitignore(root)?;
|
||||||
|
append_root_gitignore_entries(root)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn scaffold_story_kit(root: &Path, port: u16) -> Result<(), String> {
|
pub(crate) fn scaffold_story_kit(root: &Path, port: u16) -> Result<(), String> {
|
||||||
let story_kit_root = root.join(".huskies");
|
let story_kit_root = root.join(".huskies");
|
||||||
let specs_root = story_kit_root.join("specs");
|
let specs_root = story_kit_root.join("specs");
|
||||||
|
|||||||
@@ -334,6 +334,31 @@ fn scaffold_creates_story_kit_gitignore_with_relative_entries() {
|
|||||||
// Database files must be ignored so novice users don't accidentally commit them
|
// Database files must be ignored so novice users don't accidentally commit them
|
||||||
assert!(sk_content.contains("pipeline.db"));
|
assert!(sk_content.contains("pipeline.db"));
|
||||||
assert!(sk_content.contains("*.db"));
|
assert!(sk_content.contains("*.db"));
|
||||||
|
// Runtime artifacts written under .huskies/ must be ignored too (story 1219)
|
||||||
|
assert!(sk_content.contains("merge_reports/"));
|
||||||
|
assert!(sk_content.contains("session_store.json"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ensure_gitignore_entries_retrofits_already_adopted_project() {
|
||||||
|
// Simulate a project scaffolded before merge_reports/ and session_store.json
|
||||||
|
// were added to the ignore list: .huskies/.gitignore exists but lacks them.
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
fs::create_dir_all(dir.path().join(".huskies")).unwrap();
|
||||||
|
fs::write(
|
||||||
|
dir.path().join(".huskies/.gitignore"),
|
||||||
|
"worktrees/\ncoverage/\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
ensure_gitignore_entries(dir.path()).unwrap();
|
||||||
|
|
||||||
|
let sk_content = fs::read_to_string(dir.path().join(".huskies/.gitignore")).unwrap();
|
||||||
|
assert!(sk_content.contains("merge_reports/"));
|
||||||
|
assert!(sk_content.contains("session_store.json"));
|
||||||
|
// Pre-existing entries must survive untouched
|
||||||
|
assert!(sk_content.contains("worktrees/"));
|
||||||
|
assert!(sk_content.contains("coverage/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user