Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package crossplane-cli for openSUSE:Factory checked in at 2026-06-23 17:40:11 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/crossplane-cli (Old) and /work/SRC/openSUSE:Factory/.crossplane-cli.new.1956 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "crossplane-cli" Tue Jun 23 17:40:11 2026 rev:19 rq:1361248 version:2.3.3 Changes: -------- --- /work/SRC/openSUSE:Factory/crossplane-cli/crossplane-cli.changes 2026-06-10 15:53:11.868829070 +0200 +++ /work/SRC/openSUSE:Factory/.crossplane-cli.new.1956/crossplane-cli.changes 2026-06-23 17:42:49.654394860 +0200 @@ -1,0 +2,10 @@ +Tue Jun 23 04:56:25 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 2.3.3: + The v2.3.3 release is a patch release that fixes bugs in several + commands and updates dependencies. + * deps: Bump crossplane dependencies to v2.3.3 + * chore(deps): update module github.com/containerd/containerd to + v1.7.33 [security] + +------------------------------------------------------------------- Old: ---- crossplane-cli-2.3.2.obscpio New: ---- crossplane-cli-2.3.3.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ crossplane-cli.spec ++++++ --- /var/tmp/diff_new_pack.ALpwfc/_old 2026-06-23 17:42:52.006477310 +0200 +++ /var/tmp/diff_new_pack.ALpwfc/_new 2026-06-23 17:42:52.010477451 +0200 @@ -19,7 +19,7 @@ %define executable_name crossplane Name: crossplane-cli -Version: 2.3.2 +Version: 2.3.3 Release: 0 Summary: The Cloud Native Control Plane License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.ALpwfc/_old 2026-06-23 17:42:52.062479273 +0200 +++ /var/tmp/diff_new_pack.ALpwfc/_new 2026-06-23 17:42:52.070479554 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/crossplane/cli.git</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">refs/tags/v2.3.2</param> + <param name="revision">refs/tags/v2.3.3</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.ALpwfc/_old 2026-06-23 17:42:52.102480676 +0200 +++ /var/tmp/diff_new_pack.ALpwfc/_new 2026-06-23 17:42:52.106480816 +0200 @@ -3,6 +3,6 @@ <param name="url">https://github.com/crossplane/crossplane</param> <param name="changesrevision">d13e114e1f124e69fa90351de663700a7d6a7ada</param></service><service name="tar_scm"> <param name="url">https://github.com/crossplane/cli.git</param> - <param name="changesrevision">0a7ac0f69113a0f44338807debeb35ec085cfaed</param></service></servicedata> + <param name="changesrevision">0bbd2114570966d34e3137627750ec58b48d82a2</param></service></servicedata> (No newline at EOF) ++++++ crossplane-cli-2.3.2.obscpio -> crossplane-cli-2.3.3.obscpio ++++++ Binary files old/crossplane-cli-2.3.2/cmd/crossplane/function/templates/go.tar and new/crossplane-cli-2.3.3/cmd/crossplane/function/templates/go.tar differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crossplane-cli-2.3.2/cmd/crossplane/render/xr/cmd.go new/crossplane-cli-2.3.3/cmd/crossplane/render/xr/cmd.go --- old/crossplane-cli-2.3.2/cmd/crossplane/render/xr/cmd.go 2026-06-09 22:29:48.000000000 +0200 +++ new/crossplane-cli-2.3.3/cmd/crossplane/render/xr/cmd.go 2026-06-22 22:16:19.000000000 +0200 @@ -31,6 +31,7 @@ "github.com/spf13/afero" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + kruntime "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/kube-openapi/pkg/spec3" @@ -165,6 +166,7 @@ return errors.Wrap(err, "cannot apply function annotation overrides") } + var xrdUnstructured *unstructured.Unstructured if c.XRD != "" { xrd, err := render.LoadXRD(c.fs, c.XRD) if err != nil { @@ -179,6 +181,12 @@ if err := render.DefaultValues(xr.UnstructuredContent(), xr.GetAPIVersion(), *crd); err != nil { return errors.Wrapf(err, "cannot default values for XR %q", xr.GetName()) } + + obj, err := kruntime.DefaultUnstructuredConverter.ToUnstructured(xrd) + if err != nil { + return errors.Wrapf(err, "cannot convert XRD %q to unstructured", xrd.GetName()) + } + xrdUnstructured = &unstructured.Unstructured{Object: obj} } fcreds := []corev1.Secret{} @@ -287,6 +295,7 @@ RequiredResources: rrs, RequiredSchemas: rsc, FunctionCredentials: fcreds, + XRD: xrdUnstructured, } req, err := render.BuildCompositeRequest(in) if err != nil { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crossplane-cli-2.3.2/cmd/crossplane/render/xr/cmd_test.go new/crossplane-cli-2.3.3/cmd/crossplane/render/xr/cmd_test.go --- old/crossplane-cli-2.3.2/cmd/crossplane/render/xr/cmd_test.go 2026-06-09 22:29:48.000000000 +0200 +++ new/crossplane-cli-2.3.3/cmd/crossplane/render/xr/cmd_test.go 2026-06-22 22:16:19.000000000 +0200 @@ -78,6 +78,9 @@ //go:embed testdata/cmd/output/include-full-xr.yaml var includeFullXROutput string +//go:embed testdata/cmd/xrd.yaml +var xrdYAML string + func newEngineFunc(engine render.Engine) func(*render.EngineFlags, logging.Logger) render.Engine { return func(*render.EngineFlags, logging.Logger) render.Engine { return engine @@ -470,6 +473,49 @@ }, want: want{err: cmpopts.AnyError}, }, + "XRDPassedToEngine": { + reason: "When --xrd is set, the XRD should be forwarded to the render engine so it can determine the composite schema (Legacy vs Modern).", + args: args{ + cmd: Cmd{ + CompositeResource: "xr.yaml", + Composition: "composition.yaml", + Functions: "functions.yaml", + XRD: "xrd.yaml", + Timeout: time.Minute, + fs: newTestFS(map[string]string{"xrd.yaml": xrdYAML}), + newEngine: newEngineFunc(&render.MockEngine{ + MockRender: func(_ context.Context, req *renderv1alpha1.RenderRequest) (*renderv1alpha1.RenderResponse, error) { + if req.GetComposite().GetCompositeResourceDefinition() == nil { + t.Error("expected render request to contain the XRD, got nil") + } + return &renderv1alpha1.RenderResponse{ + Output: &renderv1alpha1.RenderResponse_Composite{ + Composite: &renderv1alpha1.CompositeOutput{ + CompositeResource: fillResourceRefs(t, req.GetComposite().GetCompositeResource()), + ComposedResources: []*structpb.Struct{ + mustNewStruct(t, map[string]any{ + "apiVersion": "example.org/v1alpha1", + "kind": "ComposedResource", + "metadata": map[string]any{ + "name": "composed-foo", + "annotations": map[string]any{ + "crossplane.io/composition-resource-name": "composed-foo", + }, + }, + "spec": map[string]any{"coolField": "composed!"}, + }), + }, + }, + }, + }, nil + }, + }), + }, + }, + want: want{ + stdout: successOutput, + }, + }, "IncludeFullXR": { reason: "With --include-full-xr, the rendered XR is merged into the input XR so the input's spec.fromXR survives alongside any updated fields.", args: args{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crossplane-cli-2.3.2/cmd/crossplane/render/xr/testdata/cmd/xrd.yaml new/crossplane-cli-2.3.3/cmd/crossplane/render/xr/testdata/cmd/xrd.yaml --- old/crossplane-cli-2.3.2/cmd/crossplane/render/xr/testdata/cmd/xrd.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/crossplane-cli-2.3.3/cmd/crossplane/render/xr/testdata/cmd/xrd.yaml 2026-06-22 22:16:19.000000000 +0200 @@ -0,0 +1,22 @@ +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xnopresources.nop.example.org +spec: + group: nop.example.org + names: + kind: XNopResource + plural: xnopresources + versions: + - name: v1alpha1 + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + coolField: + type: string diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crossplane-cli-2.3.2/go.mod new/crossplane-cli-2.3.3/go.mod --- old/crossplane-cli-2.3.2/go.mod 2026-06-09 22:29:48.000000000 +0200 +++ new/crossplane-cli-2.3.3/go.mod 2026-06-22 22:16:19.000000000 +0200 @@ -12,8 +12,8 @@ github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 github.com/charmbracelet/x/term v0.2.2 github.com/containerd/errdefs v1.0.0 - github.com/crossplane/crossplane-runtime/v2 v2.3.2 - github.com/crossplane/crossplane/apis/v2 v2.3.2 + github.com/crossplane/crossplane-runtime/v2 v2.3.3 + github.com/crossplane/crossplane/apis/v2 v2.3.3 github.com/crossplane/function-sdk-go v0.6.1-0.20260506001521-78a3dd862da1 github.com/docker/cli v29.4.0+incompatible github.com/docker/docker v28.5.2+incompatible @@ -116,7 +116,7 @@ github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/uax29/v2 v2.5.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect - github.com/containerd/containerd v1.7.32 // indirect + github.com/containerd/containerd v1.7.33 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect @@ -184,7 +184,7 @@ github.com/google/cel-go v0.27.0 // indirect github.com/google/certificate-transparency-go v1.3.3 // indirect github.com/google/gnostic-models v0.7.1 // indirect - github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20230919002926-dbcd01c402b2 // indirect + github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20260312205200-e9163014982e // indirect github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20250225234217-098045d5e61f // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/css v1.0.1 // indirect diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crossplane-cli-2.3.2/go.sum new/crossplane-cli-2.3.3/go.sum --- old/crossplane-cli-2.3.2/go.sum 2026-06-09 22:29:48.000000000 +0200 +++ new/crossplane-cli-2.3.3/go.sum 2026-06-22 22:16:19.000000000 +0200 @@ -205,8 +205,8 @@ github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= -github.com/containerd/containerd v1.7.32 h1:S54xuVcPxeLaYgaRABtpJ2VyVUVsy0IGf7qHBs+sbY8= -github.com/containerd/containerd v1.7.32/go.mod h1:jdwD6s/BhV4XVJGrvtziNPVA+83n66TwptVaPKprq4E= +github.com/containerd/containerd v1.7.33 h1:iAkYGC/ifR/V+0eR4iXWHNGYUF0DF2PmGV5iz4Irj5M= +github.com/containerd/containerd v1.7.33/go.mod h1:gSbSCVjPCdkfJCjyrzz7aRC+xFlqVbatNpfHfVCYGUM= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= @@ -226,10 +226,10 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/crossplane/crossplane-runtime/v2 v2.3.2 h1:gjfJmr0PTf3/Ccg4iasogXKIRjYdEMILduiP/IZN260= -github.com/crossplane/crossplane-runtime/v2 v2.3.2/go.mod h1:POGt8DSTcxQJlTww+3yGeeXuEdLyjZ61vZ3ap5tTxhE= -github.com/crossplane/crossplane/apis/v2 v2.3.2 h1:Drs3xz59qT3zFfaszxQWqr51a0leAx20DBL4TqMnqi0= -github.com/crossplane/crossplane/apis/v2 v2.3.2/go.mod h1:o+D0ktZQKJCFcpfzMKA4n53aTo2sFqqDsADBNIRuIyE= +github.com/crossplane/crossplane-runtime/v2 v2.3.3 h1:seIYf6pk7dLhXd7uSh/N9rVB8IdbCthy3LB6TSn91OI= +github.com/crossplane/crossplane-runtime/v2 v2.3.3/go.mod h1:UBxoZEXVZz9Ql2W5u5ssFE+egQ21BYqGGc2F3dozUvI= +github.com/crossplane/crossplane/apis/v2 v2.3.3 h1:nNc2N5rycYUThpcuYvN3oFpIZQFp011oxafLlnyY0r4= +github.com/crossplane/crossplane/apis/v2 v2.3.3/go.mod h1:4eVfrI6aKUhZY++AGIlJh5jEtH9zlh5ZlRz7DgHX/fg= github.com/crossplane/function-sdk-go v0.6.1-0.20260506001521-78a3dd862da1 h1:jqnuzsHs2nLi/683A1Qj7WLOpf7zB4hHhx7+6uEBCTU= github.com/crossplane/function-sdk-go v0.6.1-0.20260506001521-78a3dd862da1/go.mod h1:yg4qMMRBQPZ75INoGEjfGQ014z4GilpgDcx4Fdf6AaA= github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q= @@ -445,8 +445,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-containerregistry v0.21.2 h1:vYaMU4nU55JJGFC9JR/s8NZcTjbE9DBBbvusTW9NeS0= github.com/google/go-containerregistry v0.21.2/go.mod h1:ctO5aCaewH4AK1AumSF5DPW+0+R+d2FmylMJdp5G7p0= -github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20230919002926-dbcd01c402b2 h1:ChuUQ1y5Vf+Eev+UgEed/ljibTIcWY7mYPtWYLK7fxU= -github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20230919002926-dbcd01c402b2/go.mod h1:Ek+8PQrShkA7aHEj3/zSW33wU0V/Bx3zW/gFh7l21xY= +github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20260312205200-e9163014982e h1:/ouF9a6+BFnovNKxnj/HYCyx2eldNzYu2lZvKdez3rM= +github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20260312205200-e9163014982e/go.mod h1:42tJN6TfytRCQlKQiJ2KzfRp5raI2RjN0cjJCFkeSyc= github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20250225234217-098045d5e61f h1:GJRzEBoJv/A/E7JbTekq1Q0jFtAfY7TIxUFAK89Mmic= github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20250225234217-098045d5e61f/go.mod h1:ZT74/OE6eosKneM9/LQItNxIMBV6CI5S46EXAnvkTBI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crossplane-cli-2.3.2/gomod2nix.toml new/crossplane-cli-2.3.3/gomod2nix.toml --- old/crossplane-cli-2.3.2/gomod2nix.toml 2026-06-09 22:29:48.000000000 +0200 +++ new/crossplane-cli-2.3.3/gomod2nix.toml 2026-06-22 22:16:19.000000000 +0200 @@ -204,8 +204,8 @@ version = "v1.6.3" hash = "sha256-XZm4EastgX67Dgm5BpOEW/PY4aLcHM/O8+Xbz26PuTY=" [mod."github.com/containerd/containerd"] - version = "v1.7.32" - hash = "sha256-4SHa5JF563h1NtCl2OWzheMJg8jH1qCPaCY3/iwaqVM=" + version = "v1.7.33" + hash = "sha256-5hXYyZJcXZiS0kTlcIgEfUwIZj/E++BOC7zI2tn3/Wg=" [mod."github.com/containerd/errdefs"] version = "v1.0.0" hash = "sha256-wMZGoeqvRhuovYCJx0Js4P3qFCNTZ/6Atea/kNYoPMI=" @@ -225,11 +225,11 @@ version = "v3.17.0" hash = "sha256-b9dCq5GN5ac64UG23Rijv1qcmUZNcxb8DJQycAa96EQ=" [mod."github.com/crossplane/crossplane-runtime/v2"] - version = "v2.3.2" - hash = "sha256-GzcQz6qzsOL5SRVtJLiEo345HGOlGE053RNedvfNff8=" + version = "v2.3.3" + hash = "sha256-EV8OVPduo12izMXcPySnNvMble5cyZc1sS+zsljdWgU=" [mod."github.com/crossplane/crossplane/apis/v2"] - version = "v2.3.2" - hash = "sha256-Mld+dYtG/JpCD4VB9/CjAaJ/t8Lh/92WDOpeKlccG6o=" + version = "v2.3.3" + hash = "sha256-mRQAdTjq/hKxrvbdG80M+QoTibJI3XO287ICx70feYY=" [mod."github.com/crossplane/function-sdk-go"] version = "v0.6.1-0.20260506001521-78a3dd862da1" hash = "sha256-PFppHOnNdKBh4lipILeXI5Rp8rj+CR1XIjvu5iFmA2U=" @@ -450,8 +450,8 @@ version = "v0.21.2" hash = "sha256-6ONiJAQYguYJc6vcvdgFZYNtecrrOC7OzTVX4sIN5T8=" [mod."github.com/google/go-containerregistry/pkg/authn/k8schain"] - version = "v0.0.0-20230919002926-dbcd01c402b2" - hash = "sha256-y/xHODMYpIsday3XuwTS8bO5+1CMjgazalC2fijnC6c=" + version = "v0.0.0-20260312205200-e9163014982e" + hash = "sha256-RtHWZgcWWACijaU4/zA2r07/jZecWVvemrSWpAQb2Og=" [mod."github.com/google/go-containerregistry/pkg/authn/kubernetes"] version = "v0.0.0-20250225234217-098045d5e61f" hash = "sha256-UZyDwMt9qQw5XHHDOlTyYMRvG1BiDfBHeZLmoMzunB4=" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crossplane-cli-2.3.2/internal/xrd/infer.go new/crossplane-cli-2.3.3/internal/xrd/infer.go --- old/crossplane-cli-2.3.2/internal/xrd/infer.go 2026-06-09 22:29:48.000000000 +0200 +++ new/crossplane-cli-2.3.3/internal/xrd/infer.go 2026-06-22 22:16:19.000000000 +0200 @@ -20,12 +20,22 @@ import ( "fmt" "maps" + "math" extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "github.com/crossplane/crossplane-runtime/v2/pkg/errors" ) +const ( + schemaTypeArray = "array" + schemaTypeBoolean = "boolean" + schemaTypeInteger = "integer" + schemaTypeNumber = "number" + schemaTypeObject = "object" + schemaTypeString = "string" +) + // InferProperties infers JSON schema properties from a map of values. func InferProperties(spec map[string]any) (map[string]extv1.JSONSchemaProps, error) { properties := make(map[string]extv1.JSONSchemaProps) @@ -46,10 +56,10 @@ func inferArrayProperty(v []any) (extv1.JSONSchemaProps, error) { if len(v) == 0 { return extv1.JSONSchemaProps{ - Type: "array", + Type: schemaTypeArray, Items: &extv1.JSONSchemaPropsOrArray{ Schema: &extv1.JSONSchemaProps{ - Type: "object", + Type: schemaTypeObject, }, }, }, nil @@ -61,7 +71,7 @@ } mergedProperties := make(map[string]extv1.JSONSchemaProps) - if firstElemSchema.Type == "object" { + if firstElemSchema.Type == schemaTypeObject { maps.Copy(mergedProperties, firstElemSchema.Properties) } @@ -70,21 +80,31 @@ if err != nil { return extv1.JSONSchemaProps{}, err } + + // If an array contains a mix of numbers and integers, use number as the + // array type. + if elemSchema.Type == schemaTypeInteger && firstElemSchema.Type == schemaTypeNumber { + continue + } + if elemSchema.Type == schemaTypeNumber && firstElemSchema.Type == schemaTypeInteger { + firstElemSchema.Type = schemaTypeNumber + } + if elemSchema.Type != firstElemSchema.Type { return extv1.JSONSchemaProps{}, errors.New("mixed types detected in array") } - if elemSchema.Type == "object" { + if elemSchema.Type == schemaTypeObject { maps.Copy(mergedProperties, elemSchema.Properties) } } resultSchema := firstElemSchema - if firstElemSchema.Type == "object" && len(mergedProperties) > 0 { + if firstElemSchema.Type == schemaTypeObject && len(mergedProperties) > 0 { resultSchema.Properties = mergedProperties } return extv1.JSONSchemaProps{ - Type: "array", + Type: schemaTypeArray, Items: &extv1.JSONSchemaPropsOrArray{ Schema: &resultSchema, }, @@ -94,26 +114,45 @@ func inferProperty(value any) (extv1.JSONSchemaProps, error) { if value == nil { return extv1.JSONSchemaProps{ - Type: "string", + Type: schemaTypeString, }, nil } switch v := value.(type) { case string: return extv1.JSONSchemaProps{ - Type: "string", + Type: schemaTypeString, }, nil case int, int32, int64: return extv1.JSONSchemaProps{ - Type: "integer", + Type: schemaTypeInteger, }, nil - case float32, float64: + case float32: + // JSON doesn't have integers, so json.Unmarshal treats all numbers as + // floats. Try to detect whehter the number is actually an integer, so + // that we're more likely to infer the user's intent. This heuristic + // isn't perfect since not all integers are representable as floats, but + // it will work for common cases. + t := schemaTypeNumber + if math.Trunc(float64(v)) == float64(v) { + t = schemaTypeInteger + } + + return extv1.JSONSchemaProps{ + Type: t, + }, nil + case float64: + t := schemaTypeNumber + if math.Trunc(v) == v { + t = schemaTypeInteger + } + return extv1.JSONSchemaProps{ - Type: "number", + Type: t, }, nil case bool: return extv1.JSONSchemaProps{ - Type: "boolean", + Type: schemaTypeBoolean, }, nil case map[string]any: inferredProps, err := InferProperties(v) @@ -121,7 +160,7 @@ return extv1.JSONSchemaProps{}, errors.Wrap(err, "error inferring properties for object") } return extv1.JSONSchemaProps{ - Type: "object", + Type: schemaTypeObject, Properties: inferredProps, }, nil case []any: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crossplane-cli-2.3.2/internal/xrd/infer_test.go new/crossplane-cli-2.3.3/internal/xrd/infer_test.go --- old/crossplane-cli-2.3.2/internal/xrd/infer_test.go 2026-06-09 22:29:48.000000000 +0200 +++ new/crossplane-cli-2.3.3/internal/xrd/infer_test.go 2026-06-22 22:16:19.000000000 +0200 @@ -38,25 +38,31 @@ "StringType": { input: "hello", want: want{ - output: extv1.JSONSchemaProps{Type: "string"}, + output: extv1.JSONSchemaProps{Type: schemaTypeString}, }, }, "IntegerType": { input: 42, want: want{ - output: extv1.JSONSchemaProps{Type: "integer"}, + output: extv1.JSONSchemaProps{Type: schemaTypeInteger}, }, }, "FloatType": { input: 3.14, want: want{ - output: extv1.JSONSchemaProps{Type: "number"}, + output: extv1.JSONSchemaProps{Type: schemaTypeNumber}, + }, + }, + "IntegerAsFloatType": { + input: float64(1), + want: want{ + output: extv1.JSONSchemaProps{Type: schemaTypeInteger}, }, }, "BooleanType": { input: true, want: want{ - output: extv1.JSONSchemaProps{Type: "boolean"}, + output: extv1.JSONSchemaProps{Type: schemaTypeBoolean}, }, }, "ObjectType": { @@ -65,9 +71,9 @@ }, want: want{ output: extv1.JSONSchemaProps{ - Type: "object", + Type: schemaTypeObject, Properties: map[string]extv1.JSONSchemaProps{ - "key": {Type: "string"}, + "key": {Type: schemaTypeString}, }, }, }, @@ -76,9 +82,31 @@ input: []any{"one", "two"}, want: want{ output: extv1.JSONSchemaProps{ - Type: "array", + Type: schemaTypeArray, + Items: &extv1.JSONSchemaPropsOrArray{ + Schema: &extv1.JSONSchemaProps{Type: schemaTypeString}, + }, + }, + }, + }, + "ArrayWithMixedNumbersIntegerFirst": { + input: []any{1, float32(3.14)}, + want: want{ + output: extv1.JSONSchemaProps{ + Type: schemaTypeArray, + Items: &extv1.JSONSchemaPropsOrArray{ + Schema: &extv1.JSONSchemaProps{Type: schemaTypeNumber}, + }, + }, + }, + }, + "ArrayWithMixedNumbersFloatFirst": { + input: []any{float32(3.14), 1}, + want: want{ + output: extv1.JSONSchemaProps{ + Type: schemaTypeArray, Items: &extv1.JSONSchemaPropsOrArray{ - Schema: &extv1.JSONSchemaProps{Type: "string"}, + Schema: &extv1.JSONSchemaProps{Type: schemaTypeNumber}, }, }, }, @@ -87,9 +115,9 @@ input: []any{}, want: want{ output: extv1.JSONSchemaProps{ - Type: "array", + Type: schemaTypeArray, Items: &extv1.JSONSchemaPropsOrArray{ - Schema: &extv1.JSONSchemaProps{Type: "object"}, + Schema: &extv1.JSONSchemaProps{Type: schemaTypeObject}, }, }, }, @@ -97,7 +125,7 @@ "NilValue": { input: nil, want: want{ - output: extv1.JSONSchemaProps{Type: "string"}, + output: extv1.JSONSchemaProps{Type: schemaTypeString}, }, }, "ArrayWithMixedTypes": { @@ -123,20 +151,20 @@ }, want: want{ output: extv1.JSONSchemaProps{ - Type: "array", + Type: schemaTypeArray, Items: &extv1.JSONSchemaPropsOrArray{ Schema: &extv1.JSONSchemaProps{ - Type: "object", + Type: schemaTypeObject, Properties: map[string]extv1.JSONSchemaProps{ - "name": {Type: "string"}, - "cidr": {Type: "string"}, + "name": {Type: schemaTypeString}, + "cidr": {Type: schemaTypeString}, "serviceEndpoints": { Type: "array", Items: &extv1.JSONSchemaPropsOrArray{ - Schema: &extv1.JSONSchemaProps{Type: "string"}, + Schema: &extv1.JSONSchemaProps{Type: schemaTypeString}, }, }, - "delegation": {Type: "string"}, + "delegation": {Type: schemaTypeString}, }, }, }, @@ -180,8 +208,8 @@ }, want: want{ output: map[string]extv1.JSONSchemaProps{ - "key1": {Type: "string"}, - "key2": {Type: "integer"}, + "key1": {Type: schemaTypeString}, + "key2": {Type: schemaTypeInteger}, }, }, }, @@ -194,9 +222,9 @@ want: want{ output: map[string]extv1.JSONSchemaProps{ "nested": { - Type: "object", + Type: schemaTypeObject, Properties: map[string]extv1.JSONSchemaProps{ - "key": {Type: "boolean"}, + "key": {Type: schemaTypeBoolean}, }, }, }, @@ -211,7 +239,7 @@ "array": { Type: "array", Items: &extv1.JSONSchemaPropsOrArray{ - Schema: &extv1.JSONSchemaProps{Type: "string"}, + Schema: &extv1.JSONSchemaProps{Type: schemaTypeString}, }, }, }, ++++++ crossplane-cli.obsinfo ++++++ --- /var/tmp/diff_new_pack.ALpwfc/_old 2026-06-23 17:42:52.722502410 +0200 +++ /var/tmp/diff_new_pack.ALpwfc/_new 2026-06-23 17:42:52.734502831 +0200 @@ -1,5 +1,5 @@ name: crossplane-cli -version: 2.3.2 -mtime: 1781036988 -commit: 0a7ac0f69113a0f44338807debeb35ec085cfaed +version: 2.3.3 +mtime: 1782159379 +commit: 0bbd2114570966d34e3137627750ec58b48d82a2 ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/crossplane-cli/vendor.tar.gz /work/SRC/openSUSE:Factory/.crossplane-cli.new.1956/vendor.tar.gz differ: char 132, line 1
