diff --git a/.golangci.yaml b/.golangci.yaml index c85e3a5..4d9fce1 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,12 +1,14 @@ +version: "2" + run: - go: "1.23" + go: "1.24" timeout: 10m tests: false allow-parallel-runners: true issues-exit-code: 2 linters: - enable-all: true + default: all disable: - bodyclose - containedctx # Struct should not contain context, action does. @@ -15,18 +17,13 @@ linters: - depguard - dogsled - dupl # Check code duplications. - - execinquery - exhaustive # Doesn't really make sense. - exhaustruct # Doesn't really make sense. - - exportloopref - forcetypeassert # Priority: that can lead to serious crashes. - funlen # Break long functions. - - gci - gochecknoglobals - gochecknoinits # Init functions cause an import to have side effects, - err113 - - goimports # acts weirdly, dci handles imports anyway - - gomnd # Give constant values a name with constants. - interfacebloat - ireturn # Accept interface, return concrate. - lll @@ -45,75 +42,35 @@ linters: - wrapcheck - wsl -linters-settings: - gci: - sections: - - standard - - blank - - dot - - default - - prefix(github.com/open-component-model/ocm) - custom-order: true - funlen: - lines: 110 - statements: 60 - cyclop: - max-complexity: 60 - skip-tests: true - gocognit: - min-complexity: 60 - nolintlint: - allow-unused: false - require-explanation: true - require-specific: false - varnamelen: - ignore-names: - - err - - wg - - id - lll: - line-length: 120 - gosec: - exclude-generated: true + settings: + funlen: + lines: 110 + statements: 60 + cyclop: + max-complexity: 60 + gocognit: + min-complexity: 60 + nolintlint: + allow-unused: false + require-explanation: true + require-specific: false + varnamelen: + ignore-names: + - err + - wg + - id + revive: + rules: + - name: package-comments + disabled: true + - name: exported + disabled: true + + lll: + line-length: 120 -issues: - exclude-files: - - v1beta1/types_jsonschema.go - - v1beta1/marshal.go - - v1beta1/marshal_test.go - exclude: - - composites - exclude-rules: - - path: cmds/ - linters: - - forbidigo - - source: "https://" - linters: - - lll - - text: "shadow: declaration of \"err\"" - linters: - - govet - - text: "shadow: declaration of \"ok\"" - linters: - - govet - - path: _test\.go - linters: - - gocyclo - - errcheck - - gosec - - dupl - - funlen - - scopelint - - text: "Spec.DeepCopyInto undefined" - linters: - - typecheck - - text: "G601: Implicit memory aliasing in for loop" - # Ignored cos why not, that was the request. - linters: - - gosec - - source: "// .* #\\d+" - linters: - - godox - - path: ignore/.*\.go - linters: - - dupword + exclusions: + paths: + - v1beta1/types_jsonschema.go + - v1beta1/marshal.go + - v1beta1/marshal_test.go diff --git a/Makefile b/Makefile index 5d0ed53..d4eadf7 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ clean: ## Runs go clean go clean -i GOLANGCI_LINT ?= $(LOCALBIN)/golangci-lint -GOLANGCI_LINT_VERSION ?= v1.60.1 +GOLANGCI_LINT_VERSION ?= v2.0.2 golangci-lint: $(GOLANGCI_LINT) $(GOLANGCI_LINT): $(LOCALBIN) diff --git a/cmd/config_file_schema.go b/cmd/config_file_schema.go index 5f8fd88..75f5135 100644 --- a/cmd/config_file_schema.go +++ b/cmd/config_file_schema.go @@ -1,5 +1,6 @@ package cmd +// URLs contains url configuration. type URLs struct { URL string `json:"url"` Username string `json:"username,omitempty"` @@ -7,6 +8,7 @@ type URLs struct { Token string `json:"token,omitempty"` } +// GITUrls contains git url configuration. type GITUrls struct { URL string `json:"url"` Username string `json:"username,omitempty"` diff --git a/cmd/config_handler.go b/cmd/config_handler.go index 11a9715..be920d2 100644 --- a/cmd/config_handler.go +++ b/cmd/config_handler.go @@ -3,21 +3,24 @@ package cmd import ( "fmt" "os" + "path/filepath" "k8s.io/apimachinery/pkg/util/yaml" "github.com/Skarlso/crd-to-sample-yaml/pkg" ) +// ConfigHandler contains config. type ConfigHandler struct { configFileLocation string } +// CRDs returns schema types gathered from a config. func (h *ConfigHandler) CRDs() ([]*pkg.SchemaType, error) { if _, err := os.Stat(h.configFileLocation); os.IsNotExist(err) { return nil, fmt.Errorf("file under '%s' does not exist", h.configFileLocation) } - content, err := os.ReadFile(h.configFileLocation) + content, err := os.ReadFile(filepath.Clean(h.configFileLocation)) if err != nil { return nil, fmt.Errorf("failed to read file: %w", err) } diff --git a/cmd/crd.go b/cmd/crd.go index 51e956a..55dcb7a 100644 --- a/cmd/crd.go +++ b/cmd/crd.go @@ -13,7 +13,9 @@ import ( ) const ( + // FormatHTML is a setting that is accepted as a format output type. The type is HTML. FormatHTML = "html" + // FormatYAML is a setting that is accepted as a format output type. The type is YAML. FormatYAML = "yaml" ) @@ -35,6 +37,7 @@ type crdGenArgs struct { var crdArgs = &crdGenArgs{} +// Handler defines an interface for all CRD providers to return Schemas. type Handler interface { CRDs() ([]*pkg.SchemaType, error) } @@ -108,7 +111,7 @@ func runGenerate(_ *cobra.Command, _ []string) error { } else { outputLocation := filepath.Join(crdArgs.output, crd.Kind+"_sample."+crdArgs.format) // closed later during render - outputFile, err := os.Create(outputLocation) + outputFile, err := os.Create(filepath.Clean(outputLocation)) if err != nil { errs = append(errs, fmt.Errorf("failed to create file at: '%s': %w", outputLocation, err)) diff --git a/cmd/file_handler.go b/cmd/file_handler.go index 10204d1..23ea4ff 100644 --- a/cmd/file_handler.go +++ b/cmd/file_handler.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "os" + "path/filepath" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/util/yaml" @@ -11,16 +12,18 @@ import ( "github.com/Skarlso/crd-to-sample-yaml/pkg/sanitize" ) +// FileHandler provides options for a file provider. type FileHandler struct { location string group string } +// CRDs returns schemas parsed out of a file. func (h *FileHandler) CRDs() ([]*pkg.SchemaType, error) { if _, err := os.Stat(h.location); os.IsNotExist(err) { return nil, fmt.Errorf("file under '%s' does not exist", h.location) } - content, err := os.ReadFile(h.location) + content, err := os.ReadFile(filepath.Clean(h.location)) if err != nil { return nil, fmt.Errorf("failed to read file: %w", err) } diff --git a/cmd/folder_handler.go b/cmd/folder_handler.go index d480d5b..af7793a 100644 --- a/cmd/folder_handler.go +++ b/cmd/folder_handler.go @@ -13,11 +13,13 @@ import ( "github.com/Skarlso/crd-to-sample-yaml/pkg/sanitize" ) +// FolderHandler scans folders and returns schemas found in that folder. type FolderHandler struct { location string group string } +// CRDs goes through schemas in folders. func (h *FolderHandler) CRDs() ([]*pkg.SchemaType, error) { if _, err := os.Stat(h.location); os.IsNotExist(err) { return nil, fmt.Errorf("file under '%s' does not exist", h.location) @@ -40,7 +42,7 @@ func (h *FolderHandler) CRDs() ([]*pkg.SchemaType, error) { return nil } - content, err := os.ReadFile(path) + content, err := os.ReadFile(filepath.Clean(path)) if err != nil { return fmt.Errorf("failed to read file: %w", err) } diff --git a/cmd/git_handler.go b/cmd/git_handler.go index 4c0a686..e4c021e 100644 --- a/cmd/git_handler.go +++ b/cmd/git_handler.go @@ -19,6 +19,7 @@ import ( "github.com/Skarlso/crd-to-sample-yaml/pkg/sanitize" ) +// GitHandler contains data to parse git configuration and values. type GitHandler struct { URL string Username string @@ -32,6 +33,7 @@ type GitHandler struct { group string // this is used by the configfile. } +// CRDs returns a list of crds parsed out from crds contained in a git repository. func (g *GitHandler) CRDs() ([]*pkg.SchemaType, error) { opts, err := g.constructGitOptions() if err != nil { diff --git a/cmd/kube_handler.go b/cmd/kube_handler.go index 9d3e724..2df290d 100644 --- a/cmd/kube_handler.go +++ b/cmd/kube_handler.go @@ -15,6 +15,7 @@ import ( "github.com/Skarlso/crd-to-sample-yaml/pkg" ) +// KubeHandler contains data for a kubernetes resource. type KubeHandler struct { crd string group string @@ -23,6 +24,7 @@ type KubeHandler struct { resource string } +// CRDs returns schemas found in a cluster that have been installed. func (h *KubeHandler) CRDs() ([]*pkg.SchemaType, error) { kubeconfig := os.Getenv("KUBECONFIG") if kubeconfig == "" { diff --git a/cmd/test.go b/cmd/test.go index 130c602..6e94ee0 100644 --- a/cmd/test.go +++ b/cmd/test.go @@ -36,7 +36,7 @@ func init() { func runTest(cmd *cobra.Command, args []string) { if len(args) == 0 { - fmt.Fprintf(os.Stderr, "test needs an argument where the tests are located at") + _, _ = fmt.Fprintf(os.Stderr, "test needs an argument where the tests are located at") os.Exit(1) } @@ -81,7 +81,7 @@ func displayWarnings(warnings []tests.Outcome) error { t.AppendSeparator() t.Render() - fmt.Fprintf(os.Stdout, "\nTests total: %d, failed: %d, passed: %d\n", len(warnings), errs, len(warnings)-errs) + _, _ = fmt.Fprintf(os.Stdout, "\nTests total: %d, failed: %d, passed: %d\n", len(warnings), errs, len(warnings)-errs) if errs > 0 { return fmt.Errorf("%d test(s) failed", errs) diff --git a/go.mod b/go.mod index 17c05e3..bee9bad 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,11 @@ module github.com/Skarlso/crd-to-sample-yaml -go 1.23.0 - -toolchain go1.23.2 +go 1.24.1 require ( github.com/brianvoe/gofakeit/v6 v6.28.0 github.com/fatih/color v1.18.0 - github.com/fxamacker/cbor/v2 v2.7.0 + github.com/fxamacker/cbor/v2 v2.8.0 github.com/go-git/go-git/v5 v5.14.0 github.com/google/go-cmp v0.7.0 github.com/jedib0t/go-pretty/v6 v6.6.7 diff --git a/go.sum b/go.sum index 717c8a4..6a58f97 100644 --- a/go.sum +++ b/go.sum @@ -49,8 +49,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= +github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= diff --git a/pkg/matches/matchsnapshot/matcher.go b/pkg/matches/matchsnapshot/matcher.go index bbf0c02..931fabc 100644 --- a/pkg/matches/matchsnapshot/matcher.go +++ b/pkg/matches/matchsnapshot/matcher.go @@ -14,14 +14,17 @@ import ( "github.com/Skarlso/crd-to-sample-yaml/pkg/tests" ) +// MatcherName is the name of this matcher YAML. const MatcherName = "matchSnapshot" +// Config contains configuration details for the Matcher. Path, ignoring errors and minimal setting. type Config struct { Path string `yaml:"path"` IgnoreErrors []string `yaml:"ignoreErrors,omitempty"` Minimal bool `yaml:"minimal"` } +// Matcher is a snapshot based matcher. type Matcher struct { Updater Updater } @@ -32,6 +35,7 @@ func init() { }, MatcherName) } +// Match actually does the matching. func (m *Matcher) Match(ctx context.Context, crdLocation string, payload []byte) error { c := Config{} if err := yaml.Unmarshal(payload, &c); err != nil { @@ -79,7 +83,7 @@ func (m *Matcher) Match(ctx context.Context, crdLocation string, payload []byte) return err } - content, err := os.ReadFile(crdLocation) + content, err := os.ReadFile(filepath.Clean(crdLocation)) if err != nil { return fmt.Errorf("failed to read source template: %w", err) } @@ -89,7 +93,7 @@ func (m *Matcher) Match(ctx context.Context, crdLocation string, payload []byte) for _, s := range snapshots { // one snapshot will contain a single version and the validation // will know which version to check against - snapshotContent, err := os.ReadFile(s) + snapshotContent, err := os.ReadFile(filepath.Clean(s)) if err != nil { return fmt.Errorf("failed to read snapshot template: %w", err) } diff --git a/pkg/matches/matchsnapshot/update.go b/pkg/matches/matchsnapshot/update.go index 9e4217f..91eeebf 100644 --- a/pkg/matches/matchsnapshot/update.go +++ b/pkg/matches/matchsnapshot/update.go @@ -25,7 +25,7 @@ type Update struct{} // Update any given files in the snapshots. func (u *Update) Update(sourceTemplateLocation string, targetSnapshotLocation string, minimal bool) error { - sourceTemplate, err := os.ReadFile(sourceTemplateLocation) + sourceTemplate, err := os.ReadFile(filepath.Clean(sourceTemplateLocation)) if err != nil { return err } @@ -45,9 +45,9 @@ func (u *Update) Update(sourceTemplateLocation string, targetSnapshotLocation st if minimal { name = baseName + "-" + version.Name + ".min.yaml" } - file, err := os.OpenFile(filepath.Join(targetSnapshotLocation, name), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) + file, err := os.OpenFile(filepath.Clean(filepath.Join(targetSnapshotLocation, name)), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) if err != nil { - return fmt.Errorf("failed to open file %s: %w", filepath.Join(targetSnapshotLocation, name), err) + return fmt.Errorf("failed to open file %s: %w", filepath.Clean(filepath.Join(targetSnapshotLocation, name)), err) } parser := pkg.NewParser(schemaType.Group, schemaType.Kind, false, minimal, true) @@ -65,12 +65,14 @@ func (u *Update) Update(sourceTemplateLocation string, targetSnapshotLocation st if minimal { name = baseName + "-" + schemaType.Validation.Name + ".min.yaml" } - file, err := os.OpenFile(filepath.Join(targetSnapshotLocation, name), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) + file, err := os.OpenFile(filepath.Clean(filepath.Join(targetSnapshotLocation, name)), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) if err != nil { - return fmt.Errorf("failed to open file %s: %w", filepath.Join(targetSnapshotLocation, name), err) + return fmt.Errorf("failed to open file %s: %w", filepath.Clean(filepath.Join(targetSnapshotLocation, name)), err) } - defer file.Close() + defer func() { + _ = file.Close() + }() schemaType.Validation.Schema.Properties["kind"] = v1beta1.JSONSchemaProps{} schemaType.Validation.Schema.Properties["apiVersion"] = v1beta1.JSONSchemaProps{} diff --git a/pkg/matches/matchstring/matcher.go b/pkg/matches/matchstring/matcher.go index 9574d15..d319f21 100644 --- a/pkg/matches/matchstring/matcher.go +++ b/pkg/matches/matchstring/matcher.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "path/filepath" "k8s.io/apimachinery/pkg/util/yaml" @@ -11,19 +12,22 @@ import ( "github.com/Skarlso/crd-to-sample-yaml/pkg/tests" ) +// Matcher defines a match for String based matching. type Matcher struct{} +// Config contains errors that could be ignored by this matcher. type Config struct { IgnoreErrors []string `yaml:"ignoreErrors,omitempty"` } +// Match does the actual Match job. func (m *Matcher) Match(_ context.Context, crdLocation string, payload []byte) error { c := &Config{} if err := yaml.Unmarshal(payload, &c); err != nil { return err } - crdContent, err := os.ReadFile(crdLocation) + crdContent, err := os.ReadFile(filepath.Clean(crdLocation)) if err != nil { return fmt.Errorf("error reading file %s: %w", crdLocation, err) } diff --git a/pkg/matches/validate.go b/pkg/matches/validate.go index 6a7eb13..46f9ea6 100644 --- a/pkg/matches/validate.go +++ b/pkg/matches/validate.go @@ -56,6 +56,7 @@ func Validate(sourceCRD []byte, sampleFile []byte, ignoreErrors []string) error ) } +// ValidateCRDValidation takes a definition, converts it from a CRD to an unstructured and runs validation. func ValidateCRDValidation(crd *apiextensions.CustomResourceDefinition, sampleFile []byte, ignoreErrors []string) error { reader := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(sampleFile), maxBufferSize) obj := &unstructured.Unstructured{} diff --git a/pkg/sanitize/sanitizer.go b/pkg/sanitize/sanitizer.go index 556e499..602ec20 100644 --- a/pkg/sanitize/sanitizer.go +++ b/pkg/sanitize/sanitizer.go @@ -1,9 +1,13 @@ +// Package sanitize contains code regarding cleaning up a template that might contain +// Helm templating values. + package sanitize import ( "bytes" ) +// Sanitize takes a content and removes any potential Helm related values in the template. func Sanitize(content []byte) ([]byte, error) { // bail early if there are no template characters in the CRD if !bytes.Contains(content, []byte("{{")) { diff --git a/pkg/tests/suite.go b/pkg/tests/suite.go index 489d3fa..e437976 100644 --- a/pkg/tests/suite.go +++ b/pkg/tests/suite.go @@ -43,11 +43,13 @@ type SuiteRunner struct { Update bool } +// Test contains all the `Its` and `Asserts` that can be configured. type Test struct { It string `json:"it"` Asserts []*apiextensionsv1.JSON `json:"asserts"` } +// Suite contains all tests and a template that is the subject of the test. type Suite struct { Suite string `json:"suite"` Tests []Test `json:"tests"` @@ -160,7 +162,7 @@ func (s *SuiteRunner) constructTestMatrix() (map[string][]Test, error) { // for each template, gather the `tests`s testMatrix := map[string][]Test{} for _, testFile := range testFiles { - content, err := os.ReadFile(testFile) + content, err := os.ReadFile(filepath.Clean(testFile)) if err != nil { return nil, fmt.Errorf("os.ReadFile() returned %w", err) } diff --git a/wasm/app.go b/wasm/app.go index fd6920b..17a34d8 100644 --- a/wasm/app.go +++ b/wasm/app.go @@ -1,3 +1,5 @@ +// Package main contains the application main code for the WASM codebase. + package main import (