Conversation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds Docker containerization and a multi-stage Dockerfile; refactors Chronicle CLI to accept explicit content directory flags, resolves content dir via new helper, invokes Next binary directly, forwards lifecycle signals, and introduces a tsup-based CLI build configuration. Also lowers Node engine requirement to >=22 and updates pnpm. Changes
Sequence Diagram(s)mermaid Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@Dockerfile`:
- Around line 21-38: The Dockerfile's runner stage is running as root; update
the runtime stage to switch to the non-root node user (use USER node) after
creating and setting ownership/permissions for the app and mounted content
directories so they are writable by that user; ensure the symbolic link creation
(ln -s /app/packages/chronicle/bin/chronicle.js /usr/local/bin/chronicle),
VOLUME /app/content, and ENTRYPOINT/CMD still work under USER node by chown -R
node:node /app and chown the mounted content path (/app/content) before
switching users and then set appropriate permissions (e.g., chmod) so the node
user can execute bin/chronicle.js and write to /app and /app/content.
In `@packages/chronicle/src/cli/commands/dev.ts`:
- Around line 7-24: The Windows shim in node_modules/.bin isn't executable as-is
on Windows; update the binary resolution used by nextBin (and equivalents in
build.ts and start.ts) to choose the platform-specific file: compute the base
path using PACKAGE_ROOT and then if process.platform === 'win32' append '.cmd'
(or test for the existence of the .cmd file) before calling spawn in
devCommand.action (the variable nextBin and the spawn(...) invocation), so spawn
receives the correct executable on Windows.
In `@packages/chronicle/src/cli/utils/config.ts`:
- Around line 13-17: The function resolveContentDir inconsistently resolves
paths: it calls path.resolve(contentFlag) for contentFlag but returns
process.env.CHRONICLE_CONTENT_DIR unchanged; update resolveContentDir to
normalize the env var as well by passing process.env.CHRONICLE_CONTENT_DIR
through path.resolve (or path.resolve(process.env.CHRONICLE_CONTENT_DIR ||
process.cwd())) so the returned path is always absolute whether coming from
contentFlag, CHRONICLE_CONTENT_DIR, or falling back to process.cwd(); adjust
only resolveContentDir and reference contentFlag and CHRONICLE_CONTENT_DIR
accordingly.
In `@packages/chronicle/tsup.config.ts`:
- Around line 5-16: The build currently injects PACKAGE_ROOT via tsup's define
(in tsup.config.ts using defineConfig and PACKAGE_ROOT), embedding the build
machine's absolute path; instead remove PACKAGE_ROOT from tsup define and change
the runtime code that relies on PACKAGE_ROOT (e.g., code in src/cli that
references PACKAGE_ROOT) to compute the package root at runtime using
import.meta.url and fileURLToPath (or an ESM-compatible __dirname fallback) so
lookups like node_modules/.bin/next resolve correctly when the package is
installed; update tsup.config.ts to stop defining PACKAGE_ROOT and implement the
runtime resolution where PACKAGE_ROOT was used.
🧹 Nitpick comments (3)
packages/chronicle/src/cli/commands/start.ts (1)
16-17: Unusedconfigvariable.The
configis destructured fromloadCLIConfigbut never used. If the intent is solely to validate thatchronicle.yamlexists (sinceloadCLIConfigexits on missing config), consider adding a comment or usingloadCLIConfig(contentDir)without destructuring.🔧 Suggested change
- const { config } = loadCLIConfig(contentDir) + loadCLIConfig(contentDir) // validates chronicle.yaml existspackages/chronicle/src/cli/commands/build.ts (2)
15-16: Same issue: unusedconfigvariable.Same as in
start.ts- theconfigis destructured but not used. If this is intentional validation, consider documenting it.🔧 Suggested change
- const { config } = loadCLIConfig(contentDir) + loadCLIConfig(contentDir) // validates chronicle.yaml exists
29-32: Consider extracting shared lifecycle handling.The signal forwarding and exit code handling pattern is duplicated across
build.ts,start.ts, and likelydev.ts. While acceptable for CLI commands, you could optionally extract this into a shared utility.// In utils/process.ts export function attachLifecycleHandlers(child: ChildProcess) { child.on('close', (code) => process.exit(code ?? 0)) process.on('SIGINT', () => child.kill('SIGINT')) process.on('SIGTERM', () => child.kill('SIGTERM')) }
- Add non-root user (node) in Dockerfile runner stage - Use platform-specific next binary (.cmd on Windows) - Normalize env var path in resolveContentDir - Remove unused config destructuring in command files - Extract shared signal handling to attachLifecycleHandlers util Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
--contentflag,PACKAGE_ROOTbuild-time constant, and directnextbinary invocationtsupfor CLI bundling withbuild:cliscriptChanges
Docker
.dockerignore: Excludesnode_modules,.next,.source,.git/app/content, symlinked into project root to stay within Turbopack's filesystem boundaryCHRONICLE_CONTENT_DIR=./contentenv var resolves through symlinkCLI
--contentflag ondev,build,startcommands (replaces config-based content dir resolution)PACKAGE_ROOTinjected at build time viatsup define— eliminates brittle__dirname + ../../..relative pathnextbinary (node_modules/.bin/next) instead ofnpxwithshell: true— fixes signal handling (Ctrl+C)SIGINT/SIGTERMproperly forwarded to child Next.js processinitcommand: Creates flat content dir structure (chronicle.yaml+index.mdxin same dir)Build
tsup.config.ts:target: node22,define: { PACKAGE_ROOT }, ESM outputbuild:cliscript: Added topackage.jsontsupadded as devDependencyCleanup
contentDirfromChronicleConfigtype andchronicle.yamlgetConfigPathutilitypnpm@10,node >=22Usage
Test plan
chronicle initcreates correct structure🤖 Generated with Claude Code