Fix hero_docs new --path nesting #91
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_books#91
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Running
hero_docs new --name casper --path /tmp/caspercreates the project at/tmp/casper/casper/instead of/tmp/casper/, producing a redundant nested directory.Root cause
crates/hero_books_docusaurus/src/scaffold.rs:52-68always treats--pathas the parent directory and creates{base_path}/{name}/as the project root. This makes sense when--pathdefaults to., but not when the caller explicitly passes a path that already identifies the project root.The CLI help text in
src/bin/hero_docs.rs:22-51currently says:Desired behavior
--pathis explicitly provided, it is the project root itself. Collections and ebooks are created directly inside it; no extra{name}/level is added.--pathis omitted (default.), behavior remains: project files are created inside the current working directory (still no extra nesting).--namecontinues to be used for the collection name, ebook config name, and site title — not for directory creation.Acceptance criteria
hero_docs new --name casper --path /tmp/casperproduces/tmp/casper/{collections,ebooks,docusaurusbase}/...with no nestedcasper/casper/.hero_docs new --name casper(no--path) produces./{collections,ebooks,docusaurusbase}/...in the current directory.--forcebehavior still protects against overwriting a non-empty target.crates/hero_books_docusaurus/and the--helptext forhero_docs neware updated to match the new semantics.Scope
Small, one PR. Local to
crates/hero_books_docusaurus/src/scaffold.rsandsrc/bin/hero_docs.rsplus docs.Related
Blocks #2 (OpenRPC async docusaurus jobs) so the server exposes correct scaffold behavior.
Implementation Spec for Issue #91
Objective
Make
--paththe project root forhero_docs new(and the equivalentdocs.newRPC) so an explicit path no longer produces a redundant{name}/nesting inside it.Requirements
hero_docs new --name N --path Pcreates the project directly atP/(collections, ebooks, docusaurusbase as immediate children ofP).hero_docs new --name N(no--path) creates the project directly in the current working directory; no./N/directory is created.--namestill drives the collection name, ebook config name, and site title. It is no longer used for directory creation.docs.newRPC onhero_books_serverproduces the identical on-disk layout as the CLI for the same(name, path)inputs. Since the server shells out tohero_docs new, fixing the CLI/scaffold layer fixes the RPC. Verify no extra path manipulation inhandle_docs_newre-introduces nesting.--forcesemantics: the target directory (now equal to--path) may pre-exist; creation proceeds only if the directory is empty or--forceis set. A non-empty target without--forceis rejected.scaffold()signature becomes(name, project_root, force) -> DocusaurusResult<PathBuf>whereproject_rootis the directory that will directly containcollections/,ebooks/, anddocusaurusbase/. The return value remains the ebook directory (project_root/ebooks/{name}/).output_pathsurfaced bydocs.jobStatusis computed independently from the input hash (<docusaurus_cache>/<hash>/build) and does not consume the scaffold return value, so the fix does not touch it.Files to Modify/Create
crates/hero_books_docusaurus/src/scaffold.rs— rewritescaffold()to treat its path argument as the project root (not a parent); update doc comment, drop thebase_path.join(&name)line, rename the argument for clarity, update the non-empty/force check, and update all tests.src/bin/hero_docs.rs— updaterun_new()to pass the user-supplied path through as the project root; fix log messages that printbase_path.join(&args.name); update the--pathhelp text.crates/hero_books_server/src/web/rpc.rs— inhandle_docs_new, when the caller omitspath, default to<docusaurus_cache>/<input_hash>/(unique per-job subdirectory) so anonymous jobs do not collide. Add a verifying comment that the CLI scripthero_docs new --path Pnow produces the project directly insideP.crates/hero_books_docusaurus/README.md— document thenewsubcommand and its new--pathsemantics.Implementation Plan
Step 1: Rewrite
scaffold()to use path as project rootFiles:
crates/hero_books_docusaurus/src/scaffold.rsbase_path: &Pathtoproject_root: &Path.base_path.join(&name)computation and treatproject_rootitself as the root.fs::create_dir_all); (b) empty dir; (c) non-empty dir withforce = true. Reject non-empty dir withoutforcewith:"Target directory is not empty: {}. Use --force to overwrite.".namesanitization and empty-name check.Ok(ebooks_dir)whereebooks_dir = project_root.join("ebooks").join(&name).project_root/collections/...,project_root/ebooks/<name>/...).test_scaffold_uses_path_as_rootthat scaffolds intotmp/casperand assertstmp/casper/collections/casper/.collectionexists andtmp/casper/casper/does NOT exist.Dependencies: none
Step 2: Update the
hero_docs newCLIFiles:
src/bin/hero_docs.rs--pathhelp string to "Project directory to create (defaults to current directory)".base_pathtoproject_rootinsiderun_new().project_root.display()instead ofbase_path.join(&args.name).display().ebook_pathreturned by scaffold, whose contract (ebook directory path) is preserved.Dependencies: Step 1
Step 3: Harden the RPC path
Files:
crates/hero_books_server/src/web/rpc.rshandle_docs_new: when the caller omitspath, default to a unique<docusaurus_cache>/<input_hash>/subdirectory instead of the cache root directly. Order of operations:input_hashfrom the raw user inputs (includingpath.as_deref().unwrap_or("")).effective_path = path.unwrap_or_else(|| <docusaurus_cache>/<input_hash>).effective_pathwhen building the shell script.base_pathtoeffective_path.handle_docs_generateandhandle_docs_job_statusunchanged.Dependencies: Step 1
Step 4: Update README and
--helpFiles:
crates/hero_books_docusaurus/README.md### Subcommand: newsection with the synopsis, flag descriptions (--name,--path,--force, etc.), and an example:hero_docs new --name casper --path /tmp/caspercreates the project directly at/tmp/casper/.newandgenerate) and update the existing "Usage" block to showhero_docs generate <ebook_path>explicitly.Dependencies: Step 2
Acceptance Criteria
hero_docs new --name casper --path /tmp/caspercreates/tmp/casper/{collections,ebooks,docusaurusbase}/...with no nestedcasper/casper/.hero_docs new --name casper(no--path) creates./{collections,ebooks,docusaurusbase}/...in the current directory.docs.newwith{"name":"casper","path":"/tmp/casper"}produces the same layout as the CLI call above.output_pathreturned bydocs.jobStatus(<cache>/<hash>/build) still resolves correctly.--forcestill protects against overwriting a non-empty target.crates/hero_books_docusaurus/and CLI--helptext are updated.Notes
scaffold()signature keeps arity and types the same; only the semantics of the path argument change. Argument rename is cosmetic.{name}/nesting: without per-hash subdirs, every anonymousdocs.newwould collide at the cache root.Test Results
Build
cargo build -p hero_books_docusaurus -p hero_books_server— okhero_books_docusaurus (scaffold tests)
Total: 6
Passed: 6
Failed: 0
scaffold::tests::test_name_to_title — ok
scaffold::tests::test_scaffold_creates_structure — ok
scaffold::tests::test_scaffold_uses_path_as_root — ok
scaffold::tests::test_scaffold_fails_if_exists — ok
scaffold::tests::test_scaffold_collection_file_content — ok
scaffold::tests::test_scaffold_force_overwrites — ok
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 48 filtered out; finished in 0.01shero_books_server
test result: ok. 16 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03sFormat
cargo fmt --check— applied (diffs in hero_books_docusaurus/src/scaffold.rs and hero_books_server/src/web/rpc.rs were auto-formatted withcargo fmt)Implementation Summary
All four steps of the spec have been implemented on branch
development_fix_hero_docs_new_path_nesting.Changes
crates/hero_books_docusaurus/src/scaffold.rs—scaffold()now treats its path argument as the project root directly. No{name}/subdirectory is created at the top level. Non-empty target check with the error messageTarget directory is not empty: {path}. Use --force to overwrite.Return value (ebook directory) preserved. Five existing tests updated, new regression testtest_scaffold_uses_path_as_rootadded.src/bin/hero_docs.rs—--pathhelp text changed to "Project directory to create (defaults to current directory)". Localbase_pathrenamed toproject_root. The "Project created at" log line now prints the path directly rather thanpath/name.crates/hero_books_server/src/web/rpc.rs—handle_docs_newnow defaults thepathparameter to<docusaurus_cache>/<input_hash>/(unique per-job subdirectory) when the caller omits it. Prevents concurrent anonymousdocs.newjobs from colliding at the shared cache root.handle_docs_generateandhandle_docs_job_statusuntouched.crates/hero_books_docusaurus/README.md— Added### Subcommand: newsection with synopsis, flag descriptions, example, and a "Behavior change" note. Added### Subcommand: generatesection. Updated the Usage and Examples blocks to show the subcommand structure explicitly.Test Results
cargo build -p hero_books_docusaurus -p hero_books_server— ok.hero_books_docusaurusscaffold tests: 6/6 passed.hero_books_serverlibrary tests: 16/16 passed.cargo fmt --check— clean aftercargo fmtwas applied to the two modified crates.Notes
output_pathreturned bydocs.jobStatusis still<cache>/<hash>/buildand is unaffected.scaffold()function signature keeps the same arity and types; only the semantics of the path argument changed, and the argument was renamed toproject_rootfor clarity.Pull request opened: #95
This PR implements the changes discussed in this issue.