diff --git a/README.md b/README.md index 89200f5..5ac8e04 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,27 @@ To get an HTML output provide the format flag like this: cty generate -c delivery.krok.app_krokcommands --comments --format html ``` +### Minimal required CRD sample + +It's possible to generate a sample YAML for a CRD that will make the CRD validation pass. Meaning, it will only contain +samples for fields that are actually required. All other fields will be ignored. + +For example, a CRD having a single required field with an example and the rest being optional would generate something +like this: + +```yaml +apiVersion: delivery.krok.app/v1alpha1 +kind: KrokCommand +spec: + image: "krok-hook/slack-notification:v0.0.1" +``` + +To run cty with minimal required fields, pass in `--minimal` to the command like this: + +``` +cty generate -c delivery.krok.app_krokcommands --comments --minimal --format html +``` + ## WASM frontend There is a WASM based frontend that can be started by navigating into the `wasm` folder and running the following make diff --git a/cmd/generate.go b/cmd/generate.go index 104999b..1f3fe4e 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -35,6 +35,7 @@ var ( format string stdOut bool comments bool + minimal bool ) func init() { @@ -45,8 +46,9 @@ func init() { f.StringVarP(&url, "url", "u", "", "If provided, will use this URL to fetch CRD YAML content from.") f.StringVarP(&output, "output", "o", "", "The location of the output file. Default is next to the CRD.") f.StringVarP(&format, "format", "f", FormatYAML, "The format in which to output. Default is YAML. Options are: yaml, html.") - f.BoolVarP(&stdOut, "stdout", "s", false, "If set, it will output the generated content to stdout") - f.BoolVarP(&comments, "comments", "m", false, "If set, it will add descriptions as comments to each line where available") + f.BoolVarP(&stdOut, "stdout", "s", false, "If set, it will output the generated content to stdout.") + f.BoolVarP(&comments, "comments", "m", false, "If set, it will add descriptions as comments to each line where available.") + f.BoolVarP(&minimal, "minimal", "l", false, "If set, only the minimal required example yaml is generated.") } func runGenerate(_ *cobra.Command, _ []string) error { @@ -95,8 +97,8 @@ func runGenerate(_ *cobra.Command, _ []string) error { return fmt.Errorf("failed to load templates: %w", err) } - return pkg.RenderContent(w, content, comments) + return pkg.RenderContent(w, content, comments, minimal) } - return pkg.Generate(crd, w, comments) + return pkg.Generate(crd, w, comments, minimal) } diff --git a/pkg/create_html_output.go b/pkg/create_html_output.go index b5d5c26..1e725ae 100644 --- a/pkg/create_html_output.go +++ b/pkg/create_html_output.go @@ -7,6 +7,7 @@ import ( "html/template" "io" "io/fs" + "slices" "sort" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" @@ -60,20 +61,22 @@ func LoadTemplates() error { } // RenderContent creates an HTML website from the CRD content. -func RenderContent(w io.Writer, crdContent []byte, comments bool) error { +func RenderContent(w io.Writer, crdContent []byte, comments, minimal bool) error { crd := &v1beta1.CustomResourceDefinition{} if err := yaml.Unmarshal(crdContent, crd); err != nil { return fmt.Errorf("failed to unmarshal into custom resource definition: %w", err) } versions := make([]Version, 0) + parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, comments, minimal) + for _, version := range crd.Spec.Versions { - out, err := parseCRD(version.Schema.OpenAPIV3Schema.Properties, version.Name, version.Schema.OpenAPIV3Schema.Required) + out, err := parseCRD(version.Schema.OpenAPIV3Schema.Properties, version.Name, minimal, RootRequiredFields) if err != nil { return fmt.Errorf("failed to parse properties: %w", err) } var buffer []byte buf := bytes.NewBuffer(buffer) - if err := ParseProperties(crd.Spec.Group, version.Name, crd.Spec.Names.Kind, version.Schema.OpenAPIV3Schema.Properties, buf, 0, false, comments); err != nil { + if err := parser.ParseProperties(version.Name, buf, version.Schema.OpenAPIV3Schema.Properties, RootRequiredFields); err != nil { return fmt.Errorf("failed to generate yaml sample: %w", err) } versions = append(versions, Version{ @@ -113,7 +116,7 @@ type Property struct { // parseCRD takes the properties and constructs a linked list out of the embedded properties that the recursive // template can call and construct linked divs. -func parseCRD(properties map[string]v1beta1.JSONSchemaProps, version string, requiredList []string) ([]*Property, error) { +func parseCRD(properties map[string]v1beta1.JSONSchemaProps, version string, minimal bool, requiredList []string) ([]*Property, error) { output := make([]*Property, 0, len(properties)) sortedKeys := make([]string, 0, len(properties)) @@ -123,6 +126,11 @@ func parseCRD(properties map[string]v1beta1.JSONSchemaProps, version string, req sort.Strings(sortedKeys) for _, k := range sortedKeys { + if minimal { + if !slices.Contains(requiredList, k) { + continue + } + } // Create the Property with the values necessary. // Check if there are properties for it in Properties or in Array -> Properties. // If yes, call parseCRD and add the result to the created properties Properties list. @@ -153,21 +161,21 @@ func parseCRD(properties map[string]v1beta1.JSONSchemaProps, version string, req switch { case len(properties[k].Properties) > 0 && properties[k].AdditionalProperties == nil: requiredList = v.Required - out, err := parseCRD(properties[k].Properties, version, requiredList) + out, err := parseCRD(properties[k].Properties, version, minimal, requiredList) if err != nil { return nil, err } p.Properties = out case properties[k].Type == array && properties[k].Items.Schema != nil && len(properties[k].Items.Schema.Properties) > 0: requiredList = v.Required - out, err := parseCRD(properties[k].Items.Schema.Properties, version, requiredList) + out, err := parseCRD(properties[k].Items.Schema.Properties, version, minimal, requiredList) if err != nil { return nil, err } p.Properties = out case properties[k].AdditionalProperties != nil: requiredList = v.Required - out, err := parseCRD(properties[k].AdditionalProperties.Schema.Properties, version, requiredList) + out, err := parseCRD(properties[k].AdditionalProperties.Schema.Properties, version, minimal, requiredList) if err != nil { return nil, err } diff --git a/pkg/generate.go b/pkg/generate.go index 1e91b99..edf7dd2 100644 --- a/pkg/generate.go +++ b/pkg/generate.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io" + "slices" "sort" "strings" @@ -12,16 +13,18 @@ import ( const array = "array" +var RootRequiredFields = []string{"apiVersion", "kind", "spec"} + // Generate takes a CRD content and path, and outputs. -func Generate(crd *v1beta1.CustomResourceDefinition, w io.WriteCloser, enableComments bool) (err error) { +func Generate(crd *v1beta1.CustomResourceDefinition, w io.WriteCloser, enableComments, minimal bool) (err error) { defer func() { if cerr := w.Close(); cerr != nil { err = errors.Join(err, cerr) } }() - + parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, enableComments, minimal) for i, version := range crd.Spec.Versions { - if err := ParseProperties(crd.Spec.Group, version.Name, crd.Spec.Names.Kind, version.Schema.OpenAPIV3Schema.Properties, w, 0, false, enableComments); err != nil { + if err := parser.ParseProperties(version.Name, w, version.Schema.OpenAPIV3Schema.Properties, RootRequiredFields); err != nil { return fmt.Errorf("failed to parse properties: %w", err) } @@ -46,10 +49,29 @@ func (w *writer) write(wc io.Writer, msg string) { _, w.err = wc.Write([]byte(msg)) } +type Parser struct { + comments bool + inArray bool + indent int + group string + kind string + onlyRequired bool +} + +// NewParser creates a new parser contains most of the things that do not change over each call. +func NewParser(group, kind string, comments, requiredOnly bool) *Parser { + return &Parser{ + group: group, + kind: kind, + comments: comments, + onlyRequired: requiredOnly, + } +} + // ParseProperties takes a writer and puts out any information / properties it encounters during the runs. // It will recursively parse every "properties:" and "additionalProperties:". Using the types, it will also output // some sample data based on those types. -func ParseProperties(group, version, kind string, properties map[string]v1beta1.JSONSchemaProps, file io.Writer, indent int, inArray, comments bool) error { +func (p *Parser) ParseProperties(version string, file io.Writer, properties map[string]v1beta1.JSONSchemaProps, requiredFields []string) error { sortedKeys := make([]string, 0, len(properties)) for k := range properties { sortedKeys = append(sortedKeys, k) @@ -58,31 +80,38 @@ func ParseProperties(group, version, kind string, properties map[string]v1beta1. w := &writer{} for _, k := range sortedKeys { - if inArray { + // if field is not required, skip the entire flow. + if p.onlyRequired { + if !slices.Contains(requiredFields, k) { + continue + } + } + + if p.inArray { w.write(file, k+":") - inArray = false + p.inArray = false } else { - if comments && properties[k].Description != "" { + if p.comments && properties[k].Description != "" { comment := strings.Builder{} multiLine := strings.Split(properties[k].Description, "\n") for _, line := range multiLine { - comment.WriteString(fmt.Sprintf("%s# %s\n", strings.Repeat(" ", indent), line)) + comment.WriteString(fmt.Sprintf("%s# %s\n", strings.Repeat(" ", p.indent), line)) } w.write(file, comment.String()) } - w.write(file, fmt.Sprintf("%s%s:", strings.Repeat(" ", indent), k)) + w.write(file, fmt.Sprintf("%s%s:", strings.Repeat(" ", p.indent), k)) } switch { case len(properties[k].Properties) == 0 && properties[k].AdditionalProperties == nil: if k == "apiVersion" { - w.write(file, fmt.Sprintf(" %s/%s\n", group, version)) + w.write(file, fmt.Sprintf(" %s/%s\n", p.group, version)) continue } if k == "kind" { - w.write(file, fmt.Sprintf(" %s\n", kind)) + w.write(file, fmt.Sprintf(" %s\n", p.kind)) continue } @@ -90,10 +119,13 @@ func ParseProperties(group, version, kind string, properties map[string]v1beta1. // we need to reparse all of them again. var result string if properties[k].Type == array && properties[k].Items.Schema != nil && len(properties[k].Items.Schema.Properties) > 0 { - w.write(file, fmt.Sprintf("\n%s- ", strings.Repeat(" ", indent))) - if err := ParseProperties(group, version, kind, properties[k].Items.Schema.Properties, file, indent+2, true, comments); err != nil { + w.write(file, fmt.Sprintf("\n%s- ", strings.Repeat(" ", p.indent))) + p.indent += 2 + p.inArray = true + if err := p.ParseProperties(version, file, properties[k].Items.Schema.Properties, properties[k].Items.Schema.Required); err != nil { return err } + p.indent -= 2 } else { result = outputValueType(properties[k]) w.write(file, fmt.Sprintf(" %s\n", result)) @@ -101,17 +133,22 @@ func ParseProperties(group, version, kind string, properties map[string]v1beta1. case len(properties[k].Properties) > 0: w.write(file, "\n") // recursively parse all sub-properties - if err := ParseProperties(group, version, kind, properties[k].Properties, file, indent+2, false, comments); err != nil { + p.indent += 2 + if err := p.ParseProperties(version, file, properties[k].Properties, properties[k].Required); err != nil { return err } + p.indent -= 2 case properties[k].AdditionalProperties != nil: if len(properties[k].AdditionalProperties.Schema.Properties) == 0 { w.write(file, " {}\n") } else { w.write(file, "\n") - if err := ParseProperties(group, version, kind, properties[k].AdditionalProperties.Schema.Properties, file, indent+2, false, comments); err != nil { + + p.indent += 2 + if err := p.ParseProperties(version, file, properties[k].AdditionalProperties.Schema.Properties, properties[k].AdditionalProperties.Schema.Required); err != nil { return err } + p.indent -= 2 } } } diff --git a/pkg/generate_test.go b/pkg/generate_test.go index d946f58..cb6a69e 100644 --- a/pkg/generate_test.go +++ b/pkg/generate_test.go @@ -23,7 +23,8 @@ func TestGenerate(t *testing.T) { buffer := bytes.NewBuffer(output) version := crd.Spec.Versions[0] - require.NoError(t, ParseProperties(crd.Spec.Group, version.Name, crd.Spec.Names.Kind, version.Schema.OpenAPIV3Schema.Properties, buffer, 0, false, false)) + parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, false, false) + require.NoError(t, parser.ParseProperties(version.Name, buffer, version.Schema.OpenAPIV3Schema.Properties, RootRequiredFields)) golden, err := os.ReadFile(filepath.Join("testdata", "sample_crd_golden.yaml")) require.NoError(t, err) @@ -41,11 +42,72 @@ func TestGenerateWithExample(t *testing.T) { var output []byte buffer := bytes.NewBuffer(output) + parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, false, false) version := crd.Spec.Versions[0] - require.NoError(t, ParseProperties(crd.Spec.Group, version.Name, crd.Spec.Names.Kind, version.Schema.OpenAPIV3Schema.Properties, buffer, 0, false, false)) + require.NoError(t, parser.ParseProperties(version.Name, buffer, version.Schema.OpenAPIV3Schema.Properties, RootRequiredFields)) golden, err := os.ReadFile(filepath.Join("testdata", "sample_crd_with_example_golden.yaml")) require.NoError(t, err) assert.Equal(t, golden, buffer.Bytes()) } + +func TestGenerateWithComments(t *testing.T) { + content, err := os.ReadFile(filepath.Join("testdata", "sample_crd.yaml")) + require.NoError(t, err) + + crd := &v1beta1.CustomResourceDefinition{} + require.NoError(t, yaml.Unmarshal(content, crd)) + + var output []byte + buffer := bytes.NewBuffer(output) + + parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, true, false) + version := crd.Spec.Versions[0] + require.NoError(t, parser.ParseProperties(version.Name, buffer, version.Schema.OpenAPIV3Schema.Properties, RootRequiredFields)) + + golden, err := os.ReadFile(filepath.Join("testdata", "sample_crd_with_comments_golden.yaml")) + require.NoError(t, err) + + assert.Equal(t, golden, buffer.Bytes()) +} + +func TestGenerateMinimal(t *testing.T) { + content, err := os.ReadFile(filepath.Join("testdata", "sample_crd.yaml")) + require.NoError(t, err) + + crd := &v1beta1.CustomResourceDefinition{} + require.NoError(t, yaml.Unmarshal(content, crd)) + + var output []byte + buffer := bytes.NewBuffer(output) + + parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, false, true) + version := crd.Spec.Versions[0] + require.NoError(t, parser.ParseProperties(version.Name, buffer, version.Schema.OpenAPIV3Schema.Properties, RootRequiredFields)) + + golden, err := os.ReadFile(filepath.Join("testdata", "sample_crd_with_minimal_example_golden.yaml")) + require.NoError(t, err) + + assert.Equal(t, golden, buffer.Bytes()) +} + +func TestGenerateMinimalWithExample(t *testing.T) { + content, err := os.ReadFile(filepath.Join("testdata", "sample_crd_with_example.yaml")) + require.NoError(t, err) + + crd := &v1beta1.CustomResourceDefinition{} + require.NoError(t, yaml.Unmarshal(content, crd)) + + var output []byte + buffer := bytes.NewBuffer(output) + + parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, false, true) + version := crd.Spec.Versions[0] + require.NoError(t, parser.ParseProperties(version.Name, buffer, version.Schema.OpenAPIV3Schema.Properties, RootRequiredFields)) + + golden, err := os.ReadFile(filepath.Join("testdata", "sample_crd_with_minimal_example_with_example_for_field_golden.yaml")) + require.NoError(t, err) + + assert.Equal(t, golden, buffer.Bytes()) +} diff --git a/pkg/testdata/sample_crd_with_comments_golden.yaml b/pkg/testdata/sample_crd_with_comments_golden.yaml new file mode 100644 index 0000000..b99d383 --- /dev/null +++ b/pkg/testdata/sample_crd_with_comments_golden.yaml @@ -0,0 +1,25 @@ +# APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources +apiVersion: delivery.krok.app/v1alpha1 +# Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds +kind: KrokCommand +metadata: {} +# KrokCommandSpec defines the desired state of KrokCommand +spec: + # CommandHasOutputToWrite if defined, it signals the underlying Job, to put its output into a generated and created secret. + commandHasOutputToWrite: true + # Dependencies defines a list of command names that this command depends on. + dependencies: ["string"] + # Enabled defines if this command can be executed or not. + enabled: true + # Image defines the image name and tag of the command example: krok-hook/slack-notification:v0.0.1 + image: string + # Platforms holds all the platforms which this command supports. + platforms: ["string"] + # ReadInputFromSecret if defined, the command will take a list of key/value pairs in a secret and apply them as arguments to the command. + readInputFromSecret: + name: string + namespace: string + # Schedule of the command. example: 0 * * * * // follows cron job syntax. + schedule: string +# KrokCommandStatus defines the observed state of KrokCommand +status: {} diff --git a/pkg/testdata/sample_crd_with_minimal_example_golden.yaml b/pkg/testdata/sample_crd_with_minimal_example_golden.yaml new file mode 100644 index 0000000..effefa2 --- /dev/null +++ b/pkg/testdata/sample_crd_with_minimal_example_golden.yaml @@ -0,0 +1,4 @@ +apiVersion: delivery.krok.app/v1alpha1 +kind: KrokCommand +spec: + image: string diff --git a/pkg/testdata/sample_crd_with_minimal_example_with_example_for_field_golden.yaml b/pkg/testdata/sample_crd_with_minimal_example_with_example_for_field_golden.yaml new file mode 100644 index 0000000..6b27da6 --- /dev/null +++ b/pkg/testdata/sample_crd_with_minimal_example_with_example_for_field_golden.yaml @@ -0,0 +1,4 @@ +apiVersion: delivery.krok.app/v1alpha1 +kind: KrokCommand +spec: + image: "krok-hook/slack-notification:v0.0.1" diff --git a/sample-crd/awsclusters.infrastructure.cluster.x-k8s.io_sample.html b/sample-crd/awsclusters.infrastructure.cluster.x-k8s.io_sample.html index 0690d88..a5b20e2 100644 --- a/sample-crd/awsclusters.infrastructure.cluster.x-k8s.io_sample.html +++ b/sample-crd/awsclusters.infrastructure.cluster.x-k8s.io_sample.html @@ -356,6 +356,8 @@

+ required +
@@ -375,6 +377,8 @@

+ required +
@@ -413,6 +417,8 @@

+ required +
@@ -4061,6 +4067,8 @@

+ required +
@@ -4080,6 +4088,8 @@

+ required +
@@ -4118,6 +4128,8 @@

+ required +
diff --git a/sample-crd/krokcommands.delivery.krok.app_sample.html b/sample-crd/krokcommands.delivery.krok.app_sample.html new file mode 100644 index 0000000..d52fdb7 --- /dev/null +++ b/sample-crd/krokcommands.delivery.krok.app_sample.html @@ -0,0 +1,186 @@ + + + + + + + + + Preview CRDs + + + + + + + + +
+
+
+ +

+ Version: delivery.krok.app/v1alpha1
+ Kind: KrokCommand +

+

+

+

KrokCommand is the Schema for the krokcommands API

+
+ +
+
+
+
# APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+apiVersion: delivery.krok.app/v1alpha1
+# Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+kind: KrokCommand
+# KrokCommandSpec defines the desired state of KrokCommand
+spec:
+  # Image defines the image name and tag of the command example: krok-hook/slack-notification:v0.0.1
+  image: string
+
+
+
+
+

+
+ + +
+
+ +
+ + apiVersion string + + + + + required + + +
+
+

APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

+
+ + + +
+
+
+ +
+ + kind string + + + + + required + + +
+
+

Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

+
+ + + +
+
+
+ +
+ + spec object + + + + + required + + +
+
+

KrokCommandSpec defines the desired state of KrokCommand

+
+ + + +
+ + image string + + + + + required + + +
+
+

Image defines the image name and tag of the command example: krok-hook/slack-notification:v0.0.1

+
+
+ + + + +
+
+
+
+ + + +
+
+
+ +
+ +
+
+
+ + + + + + diff --git a/wasm/app-worker.js b/wasm/app-worker.js index 6672202..68b869c 100644 --- a/wasm/app-worker.js +++ b/wasm/app-worker.js @@ -1,8 +1,8 @@ -const cacheName = "app-" + "f12aae0089af3c57e2d86eef472876387bf42dc1"; +const cacheName = "app-" + "0bbe4f618b019a028888bfd14fb564d3bd34e87d"; const resourcesToCache = ["/","/app.css","/app.js","/manifest.webmanifest","/wasm_exec.js","/web/app.wasm","/web/css/alert.css","/web/img/logo.png","https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js","https://cdn.jsdelivr.net/npm/halfmoon@2.0.1/css/halfmoon.min.css","https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.11/clipboard.min.js","https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css","https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-twilight.min.css"]; self.addEventListener("install", (event) => { - console.log("installing app worker f12aae0089af3c57e2d86eef472876387bf42dc1"); + console.log("installing app worker 0bbe4f618b019a028888bfd14fb564d3bd34e87d"); event.waitUntil( caches @@ -28,7 +28,7 @@ self.addEventListener("activate", (event) => { ); }) ); - console.log("app worker f12aae0089af3c57e2d86eef472876387bf42dc1 is activated"); + console.log("app worker 0bbe4f618b019a028888bfd14fb564d3bd34e87d is activated"); }); self.addEventListener("fetch", (event) => { diff --git a/wasm/app.go b/wasm/app.go index 8930212..bb0e3d1 100644 --- a/wasm/app.go +++ b/wasm/app.go @@ -62,14 +62,15 @@ func (h *crdView) Render() app.UI { } versions := make([]Version, 0) + parser := pkg.NewParser(crd.Spec.Group, crd.Spec.Names.Kind, h.comment, false) for _, version := range crd.Spec.Versions { - out, err := parseCRD(version.Schema.OpenAPIV3Schema.Properties, version.Name, version.Schema.OpenAPIV3Schema.Required) + out, err := parseCRD(version.Schema.OpenAPIV3Schema.Properties, version.Name, pkg.RootRequiredFields) if err != nil { return h.buildError(err) } var buffer []byte buf := bytes.NewBuffer(buffer) - if err := pkg.ParseProperties(crd.Spec.Group, version.Name, crd.Spec.Names.Kind, version.Schema.OpenAPIV3Schema.Properties, buf, 0, false, h.comment); err != nil { + if err := parser.ParseProperties(version.Name, buf, version.Schema.OpenAPIV3Schema.Properties, pkg.RootRequiredFields); err != nil { return h.buildError(err) } versions = append(versions, Version{ diff --git a/wasm/app.js b/wasm/app.js index 86708ff..92c9f7b 100644 --- a/wasm/app.js +++ b/wasm/app.js @@ -5,7 +5,7 @@ var goappNav = function () {}; var goappOnUpdate = function () {}; var goappOnAppInstallChange = function () {}; -const goappEnv = {"GOAPP_INTERNAL_URLS":"null","GOAPP_ROOT_PREFIX":"","GOAPP_STATIC_RESOURCES_URL":"","GOAPP_VERSION":"f12aae0089af3c57e2d86eef472876387bf42dc1"}; +const goappEnv = {"GOAPP_INTERNAL_URLS":"null","GOAPP_ROOT_PREFIX":"","GOAPP_STATIC_RESOURCES_URL":"","GOAPP_VERSION":"0bbe4f618b019a028888bfd14fb564d3bd34e87d"}; const goappLoadingLabel = "{progress}%"; const goappWasmContentLengthHeader = ""; diff --git a/wasm/index.html b/wasm/index.html index d3f7d8b..deaeae1 100644 --- a/wasm/index.html +++ b/wasm/index.html @@ -6,25 +6,25 @@ - - - + + + - + Preview CRDs - - - + + + - + - - - + + + @@ -62,7 +62,7 @@
diff --git a/wasm/web/app.wasm b/wasm/web/app.wasm index c6d7ce9..e621742 100755 Binary files a/wasm/web/app.wasm and b/wasm/web/app.wasm differ