story-kit: start 65_story_standardised_script_test_entry_point_for_all_projects
This commit is contained in:
@@ -379,6 +379,8 @@ To support both Remote and Local models, the system implements a `ModelProvider`
|
||||
* Shell commands that modify state (non-readonly) should ideally require a UI confirmation (configurable).
|
||||
* File writes must be confirmed or revertible."#;
|
||||
|
||||
const STORY_KIT_SCRIPT_TEST: &str = "#!/usr/bin/env bash\nset -euo pipefail\n\n# Add your project's test commands here.\n# Story Kit agents invoke this script as the canonical test runner.\n# Exit 0 on success, non-zero on failure.\necho \"No tests configured\"\n";
|
||||
|
||||
/// Walk from `start` up through parent directories, returning the first
|
||||
/// directory that contains a `.story_kit/` subdirectory, or `None`.
|
||||
pub fn find_story_kit_root(start: &Path) -> Option<PathBuf> {
|
||||
@@ -468,6 +470,24 @@ fn write_file_if_missing(path: &Path, content: &str) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write `content` to `path` if missing, then ensure the file is executable.
|
||||
fn write_script_if_missing(path: &Path, content: &str) -> Result<(), String> {
|
||||
write_file_if_missing(path, content)?;
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mut perms = fs::metadata(path)
|
||||
.map_err(|e| format!("Failed to read permissions for {}: {}", path.display(), e))?
|
||||
.permissions();
|
||||
perms.set_mode(0o755);
|
||||
fs::set_permissions(path, perms)
|
||||
.map_err(|e| format!("Failed to set permissions on {}: {}", path.display(), e))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scaffold_story_kit(root: &Path) -> Result<(), String> {
|
||||
let story_kit_root = root.join(".story_kit");
|
||||
let specs_root = story_kit_root.join("specs");
|
||||
@@ -475,17 +495,21 @@ fn scaffold_story_kit(root: &Path) -> Result<(), String> {
|
||||
let functional_root = specs_root.join("functional");
|
||||
let stories_root = story_kit_root.join("stories");
|
||||
let archive_root = stories_root.join("archive");
|
||||
let script_root = root.join("script");
|
||||
|
||||
fs::create_dir_all(&tech_root).map_err(|e| format!("Failed to create specs/tech: {}", e))?;
|
||||
fs::create_dir_all(&functional_root)
|
||||
.map_err(|e| format!("Failed to create specs/functional: {}", e))?;
|
||||
fs::create_dir_all(&archive_root)
|
||||
.map_err(|e| format!("Failed to create stories/archive: {}", e))?;
|
||||
fs::create_dir_all(&script_root)
|
||||
.map_err(|e| format!("Failed to create script/ directory: {}", e))?;
|
||||
|
||||
write_file_if_missing(&story_kit_root.join("README.md"), STORY_KIT_README)?;
|
||||
write_file_if_missing(&specs_root.join("README.md"), STORY_KIT_SPECS_README)?;
|
||||
write_file_if_missing(&specs_root.join("00_CONTEXT.md"), STORY_KIT_CONTEXT)?;
|
||||
write_file_if_missing(&tech_root.join("STACK.md"), STORY_KIT_STACK)?;
|
||||
write_script_if_missing(&script_root.join("test"), STORY_KIT_SCRIPT_TEST)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1027,6 +1051,24 @@ mod tests {
|
||||
assert!(dir.path().join(".story_kit/specs/00_CONTEXT.md").exists());
|
||||
assert!(dir.path().join(".story_kit/specs/tech/STACK.md").exists());
|
||||
assert!(dir.path().join(".story_kit/stories/archive").is_dir());
|
||||
assert!(dir.path().join("script/test").exists());
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn scaffold_story_kit_creates_executable_script_test() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let script_test = dir.path().join("script/test");
|
||||
assert!(script_test.exists(), "script/test should be created");
|
||||
let perms = fs::metadata(&script_test).unwrap().permissions();
|
||||
assert!(
|
||||
perms.mode() & 0o111 != 0,
|
||||
"script/test should be executable"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user