The Aspire toolchain is bundled as a local dotnet tool — no workload install required.
dotnet tool restoreIf you only need to validate rendering or authoring changes, the Docker setup removes the .NET/Node prerequisite:
./dev.sh build # build images (once, or after Dockerfile changes)
./dev.sh isolated-serve # serve docs with hot reload at http://localhost:3000
./dev.sh test # run the full unit-test suiteThe same setup runs the assembled site and the codex. All three modes use the same verbs
(<mode>-serve, <mode>-build, <mode>-watch, and <mode>-clone where the mode clones repositories):
| Mode | Serves | URL | Commands |
|---|---|---|---|
isolated |
One docset (docs/) |
http://localhost:3000 | isolated-serve, isolated-build |
assembler |
Every repository under one navigation | http://localhost:4000 | assembler-clone, assembler-build, assembler-serve, assembler-watch |
codex |
The codex portal | http://localhost:4001 | codex-clone, codex-build, codex-serve, codex-watch |
./dev.sh assembler-clone # clone the repositories (first run, needs network)
./dev.sh assembler-watch # rebuild and reload on every source changeNotes for the assembler and codex modes:
- They run from source in the tooling image, so local code changes apply without a publish.
- Builds are static and the site is down while one runs.
-watchrebuilds on each change tosrc/,config/, ordocs/, then reloads open pages. - Clones and build output live in named volumes.
./dev.sh cleanremoves them. - The image has no
ssh, sodev.shclones over HTTPS. Private repositories needGITHUB_TOKENor a logged-ingh. - The serve commands publish a fixed port and reject
--port. codexreads its config fromCODEX_CONFIG(defaultconfig/codex.example.yml).- The assembler builds the API Explorer pages. Set
FEATURE_ASSEMBLER_API_EXPLORER=falseto skip them and build faster. After changing it, run./dev.sh assembler-build --no-assume-buildonce, because the build stamp ignores environment variables.
Run ./dev.sh help for the full list of commands. Docker files live under build/dev/.
dotnet aspire runWill spin up all our services and clone and build all the documentation sets.
By default, private repositories are skipped, existing clones are reused, and the build is skipped when code/config/content is unchanged. To opt out of any default:
dotnet aspire run -- --no-skip-private-repositories # include private repos (requires auth tokens)
dotnet aspire run -- --no-assume-cloned # force a fresh clone
dotnet aspire run -- --no-assume-build # force a full rebuild even if stamp matches--skip-private-repositories (default) injects docs-builder's own docs into the navigation
so you can validate new features without production credentials.
Our Integration Tests use these defaults to validate the assembler builds.
Tip: Install the Aspire skills plugin for day-to-day orchestration and monitoring support in Claude Code:
/plugin marketplace add microsoft/aspire-skills /plugin install aspire@aspire-skills
Two watch loops are available depending on what you're working on:
./build.sh watchMonitors code, Razor template files, and frontend assets for a single documentation set.
Markdown files refresh via live reload without a recompile. Code or layout changes relaunch
the server automatically. Web assets are rebuilt by vite build --watch in the background.
./build.sh watchallRuns dotnet watch on the Aspire AppHost. When you edit any .cs file the AppHost restarts,
the assembler clone step is skipped (default-on --assume-cloned), and the build step
consults the MVID stamp — if code actually changed it rebuilds; otherwise it skips in seconds.
No live reload; the site comes back on port 4000 after each restart.
When you add or change CLI options in src/tooling/docs-builder/Commands/, regenerate the checked-in schema so CI and the published CLI reference stay in sync:
dotnet run --project src/tooling/docs-builder -- __schema > docs/cli-schema.jsonCommit the updated docs/cli-schema.json with your code changes. CI compares the schema (ignoring the version field) and fails if it has drifted.
The schema drives auto-generated parameter tables and usage synopses on CLI reference pages. For behavior, workflows, and examples that the schema cannot express, also update supplemental files under docs/cli/ (see Writing supplemental content).
Before starting implementation on anything non-trivial — a new directive, a CLI command, a change that touches more than one project — write a short plan first: a few paragraphs (in the PR description, a linked issue, or handed to an agent as a prompt) stating what's changing and an explicit definition of done. This isn't bureaucracy for its own sake: it's what lets a change be scoped to a single sitting/session instead of sprawling as the "actual" requirements get discovered mid-implementation. Trivial fixes (typos, one-line bug fixes, dependency bumps) don't need this.
This section outlines the process for releasing a new version of this project.
This project uses Semantic Versioning and its version is
automatically determined by release-drafter
based on the labels of the pull requests merged into the main branch.
See the release-drafter configuration for more details.
This repository uses Husky.Net to automatically format and validate code before commits.
Pre-commit hooks (run on staged files):
- dotnet-lint - Checks C# formatting with curb (
dotnet curb check .) - prettier - Formats TypeScript, JavaScript, CSS, and JSON files in
src/Elastic.Documentation.Site/ - typescript-check - Type checks TypeScript files with
tsc --noEmit(only if TS files are staged) - eslint - Lints and fixes JavaScript/TypeScript files
Husky.Net is included as a dotnet tool. After cloning the repository, restore the tools:
dotnet tool restoreThen install the git hooks:
dotnet husky installThat's it! The hooks will now run automatically on every commit.
The pre-commit hook runs dotnet tool restore before curb, so a new worktree does not need a manual restore before the first commit. You still need dotnet husky install in that worktree so .husky/_/husky.sh exists.
Once installed, hooks run automatically when you commit:
git add .
git commit -m "your message" # Pre-commit hooks run hereNote: If hooks modify files (prettier, eslint), the commit will fail so you can review the changes. Simply stage the changes and commit again:
git add -u
git commit -m "your message"If the dotnet-lint hook fails, run dotnet curb format . to fix the formatting and commit the fixes.
You can test hooks without committing:
# Run all pre-commit tasks
dotnet husky run --group pre-commit
# Test individual tasks
dotnet husky run --name dotnet-lint
dotnet husky run --name prettier
dotnet husky run --name typescript-check
dotnet husky run --name eslintHook configuration is defined in .husky/task-runner.json. See the Husky.Net documentation for more details.
To create a new release trigger the release workflow on the main branch.
Every time a pull request is merged into the main branch, release-drafter will
create a draft release or update the existing draft release in the Releases page.
To create a new release you need to publish the existing draft release created by release-drafter.
Important
Make sure the release-drafter workflow is finished before publishing the release.
Note
When a release is published, the create-major-tag workflow
will force push a new major tag in the format vX where X is the major version of the release.
For example, if the release is 1.2.3 was published, the workflow will force push a new tag v1 on the same commit.