feat(tooling): migrate docs-builder CLI from ConsoleAppFramework to Nullean.Argh#3202
feat(tooling): migrate docs-builder CLI from ConsoleAppFramework to Nullean.Argh#3202
Conversation
…rk to Nullean.Argh 0.7.0 Replaces ConsoleAppFramework with Nullean.Argh.Hosting in docs-builder and Nullean.Argh (standalone) in the aspire AppHost. Key improvements: proper namespace hierarchy (assembler, codex, etc.), record binding via [AsParameters] for the shared ElasticsearchIndexOptions, ICommandMiddleware replacing filter chain, GlobalCliOptions with --log-level/--config-source/--skip-private-repositories as typed global flags, and docs-builder assemble as a hoisted root command. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Create IsolatedBuildOptions, AssemblerBuildOptions, and AssemblerCloneOptions records in the service layer. Update all six affected service methods to accept these DTOs and the existing ElasticsearchIndexOptions directly, eliminating the tuple-based state unpacking in ServiceInvoker lambdas. Commands are now thin wrappers that construct a DTO and pass it straight through. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…m> in DTOs
- Bump Nullean.Argh + Nullean.Argh.Hosting to 0.8.0
- Delete ExporterParser: IReadOnlySet<Exporter>? now bound natively by argh
- IsolatedBuildOptions and AssemblerBuildOptions include Exporters via repeated
flags (--exporters Html --exporters Elasticsearch); [CollectionSyntax(Separator=",")]
deferred until argh fixes [AsParameters] + IReadOnlySet<Enum> interaction
- IsolatedBuildCommand.Build and AssemblerCommands.Build use [AsParameters] DTO
- [CommandName("build")] + [DefaultCommand] ready for MapAndRootAlias once the
generator bug (missing { on subsequent methods) is fixed in argh
- Map<IsolatedBuildCommand>() used in place of MapAndRootAlias<> for now
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…s-builder build
- Bump to 0.8.1 (fixes MapAndRootAlias generator bug — missing { on subsequent methods)
- Switch Map<IsolatedBuildCommand>() → MapAndRootAlias<IsolatedBuildCommand>():
docs-builder build is now both a named command and the root default alias
- IsolatedBuildOptions and AssemblerBuildOptions include IReadOnlySet<Exporter>?
(repeated flags: --exporters Html --exporters Elasticsearch)
- [CollectionSyntax(Separator=",")] deferred: [AsParameters] + [CollectionSyntax]
still emits a stray closing brace in 0.8.1 generated code
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Bump to 0.9.0 (fixes [AsParameters] + [CollectionSyntax] stray-brace generator bug) - Re-enable [CollectionSyntax(Separator=",")] on IsolatedBuildOptions.Exporters and AssemblerBuildOptions.Exporters: --exporters Html,Elasticsearch now works - Add ArghApp.TryArghIntrinsicCommand(args) pre-host fast path: --help, --version, __schema and __completion no longer trigger host construction or startup logs - Nullean.Argh.Interfaces added to Isolated and Assembler service projects Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
ElasticsearchIndexOptions:
- Endpoint/ProxyAddress: string? -> Uri? with [UriScheme("http","https")]
- BootstrapTimeout: int? (minutes) -> TimeSpan? with [TimeSpanRange("1s","60m")]
- [Range] on SearchNumThreads, IndexNumThreads, BufferSize, MaxRetries
- Rename NoAiEnrichment -> AiEnrichment, NoEis -> Eis (positive flags,
clean --no-ai-enrichment / --no-eis negation; removes double-negative)
- Full XML docs on all properties
XML documentation:
- IsolatedBuildOptions, AssemblerBuildOptions: XML docs on all properties
- Namespace class summaries and remarks: assembler (navigation.yml unified site),
codex (independent per-set navigation), inbound-links (link registry concept)
- All assembler, codex, and inbound-links commands: complete <summary> and
<remarks> explaining concepts (assembler, codex, link registry, bloom filter),
ordering requirements, and usage examples
- Root commands (build, index, diff): <summary> tightened, <remarks> added
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…pace summaries - ChangelogCommands: add class-level summary; rewrite all method summaries to one terse sentence; move detail to <remarks>; remove <code> invocation blocks - All commands: remove <code> CLI invocation examples from <remarks> throughout - GlobalCliOptions: remove manual enum listing from --log-level summary (argh renders allowed values automatically) - AssemblerBuildOptions, IsolatedBuildOptions: add XML docs to properties (visible in IDE; argh cross-assembly doc limitation tracked separately) - Nested assembler namespaces: add class-level <summary> to BloomFilterCommands, ConfigurationCommand, ContentSourceCommands, DeployCommands, NavigationCommands so they appear in assembler --help listings - ServeCommand, MoveCommand, FormatCommand, IndexCommand: tighten summaries and move detail to <remarks> without code blocks Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
… docs from referenced projects PR #25 in 0.9.1 adds XML doc fallback for [AsParameters] DTO members in referenced assemblies. Enable GenerateDocumentationFile on Isolated, Assembler, and Configuration service projects so argh's generator can read property summaries from the produced .xml files. Result: docs-builder build --help, assembler build --help, assembler index --help etc. now show full descriptions for every [AsParameters] DTO property (IsolatedBuildOptions, AssemblerBuildOptions, ElasticsearchIndexOptions). Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…alBaseUrl as Uri?
- IsolatedBuildOptions.CanonicalBaseUrl: string? -> Uri? with [Url]
(argh maps [Url] on Uri? to [UriScheme("http","https")] constraint)
- IsolatedBuildService.Build: remove manual Uri.TryCreate; use the
already-validated Uri? from argh binding with ?? default fallback
- ElasticsearchIndexOptions.Endpoint/ProxyAddress: [UriScheme("http","https")]
-> [Url] for brevity (equivalent per argh generator)
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…ectoryInfo
- ElasticsearchIndexOptions.CertificatePath: string? -> FileInfo? [FileExtensions("pem,der,crt,cer")]
- IsolatedBuildOptions.Path, Output: string? -> DirectoryInfo?
- Codex config params (all four commands): string -> FileInfo [FileExtensions("yml,yaml")]
- Codex output params: string? -> DirectoryInfo?
- DeployCommands: planFile -> FileInfo [json], @out -> FileInfo?, redirectsFile -> FileInfo? [json]
- CodexUpdateRedirectsCommand.redirectsFile: string? -> FileInfo? [json]
- NavigationCommands.ValidateLinkReference file: string? -> FileInfo? [json]
- InboundLinkCommands.ValidateLinkReference file: string? -> FileInfo? [json]
- BloomFilterCommands.Create builtDocsDir: string -> DirectoryInfo
- AssemblerCommands.Serve path: string? -> DirectoryInfo?
- ServeCommand path: string? -> DirectoryInfo?
- ChangelogCommand.Init path/changelogDir/bundlesDir: string? -> DirectoryInfo?
- ChangelogCommand config params (Add, Bundle, Remove, Render, GhRelease, Upload, EvaluatePr): string? -> FileInfo? [yml,yaml]
- ChangelogCommand directory params: string? -> DirectoryInfo?
- ChangelogCommand.BundleAmend bundlePath: string -> FileInfo [yml,yaml]
- Help text: path/file/dir params now show as <dir>, <file>, <path> with extension hints
Tilde expansion report (params needing future argh [ExpandTilde] attribute):
changelog init path/changelogDir/bundlesDir, all config params, all directory
params, assembler deploy planFile/@out, CertificatePath, build Path/Output.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…tSymbolicLinks], [ExpandUserProfile] - Bump Nullean.Argh, Nullean.Argh.Hosting, Nullean.Argh.Interfaces to 0.11.0 - [Existing] on all input FileInfo/DirectoryInfo params that must be present: CertificatePath, all codex config files, changelog config/bundlePath, deploy planFile/redirectsFile, navigation/inbound-links file, bloom-filter builtDocsDir, serve path params, IsolatedBuildOptions.Path (source dir must exist) - [RejectSymbolicLinks] on every FileInfo/DirectoryInfo param and property - [ExpandUserProfile] on every FileInfo/DirectoryInfo param and property (fixes ~/path expansion for all path arguments) - IsolatedBuildOptions.Output intentionally omitted from [Existing] (created by build) - Codex and changelog output directories omitted from [Existing] (created by commands) Help output now shows: [existing] [no symlinks] [expand ~ profile] on appropriate args. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Fixes: - PR #28: DTO XML summaries now render in --help; global options show short flag first (e.g. -l, --log-level) - PR #29: enum choices listed lowercase in help (html, elasticsearch, ...) - PR #30: [Existing] on optional nullable FileInfo?/DirectoryInfo? no longer throws ArgumentNullException when the flag is omitted - PR #31: MapAndRootAlias root help and leading flag handling fixed Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…Parameters] Argh 0.12.0 correctly handles IReadOnlySet<T> in [AsParameters] DTOs but initialises the collection to an empty HashSet (not null) when no --exporters flags are supplied. The previous `??=` null-coalesce never fired on an empty set, so IsolatedBuildService and AssemblerBuildService ran with zero exporters (producing no output, no links.json). Replace `??=` with an explicit count-based guard in both services. Also enable [CollectionSyntax(Separator=",")] on IsolatedBuildOptions.Exporters now that the [AsParameters] + [CollectionSyntax] generator bug is fixed in 0.12.0. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…ed code 0.12.1 (PR #33) fixed IReadOnlySet<T>? defaulting to null when omitted in [AsParameters] DTOs, but the generator emits the nullable null-return into a non-nullable typed local variable (CS8600 / missing ? on the declaration), making the project unbuildable. 0.12.2 does not fix this. Pin to 0.12.0 and keep the is not { Count: > 0 } guard as a workaround until the generator bug is resolved upstream. Remove [CollectionSyntax] comments from DTOs (they were only relevant to the broken [AsParameters]+[CollectionSyntax] combination which is now fixed in 0.12.0 anyway). Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Fixes the CS8600 generator bug introduced in 0.12.1 where IReadOnlySet<T>? in
[AsParameters] DTOs was emitted with a non-nullable local type. Remove the
is not { Count: > 0 } workaround — ??= is correct again. Re-enable
[CollectionSyntax(Separator=",")] on IsolatedBuildOptions and AssemblerBuildOptions.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…s in help, AGH0033)
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (51)
✨ Finishing Touches✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 6 minutes and 22 seconds.Comment |
Why
docs-builderused ConsoleAppFramework as its CLI layer. Two fundamental problems made it worth replacing:1. Help output was nearly useless.
CAF rendered a flat list of flags with no descriptions, no namespace grouping, and no validation hints. There was no way to express that
--endpointrequires an http/https URI, that--plan-filemust exist on disk, or that--exporters Html,Elasticsearchis a comma-separated collection. Contributors had to read source code to understand what a command accepted.2. Namespace support was a string hack.
Commands were registered with prefixes like
"assembler content-source"— a flat simulation of hierarchy. There was no realassembler --helppage that showed only assembler sub-commands, no per-namespace help, and no way to document what theassemblernamespace is.What
This PR replaces ConsoleAppFramework with Nullean.Argh (0.11.0), a Roslyn source-generator CLI framework that is AOT/trim-safe by design.
Real namespace hierarchy
assembler,assembler deploy,assembler navigation,codex,changelogetc. are now first-class namespaces.docs-builder assembler --helpshows only assembler commands. Each namespace has a one-liner summary visible in the parent listing.Help text driven by XML docs
<summary>becomes the terse one-liner.<remarks>becomes the explanatory block. Param docs flow directly into flag descriptions. The migration invested in complete XML documentation for every command, with concepts like assembler, codex, link registry, and bloom filter explained inline for contributors unfamiliar with the infrastructure.--helpno longer emits host startup logs — argh 0.9.0+ detects intrinsic commands (--help,--version,__schema,__completion) at registration time and exits before the host is built.Typed, validated parameters
URIs —
--endpointand--proxy-addressareUri?with[Url](DataAnnotations); argh rejects non-http/https values at parse time.Timeouts —
--bootstrap-timeoutisTimeSpan?with[TimeSpanRange("1s","60m")]. Users pass4mor90s, not a raw integer.Thread / buffer counts —
[Range(1,128)]on thread counts,[Range(1,10_000)]on buffer size,[Range(0,20)]on retries.File and directory paths — path parameters are
FileInfoorDirectoryInfo(notstring). Help shows<file>or<dir>. Where a file must exist before the command runs (config files, cert paths, plan files, links.json),[Existing]is applied and argh rejects at parse time rather than deep inside service code.[RejectSymbolicLinks]is on every path parameter.[ExpandUserProfile]means~/pathworks everywhere.File extensions —
[FileExtensions(Extensions="yml,yaml")]on config file arguments,[FileExtensions(Extensions="json")]on JSON inputs,[FileExtensions(Extensions="pem,der,crt,cer")]on certificate paths.Exporters — previously a custom
ExporterParserwith its own vocabulary (es,config,links…). Now nativeIReadOnlySet<Exporter>binding — argh parses and validates the values, and lists the allowed set in help. Enum parsing is case-insensitive.DTOs replace flat parameter lists
Service methods previously took 12–22 individual
string?/bool?/int?parameters. This PR introducesIsolatedBuildOptions,AssemblerBuildOptions,AssemblerCloneOptions, and the already-existingElasticsearchIndexOptionsas proper records that:[AsParameters](one line in the command method)docs-builder buildas a named commanddocs-builder(no sub-command) still builds the current documentation set.docs-builder buildnow also works as an explicit named command (MapAndRootAlias).assembleconsolidationThe old
assemble(one-shot) andassembler(individual steps) namespace split is clarified:docs-builder assembleruns config-init + clone + build in one shot;docs-builder assembler clone/build/serve/index/sitemapare the individual steps.Breaking changes
--no-ai-enrichment--no-ai-enrichment(via--ai-enrichment/--no-ai-enrichment)--no-eis--eis/--no-eis--bootstrap-timeout 4(minutes as int)--bootstrap-timeout 4m(TimeSpan string)docs-builder assemble(old one-shot)docs-builder assemble(unchanged behaviour, new name in help)docs-builder codex <config>positional wasstringFileInfo— path must resolve to a real fileExporter names (
html,es,config,links) are not breaking —Enum.TryParseis case-insensitive sohtmlandHtmlboth work.argh bugs found and fixed upstream
During this migration five bugs were found and fixed in Nullean.Argh (0.7→0.11):
--help{identifier}in XML doc text emitted as C# interpolation holes in generated codebool?+bool no<Name>generating duplicate--no-*switch arms (CS8510)__evreused across multiple enum properties in the same options type (CS0136)[AsParameters]DTO property<summary>XML docs not rendered in--helpfor types in referenced assembliesOne known bug remains open:
[Existing]on a nullableFileInfo?/DirectoryInfo?fires its existence check even when the value is absent (null), causingArgumentNullExceptioninside the generated runner.Test plan
dotnet build— 0 errors, 0 warningsdocs-builder --help— root help, no startup logs,(default: build)alias showndocs-builder build --help— all options have descriptions,[existing]on--pathdocs-builder assembler --help— namespace summaries for all sub-namespacesdocs-builder assembler index --help— ES options show[schemes: http|https],[time-span-range],[range]hintsdocs-builder assembler deploy apply --help—--plan-fileshows[existing]docs-builder assembler deploy apply staging bucket missing.json— argh rejects with "path does not exist"docs-builder index --endpoint not-a-url— argh rejects with scheme errordocs-builder build --bootstrap-timeout bad— argh rejects with time-span errordocs-builder codex --help— codex concept explained in namespace summarydocs-builder changelog --help— all sub-command summaries are one terse line