Skip to content

Replace docs JSON-RPC methods with bicep/renderDocs and bicep/getDocsModel - #20209

Closed
Jared Holgate (jaredfholgate) wants to merge 2 commits into
mainfrom
jaredfholgate-docs-rpc-surface-rework
Closed

Jared Holgate (jaredfholgate) wants to merge 2 commits into
mainfrom
jaredfholgate-docs-rpc-surface-rework

Conversation

@jaredfholgate

@jaredfholgate Jared Holgate (jaredfholgate) commented Aug 20, 2026

Copy link
Copy Markdown
Member

Description

Replaces the two experimental docs JSON-RPC methods with better-shaped ones, and marks them experimental.

Now After
bicep/generateDocs (writes files, takes Paths[]) bicep/renderDocs — returns rendered content, never writes
bicep/outputDocs (returns content, takes single Path) bicep/getDocsModel — returns the typed documentation model as JSON

Follow-up to #20183, which introduced these methods. The RPC surface was questioned during review.

Why

No other RPC method writes to disk. The only file-write call in CliJsonRpcServer.cs was inside GenerateDocs. bicep/compile is the RPC counterpart of bicep build, and it deliberately returns Contents via a StringWriter rather than writing main.json. The established convention is that RPC returns content and the client owns the filesystemgenerateDocs broke it.

The CLI merged docs output into docs generate --stdout, so two RPC methods split by output destination had become a stale echo of a command that no longer exists. Splitting by what is returned (rendered text vs typed model) is meaningful; splitting by where it goes is not.

outputDocs was misnamed. "Output" describes a destination, not an operation, and appeared nowhere else in the protocol. render matches compile/format as a transformation verb and is already the codebase's own term (IBicepDocumentationGenerator.Render).

getDocsModel fills a real gap and follows the get* family (getMetadata, getSnapshot, getDeploymentGraph, getFileReferences). GetMetadataResponse is the precedent for returning a structured model, but it is much shallower — name, type name, description, range. The docs model adds allowed values, numeric and length bounds, patterns, nested properties, discriminator cases, exported types/variables/functions, cross-referenced modules and usage examples. Previously a tool wanting that data had to render Markdown and parse it back.

Breaking change to ICliJsonRpcProtocol

ICliJsonRpcProtocol carries a stability guarantee as of Bicep 0.29, so this is called out deliberately. It is justified because the bicep docs feature is experimental and has no known RPC consumers:

  • GitHub code search for bicep/generateDocs / bicep/outputDocs returns 5 results. Four are in this repo; the fifth is Azure/bicep-reps active/0025-module-documentation-generation.md, the design proposal — not a consumer.
  • The AVM verifier drives the CLI, not RPC.
  • The feature merged on 2026-08-18 and has not appeared in a release, so adoption is nil.

The conflicting stability promises are now resolved explicitly: both methods and all docs-specific records carry <remarks> stating they may change while the bicep docs feature remains experimental, notwithstanding the guarantee for the rest of the interface.

Paths array is a deliberate deviation

Every other RPC method takes a single Path. These two take Paths[], for these reasons:

  • Bulk is the driving use case — AVM runs over 573 modules, and one round trip beats 573.
  • The shared ActiveSourceFileSet optimisation depends on batching, so a batch reuses parsed source files across modules.
  • generateDocs already established the precedent.
  • Keeping the two docs methods symmetric with each other matters more than matching the single-path family.

Both return one result per requested path in request order, and a failure for one path does not prevent the others from being processed.

getDocsModel and configuration

getDocsModel takes no template options — the model is built before rendering, so templateFile/templateRoot/custom values are meaningless there. It does still resolve options from bicepconfig.json, because configuration shapes the model itself:

  • BuildModel(compilation, customValues, ct) passes default DocumentationExamples, whereas bicep docs generate resolves them from config. Without this, a repo setting "examples": { "sources": [] } would get different usage examples from getDocsModel than from renderDocs.
  • documentation.template.values populates the model's custom field.

So the returned model matches what renderDocs and the CLI render from. Configuration continues to be resolved independently for each requested file, so one batch may mix modules with different templates, custom values and example settings.

Included fixes

Version gate corrected from 0.46.0 to 0.47.0. v0.46.0 and v0.46.1 both released on 2026-07-30, and the docs commit (0761a86b11a0) is 38 commits ahead of v0.46.1 — the docs methods shipped in neither. A client on v0.46.1 passed EnsureMinimumVersion and then failed with "method not found".

BaselineHelper.GetRepoRoot() now accepts a .git file. It searched for a .git directory, but in a linked git worktree .git is a file containing a gitdir: pointer. The static initialiser threw, so every baseline test failed with TypeInitializationException — including PublicApiTests, which made it impossible to regenerate Azure.Bicep.RpcClient.txt via SetBaseLine=true. One line, and it unblocks ~132 baseline tests for anyone working in a worktree.

Dead code removed. OutputWriter existed on CliJsonRpcServer and JsonRpcCommand solely for the docs write path, along with DocsTarget and ValidateDocsOutputFileName.

Known gap

Neither method exposes the configured documentation.output.file, so a client wanting to reproduce bicep docs generate file naming chooses its own filename. This is intentional — it keeps DocsResult aligned with CompileResponse — and is documented.

Example Usage

bicep/renderDocs:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "bicep/renderDocs",
  "params": {
    "paths": ["/repo/modules/storage/main.bicep"],
    "templateFile": null,
    "templateRoot": null,
    "customTemplateValues": { "owner": "Platform Team" },
    "noRestore": false
  }
}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "results": [
      {
        "path": "/repo/modules/storage/main.bicep",
        "success": true,
        "diagnostics": [],
        "contents": "# Storage Account\n\nDeploys a storage account.\n"
      }
    ]
  }
}

bicep/getDocsModel returns the same data pre-render, using the protocol's own field names (typeName, isRequired, isSecure, isExisting, isTruncated, nestedProperties). The template-only helpers defaultValueFence and fence are excluded, being Markdown rendering aids rather than model data.

From C#:

var rendered = await client.RenderDocs(new RenderDocsRequest(
    Paths: ["./modules/storage/main.bicep"],
    TemplateFile: null,
    TemplateRoot: null,
    CustomTemplateValues: null,
    NoRestore: false));

foreach (var module in rendered.Results.Where(module => module.Success))
{
    // The client owns the filesystem - nothing is written by the RPC server.
    var directory = Path.GetDirectoryName(module.Path)!;
    await File.WriteAllTextAsync(Path.Combine(directory, "README.md"), module.Contents);
}

Full request/response payloads for both methods are in docs/experimental/docs-commands.md.

Documentation

  • docs/bicep-rpc-client.md gains Render docs and Get docs model sections. This file documented 8 operations and omitted both docs methods entirely — they were the only ones missing.
  • docs/experimental/docs-commands.md has a rewritten ## JSON-RPC section with per-method parameter tables, complete JSON-RPC request and response examples, and a C# example.

Both state the experimental caveat and that neither method writes files.

Validation

Build is clean at 0 warnings (TreatWarningsAsErrors). ~11,900 tests pass, 0 failures:

Project Passed
Bicep.Core.UnitTests 7023
Bicep.Core.IntegrationTests 3475
Bicep.Cli.IntegrationTests 1140
Bicep.Decompiler.IntegrationTests 105
Bicep.RpcClient.Tests 98
Bicep.Cli.UnitTests 72

The public API baseline was regenerated with --filter TestCategory=Baseline --test-parameter SetBaseLine=true, not hand-edited.

New JSON-RPC integration tests cover multiple paths in one request, a compilation failure among successes with request order preserved, a custom template via TemplateFile with includes and custom values, template settings coming from bicepconfig.json rather than the request, per-module configuration resolution in a single batch, cancellation threading into BuildModel/Render, structured failures, a fully populated model, and assertions that no files are written. A System.Text.Json round-trip test covers the model's ImmutableSortedDictionary and nested ImmutableArray members on netstandard2.0.

Checklist

Microsoft Reviewers: Open in CodeFlow

Replaces bicep/generateDocs and bicep/outputDocs with bicep/renderDocs and
bicep/getDocsModel, and marks both as experimental.

generateDocs was the only RPC method that wrote to disk. bicep/compile is the
RPC counterpart of bicep build and returns Contents rather than writing
main.json; renderDocs now follows that convention. The CLI merged docs output
into docs generate --stdout, so splitting two methods by output destination no
longer matched any command. getDocsModel returns the typed documentation model,
which previously required rendering Markdown and parsing it back.

getDocsModel resolves options from bicepconfig.json so usage-example discovery
and configured custom values match what renderDocs and the CLI produce.

Also corrects the client version gate from 0.46.0 to 0.47.0. Both 0.46.0 and
0.46.1 shipped before the docs feature merged, so the old gate let clients pass
the check and then fail with method not found.

Fixes BaselineHelper.GetRepoRoot to accept a .git file as a repo-root marker so
baseline tests run from a git worktree, where .git is a file rather than a
directory.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Test this change out locally with the following install scripts (Action run 32820517719)

VSCode
  • Mac/Linux
    bash <(curl -Ls https://aka.ms/bicep/nightly-vsix.sh) --run-id 32820517719
  • Windows
    iex "& { $(irm https://aka.ms/bicep/nightly-vsix.ps1) } -RunId 32820517719"
Azure CLI
  • Mac/Linux
    bash <(curl -Ls https://aka.ms/bicep/nightly-cli.sh) --run-id 32820517719
  • Windows
    iex "& { $(irm https://aka.ms/bicep/nightly-cli.ps1) } -RunId 32820517719"

@jaredfholgate

Copy link
Copy Markdown
Member Author

Copilot resolve the merge conflicts on this branch.

Co-authored-by: jaredfholgate <1612200+jaredfholgate@users.noreply.github.com>

Copilot AI commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Copilot resolve the merge conflicts on this branch.

Resolved. I merged origin/main into this branch and fixed the conflicts in commit 23dde6b.

@jaredfholgate

Copy link
Copy Markdown
Member Author

Replaced with #20215

@jaredfholgate
Jared Holgate (jaredfholgate) deleted the jaredfholgate-docs-rpc-surface-rework branch September 3, 2026 09:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants