From 1fd501647f55c9bec2be97bc16ca2575bb1a0ab5 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Wed, 17 Apr 2024 19:37:40 +0200 Subject: [PATCH] feat: Add possibility to parse comments --- README.md | 1 + cmd/helm-schema/cli.go | 2 + cmd/helm-schema/main.go | 16 ++++++- pkg/util/file.go | 104 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 121 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9a351b4..3ece8e3 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ Flags: -o, --output-file string "jsonschema file path relative to each chart directory to which jsonschema will be written (default 'values.schema.json')" -f, --value-files strings "filenames to check for chart values (default [values.yaml])" -k, --skip-auto-generation strings "skip the auto generation for these fields (default [])" + -u, --uncomment "Consinder yaml which is commented out" -v, --version "version for helm-schema" ``` diff --git a/cmd/helm-schema/cli.go b/cmd/helm-schema/cli.go index 0e80c62..3ae69eb 100644 --- a/cmd/helm-schema/cli.go +++ b/cmd/helm-schema/cli.go @@ -52,6 +52,8 @@ func newCommand(run func(cmd *cobra.Command, args []string) error) (*cobra.Comma BoolP("dry-run", "d", false, "don't actually create files just print to stdout passed") cmd.PersistentFlags(). BoolP("keep-full-comment", "s", false, "Keep the whole leading comment (default: cut at empty line)") + cmd.PersistentFlags(). + BoolP("uncomment", "u", false, "Consinder yaml which is commented out") cmd.PersistentFlags(). BoolP("dont-strip-helm-docs-prefix", "x", false, "Disable the removal of the helm-docs prefix (--)") cmd.PersistentFlags(). diff --git a/cmd/helm-schema/main.go b/cmd/helm-schema/main.go index 57f5c2f..40b300b 100644 --- a/cmd/helm-schema/main.go +++ b/cmd/helm-schema/main.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "errors" "fmt" "os" @@ -47,7 +48,7 @@ type Result struct { } func worker( - dryRun, keepFullComment, dontRemoveHelmDocsPrefix bool, + dryRun, uncomment, keepFullComment, dontRemoveHelmDocsPrefix bool, valueFileNames []string, skipAutoGenerationConfig *schema.SkipAutoGenerationConfig, outFile string, @@ -113,6 +114,17 @@ func worker( continue } + // Optional preprocessing + if uncomment { + // Remove comments from valid yaml + content, err = util.RemoveCommentsFromYaml(bytes.NewReader(content)) + if err != nil { + result.Errors = append(result.Errors, err) + results <- result + continue + } + } + var values yaml.Node err = yaml.Unmarshal(content, &values) if err != nil { @@ -136,6 +148,7 @@ func exec(cmd *cobra.Command, _ []string) error { dryRun := viper.GetBool("dry-run") noDeps := viper.GetBool("no-dependencies") keepFullComment := viper.GetBool("keep-full-comment") + uncomment := viper.GetBool("uncomment") outFile := viper.GetString("output-file") dontRemoveHelmDocsPrefix := viper.GetBool("dont-strip-helm-docs-prefix") if err := viper.UnmarshalKey("value-files", &valueFileNames); err != nil { @@ -174,6 +187,7 @@ func exec(cmd *cobra.Command, _ []string) error { defer wg.Done() worker( dryRun, + uncomment, keepFullComment, dontRemoveHelmDocsPrefix, valueFileNames, diff --git a/pkg/util/file.go b/pkg/util/file.go index 9db09e2..86e215a 100644 --- a/pkg/util/file.go +++ b/pkg/util/file.go @@ -1,8 +1,12 @@ package util import ( + "bufio" "io" + "regexp" "strings" + + "gopkg.in/yaml.v3" ) // ReadFileAndFixNewline reads the content of a io.Reader and replaces \r\n with \n @@ -11,5 +15,103 @@ func ReadFileAndFixNewline(reader io.Reader) ([]byte, error) { if err != nil { return nil, err } - return []byte(strings.Replace(string(content), "\r\n", "\n", -1)), nil + return []byte(strings.ReplaceAll(string(content), "\r\n", "\n")), nil +} + +func appendAndNL(to, from *[]byte) { + if from != nil { + *to = append(*to, *from...) + } + *to = append(*to, '\n') +} + +func appendAndNLStr(to *[]byte, from string) { + *to = append(*to, from...) + *to = append(*to, '\n') +} + +// RemoveCommentsFromYaml tries to remove comments if they contain valid yaml +func RemoveCommentsFromYaml(reader io.Reader) ([]byte, error) { + result := make([]byte, 0) + buff := make([]byte, 0) + scanner := bufio.NewScanner(reader) + + commentMatcher := regexp.MustCompile(`^\s*#\s*`) + schemaMatcher := regexp.MustCompile(`^\s*#\s@schema\s*`) + + var line string + var inCode, inSchema bool + var codeIndention int + var unknownYaml interface{} + + for scanner.Scan() { + line = scanner.Text() + + // If the line is empty and we are parsing a block of potential yaml, + // the parsed block of yaml is "finished" and should be added to the + // result + if line == "" && inCode { + appendAndNL(&result, &buff) + appendAndNLStr(&result, line) + // reset + inCode = false + buff = make([]byte, 0) + continue + } + + // Line contains @schema + // The following lines will be added to result + if schemaMatcher.Match([]byte(line)) { + inSchema = !inSchema + appendAndNLStr(&result, line) + continue + } + + // Inside a @schema block + if inSchema { + appendAndNLStr(&result, line) + continue + } + + // Havent found a potential yaml block yet + if !inCode { + if match := commentMatcher.Find([]byte(line)); match != nil { + codeIndention = len(match) + inCode = true + } + } + + // Try if this line is valid yaml + if inCode { + if commentMatcher.Match([]byte(line)) { + // Strip the commet away + strippedLine := line[codeIndention:] + // add it to the already parsed valid yaml + appendAndNLStr(&buff, strippedLine) + // check if the new block is still valid yaml + err := yaml.Unmarshal(buff, &unknownYaml) + if err != nil { + // Invalid yaml found, + // Remove the stripped line again + buff = buff[:len(buff)-len(strippedLine)-1] + // and add the commented line instead + appendAndNLStr(&buff, line) + } + // its still valid yaml + continue + } + + // If the line is not a comment it must be yaml + appendAndNLStr(&buff, line) + continue + } + // line is valid yaml + appendAndNLStr(&result, line) + } + + if len(buff) > 0 { + appendAndNL(&result, &buff) + } + + return result, nil }