Skip to content

fix(generator): keep version in CRD schema names and merge multi-document CRDs into one spec - #182

Merged
Peefy merged 4 commits into
mainfrom
fix/181-crd-single-version-names
Oct 9, 2026
Merged

Peefy merged 4 commits into
mainfrom
fix/181-crd-single-version-names

Conversation

@Peefy

@Peefy Peefy commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #181.

Since #180, a CRD with exactly one version gets a schema name after the bare Kind (e.g. Widget), dropping the version qualifier. Two defects, one root cause:

A. kcl import groups generated files into per-version directories by extracting the version from the file name (_v1, _v1alpha1, ...). A single-version CRD rendered as models/unknown/widget.k instead of models/v1/..., silently changing the import path of published modules (strimzi_kafka_operator.v1 -> ...unknown).

B. Two CRDs sharing a Kind across different API groups both rendered as models/widget.k: the CLI converts each YAML document of the input file into its own spec and generates each separately, so the second file silently overwrote the first (exit code 0).

Fix

  • pkg/swagger/generator/names.go: append the version to the short root name unconditionally (Widget -> WidgetV1), so the file name always carries the version. When the same Kind+Version is declared by several API groups, every root of that class is additionally qualified with its group segments (example.com/other.com Widget -> ExampleComWidgetV1 / OtherComWidgetV1), keeping both definitions and preserving the group identity.
  • pkg/kube_resource/generator/generator.go: GetSpecs/GetSpec now merge all YAML documents of one file into a single swagger spec, so every CRD of the file is generated in one run against a shared name registry, instead of one overwrite-prone generation per document. Side effect: the standalone binary now handles multi-document files instead of silently converting only the first document.
  • pkg/swagger/generator/{model,language}.go: make rendered default/example values deterministic. RecoverMapValueOrder filled MapSlice slots from value.MapKeys() (random order) and toKclValue iterated maps via MapRange (random order), so the golden suite failed intermittently (~1 in 3 runs) on JSON key order.

Multi-version CRDs keep the names #180 introduced (WidgetV1 / WidgetV1alpha1); --long-names is unchanged. Golden fixtures regenerated: single-version model files now carry the _v<version> suffix.

Verification

The issue's reproducer now yields:

models/v1/example_com_widget_v1.k
models/v1/other_com_widget_v1.k
    replicas?: int        # no longer lost
    otherOnlyField?: int

and the generated package compiles and runs with kcl (schema instantiation, correct apiVersion defaults). go test ./... passes; the golden suite was run repeatedly to confirm the flake is gone.

Known limitation: the same Kind in different groups split across separate input files (kcl import -m crd a.yaml b.yaml) can still collide; 0.12.x avoided this via fully qualified names. The reported repro is a single multi-document file, which this fix covers.

…ment CRDs into one spec

Fixes #181. Since #180, importing a CRD with exactly one version produced
schema names after the bare Kind (e.g. Widget), dropping the version
qualifier. Two defects, one root cause:

A. kcl import groups the generated files into per-version directories by
   extracting the version from the file name (_v1, _v1alpha1, ...). A
   single-version CRD rendered as models/unknown/widget.k instead of
   models/v1/example_com_v1_widget.k, silently changing the import path
   of published modules (e.g. strimzi_kafka_operator.v1 -> ...unknown).

B. Two CRDs sharing a Kind across different API groups both rendered as
   models/widget.k: the CLI converts each YAML document of the input file
   into its own spec and generates each separately, so the second file
   silently overwrote the first with exit code 0.

Fixes:

- pkg/swagger/generator/names.go: append the version to the short root
  name unconditionally (Widget -> WidgetV1), so the file name always
  carries the version. When the same Kind+Version is declared by several
  API groups, every root of that class is additionally qualified with its
  group segments (example.com/other.com Widget -> ExampleComWidgetV1 /
  OtherComWidgetV1), keeping both definitions and preserving the group
  identity #180 removed.
- pkg/kube_resource/generator/generator.go: GetSpecs/GetSpec now merge
  all YAML documents of one file into a single swagger spec, so every CRD
  of the file is generated in one run against a shared name registry
  instead of one overwrite-prone generation per document. As a side
  effect the standalone binary handles multi-document files too instead
  of silently converting only the first document.
- pkg/swagger/generator/{model,language}.go: make the rendered default
  and example values deterministic. RecoverMapValueOrder filled MapSlice
  slots from value.MapKeys() (random order) and toKclValue iterated maps
  via MapRange (random order), so golden tests failed intermittently
  (~1 in 3 runs) on JSON key order.

Multi-version CRDs keep the names #180 introduced (WidgetV1 /
WidgetV1alpha1); --long-names is unchanged. Golden fixtures regenerated:
single-version model files now carry the _v<version> suffix.

Verified against the reproducer in the issue: two single-version Widget
CRDs in one file now render as models/v1/example_com_widget_v1.k and
models/v1/other_com_widget_v1.k, both fields intact, and the generated
package compiles and runs with kcl (schema instantiation, apiVersion
defaults). go test ./... passes; the golden suite was run repeatedly to
confirm the flake is gone.

Known limitation: the same Kind in different groups split across separate
input files (kcl import -m crd a.yaml b.yaml) can still collide; 0.12.x
avoided this via fully qualified names. The issue's repro is a single
multi-document file, which this fix covers.

Signed-off-by: Claude Opus 4.6 <noreply@anthropic.com>
Peefy pushed a commit that referenced this pull request Oct 9, 2026
…mp dir

The generator wrote the bundled k8s.json next to the converted swagger
spec to a fixed path in the shared temp dir on every GetSpec/GetSpecs
call. go test runs the cmds and swagger/generator suites concurrently,
and both invoke the converter (one in-process, one via the binary
subprocess), so one run truncated the file while the other was reading
it: "could not resolve schema: unexpected end of JSON input". CI failed
intermittently on random CRD cases because of this (kcl-lang/kcl-openapi
#182).

Write the k8s.json and the spec into a fresh os.MkdirTemp directory per
call instead. The relative "k8s.json#/definitions/..." references still
resolve, now against the spec file's own private directory, and
concurrent runs no longer share a file.
…mp dir

The generator wrote the bundled k8s.json next to the converted swagger
spec to a fixed path in the shared temp dir on every GetSpec/GetSpecs
call. go test runs the cmds and swagger/generator suites concurrently,
and both invoke the converter (one in-process, one via the binary
subprocess), so one run truncated the file while the other was reading
it: "could not resolve schema: unexpected end of JSON input". CI failed
intermittently on random CRD cases because of this (kcl-lang/kcl-openapi
#182).

Write the k8s.json and the spec into a fresh os.MkdirTemp directory per
call instead. The relative "k8s.json#/definitions/..." references still
resolve, now against the spec file's own private directory, and
concurrent runs no longer share a file.

Signed-off-by: Claude Opus 4.6 <noreply@anthropic.com>
@Peefy
Peefy force-pushed the fix/181-crd-single-version-names branch from 09939fb to b50fd5b Compare October 9, 2026 15:58
WithXOrder wrote the rewritten spec to the temp dir root, away from the
original document. Relative $refs in the spec (e.g. the k8s.json sidecar
written next to converted CRD specs) resolve against the directory of
the document being loaded, so the rewrite broke that resolution and the
flatten step failed with "cannot load spec: open <tmp>/k8s.json: no such
file or directory" on machines without a stale copy there. Local macOS
runs passed only because earlier versions had left exactly such a stale
file behind in the per-user temp dir.

Write the rewrite into the original spec's directory instead, and remove
both intermediate files at the end of analyzeSpec: the document content
lives in the loaded specDoc by then, and the old behavior leaked the
rewritten specs next to the sources whenever those lived outside the
temp dir.

Signed-off-by: Claude Opus 4.6 <noreply@anthropic.com>
@coveralls

coveralls commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 37960217708

Coverage increased (+2.0%) to 66.742%

Details

  • Coverage increased (+2.0%) from the base build.
  • Patch coverage: Could not be determined — this PR's diff is too large for GitHub to return (406 error at GitHub).
  • 136 coverage regressions across 5 files.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

136 previously-covered lines in 5 files lost coverage.

File Lines Losing Coverage Coverage
pkg/swagger/generator/names.go 49 78.47%
pkg/kube_resource/generator/generator.go 41 72.91%
pkg/swagger/generator/spec.go 24 84.25%
pkg/swagger/generator/language.go 21 74.43%
pkg/swagger/generator/model.go 1 61.21%

Coverage Stats

Coverage Status
Relevant Lines: 4895
Covered Lines: 3267
Line Coverage: 66.74%
Coverage Strength: 0.76 hits per line

💛 - Coveralls

WithXOrder and getSpecFiles created their temp files with os.CreateTemp
but never closed the returned handle (the content was written through a
second handle, or written but left open). Harmless on unix, where tests
remove temp dirs with open files just fine; on Windows the test cleanup
fails with "The process cannot access the file because it is being used
by another process" once the rewritten spec lives next to the input
inside t.TempDir.

Signed-off-by: Claude Opus 4.6 <noreply@anthropic.com>
@Peefy
Peefy merged commit 96f08af into main Oct 9, 2026
6 checks passed
@Peefy
Peefy deleted the fix/181-crd-single-version-names branch October 9, 2026 16:44
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.

import -m crd: single-version CRDs emit models/unknown/ and same-Kind CRDs across groups silently overwrite (0.13.x regression)

3 participants