Skip to content

🤖 fix: make generated CRD API docs YAML-oriented#59

Merged
ThomasK33 merged 1 commit into
mainfrom
docs-yaml-api-reference
Feb 12, 2026
Merged

🤖 fix: make generated CRD API docs YAML-oriented#59
ThomasK33 merged 1 commit into
mainfrom
docs-yaml-api-reference

Conversation

@ThomasK33

Copy link
Copy Markdown
Member

Summary
Improves generated CRD API reference docs so they read more like YAML users actually write, with cleaner field/type names and selective nested-object expansion for actionable object fields.

Background
The generated docs were hard to use because table keys repeated spec./status., type names included noisy fully-qualified package paths, and object-like fields (for example LocalObjectReference) did not show nested members users need in YAML.

Implementation

  • Updated hack/crd-ref-docs/templates/markdown/gv_list.tpl:
    • removed redundant spec. / status. prefixes from table rows.
    • shortened common type package paths (metav1, corev1, and local API types).
    • added a recursive renderFields template for nested field expansion.
    • constrained expansion to project-local API types and corev1.LocalObjectReference with depth limit 3 to avoid doc bloat.
  • Regenerated API reference outputs under docs/reference/api/*.md.

Validation

  • make verify-vendor
  • make test
  • make build
  • make lint
  • make docs-reference
  • make docs-check

Risks
Low to moderate. This changes only documentation generation templates and generated markdown output, not runtime behavior. Main risk is doc rendering regressions; mitigated by successful make docs-check (strict mkdocs build) and regeneration checks.


📋 Implementation Plan

Plan: Make CRD API reference docs read like YAML (nested + less verbose)

Context / Why

The current generated API reference docs (e.g. docs/reference/api/coderprovisioner.md) are hard for users to act on:

  • The Field column repeats spec. / status. even though the table is already under ## Spec / ## Status.
  • The Type column shows fully-qualified Go package paths, which is noisy.
  • Most importantly, nested struct types are opaque (e.g. controlPlaneRef is a corev1.LocalObjectReference, but the docs don’t show that the YAML must contain name:). Users don’t know what to write.

Goal: adjust the crd-ref-docs markdown template so the output is:

  • easier to scan,
  • closer to the YAML structure people actually write,
  • and provides actionable detail for “object” fields (at least for the important/commonly-confusing ones like LocalObjectReference).

Evidence (what we verified)

  • API reference docs are generated by crd-ref-docs via hack/update-reference-docs.sh / make docs-reference. (hack/update-reference-docs.sh, hack/crd-ref-docs/config.yaml)
  • Output is a simple flat table produced by hack/crd-ref-docs/templates/markdown/gv_list.tpl.
  • The example in the screenshot matches docs/reference/api/coderprovisioner.md.
  • crd-ref-docs represents nested types via types.Type with a Members() method that returns struct fields even through aliases/slices/pointers. (vendor/github.com/elastic/crd-ref-docs/types/types.go)

Implementation details

1) Remove redundant spec. / status. prefixes

File: hack/crd-ref-docs/templates/markdown/gv_list.tpl

  • Change the rows under ## Spec from:
| `spec.{{ $member.Name }}` | ... |

to:

| `{{ $member.Name }}` | ... |
  • Change the rows under ## Status from:
| `status.{{ $member.Name }}` | ... |

to:

| `{{ $member.Name }}` | ... |

Result: tables read like the YAML keys a user will set under spec: / status:.


2) Bonus: Clean up verbose Go type paths in the Type column

File: hack/crd-ref-docs/templates/markdown/gv_list.tpl (the typeName helper)

Update typeName to replace noisy package paths with common aliases / short names.

Proposed replacements (minimal, focused on what we see in our CRDs):

  • k8s.io/apimachinery/pkg/apis/meta/v1.metav1. (already present)
  • k8s.io/api/core/v1.corev1.
  • github.com/coder/coder-k8s/api/v1alpha1. → `` (strip to just the type name)

Shape:

{{- $typeString := $fieldType.String -}}
{{- $typeString = trimPrefix "*" $typeString -}}
{{- $typeString = $typeString | replace "k8s.io/apimachinery/pkg/apis/meta/v1." "metav1." -}}
{{- $typeString = $typeString | replace "k8s.io/api/core/v1." "corev1." -}}
{{- $typeString = $typeString | replace "github.com/coder/coder-k8s/api/v1alpha1." "" -}}
{{- $typeString -}}

Result: types become readable (corev1.LocalObjectReference, CoderProvisionerKeySpec, etc.) without losing meaning.


3) Show nested fields for selected “object” types (so users know what to write)

File: hack/crd-ref-docs/templates/markdown/gv_list.tpl

Replace the flat range $specMembers / range $statusMembers rendering with a small recursive renderer that can optionally expand struct members.

3.1) Expansion policy (avoid doc bloat)

We should not blindly expand every imported Kubernetes type (e.g. corev1.EnvVar can recursively explode). Instead, expand only when:

  • The field’s type has members (len $t.Members > 0), and
  • The type is either:
    • Project-local ($t.Package starts with github.com/coder/coder-k8s/api/), or
    • A small, commonly-confusing external wrapper like k8s.io/api/core/v1.LocalObjectReference.

Add a simple depth limit (e.g. maxDepth := 3) to prevent runaway recursion.

3.2) Table rendering shape

Add a new template block (name is flexible) that prints rows for a list of fields and can recurse:

{{- define "renderFields" -}}
{{- $fields := index . 0 -}}
{{- $depth := index . 1 -}}
{{- $maxDepth := index . 2 -}}
{{- $indent := repeat $depth "  " -}}

{{- range $f := $fields }}
{{- if not $f.Type -}}
{{ fail (printf "field %q is missing type information" $f.Name) }}
{{- end -}}

{{- $t := $f.Type -}}
{{- $hasMembers := gt (len $t.Members) 0 -}}
{{- $isProjectLocal := hasPrefix $t.Package "github.com/coder/coder-k8s/api/" -}}
{{- $isLocalObjectRef := and (eq $t.Package "k8s.io/api/core/v1") (eq $t.Name "LocalObjectReference") -}}
{{- $shouldExpand := and $hasMembers (lt $depth $maxDepth) (or $isProjectLocal $isLocalObjectRef) -}}

| {{ $indent }}`{{ $f.Name }}` | `{{ template "typeName" $t }}` | {{ markdownRenderFieldDoc $f.Doc }} |
{{- if $shouldExpand }}
{{- template "renderFields" (list $t.Members (add $depth 1) $maxDepth) -}}
{{- end }}
{{- end }}
{{- end -}}

Then render spec/status like:

## Spec

| Field | Type | Description |
| --- | --- | --- |
{{- template "renderFields" (list $specMembers 0 3) }}

## Status

| Field | Type | Description |
| --- | --- | --- |
{{- template "renderFields" (list $statusMembers 0 3) }}

Result: users will see controlPlaneRefname (and other small structs) directly in the table, without needing to understand Go types.

Why this policy (and not “expand everything”)?

Expanding every imported Kubernetes type tends to create huge, hard-to-navigate pages and slows review. The hybrid approach targets the worst UX offenders (object refs and project-local structs) while keeping common K8s “implementation detail” structs as a single row.

Follow-up options if we want more detail later:

  • allowlist additional external types (e.g. metav1.LabelSelector),
  • add per-type <details> blocks instead of in-table recursion,
  • or render linked “Type sections” further down the page.

4) Regenerate reference docs and confirm output

After template changes, regenerate docs so the repo reflects the new format.

  • Run: make docs-reference (or bash hack/update-reference-docs.sh)
  • Confirm:
    • docs/reference/api/coderprovisioner.md no longer has spec. / status. prefixes.
    • controlPlaneRef expands to show name.
    • Type strings are shortened as expected.
    • mkdocs.yml nav updates (if the script touches it).

Validation

  • make docs-reference (ensures generator + templates work and output is updated)
  • make docs-check (ensures the docs site still builds)
  • (Optional but recommended) make test (sanity check; should be unchanged but cheap to verify)

Generated with mux • Model: openai:gpt-5.3-codex • Thinking: xhigh • Cost: $0.53

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

Please review this docs-generation change.

@ThomasK33 ThomasK33 force-pushed the docs-yaml-api-reference branch from ff7f500 to b65e3d6 Compare February 12, 2026 10:40
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

Addressed docs-quality markdownlint failures from CI and force-pushed updates.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33 ThomasK33 force-pushed the docs-yaml-api-reference branch from b65e3d6 to c55b318 Compare February 12, 2026 10:42
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

Added cspell allowlist entry for the new corev1 type alias text in generated API docs and revalidated locally.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33

Copy link
Copy Markdown
Member Author

@ThomasK33 ThomasK33 added this pull request to the merge queue Feb 12, 2026
Merged via the queue into main with commit 6dc3a4d Feb 12, 2026
11 checks passed
@ThomasK33 ThomasK33 deleted the docs-yaml-api-reference branch February 12, 2026 10:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant