forked from helm/helm
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HIP-0019 adds .helmlintignore capability
See HIP-0019 proposal at helm/community: helm/community#353 Co-authored-by: Danilo Patrucco <[email protected]> Signed-off-by: Daniel J. Pritchett <[email protected]>
- Loading branch information
1 parent
c86e0d3
commit f93c255
Showing
5 changed files
with
517 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
Copyright The Helm Authors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
//Package ignore | ||
/* | ||
Package ignore contains tools for linting charts. | ||
Linting is the process of testing charts for errors or warnings regarding | ||
formatting, compilation, or standards compliance. | ||
*/ | ||
package ignore // import "helm.sh/helm/v3/pkg/lint/ignore" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package ignore | ||
|
||
import ( | ||
"fmt" | ||
"helm.sh/helm/v3/pkg/lint/support" | ||
"log" | ||
) | ||
|
||
// DefaultIgnoreFileName is the name of the lint ignore file | ||
const DefaultIgnoreFileName = ".helmlintignore" | ||
|
||
var debugFn func(format string, v ...interface{}) | ||
|
||
type Ignorer struct { | ||
ChartPath string | ||
Matchers []MatchesErrors | ||
} | ||
|
||
func NewIgnorer(chartPath string, lintIgnorePath string, debugLogFn func(string, ...interface{})) (*Ignorer, error) { | ||
debugFn = debugLogFn | ||
matchers, err := LoadFromFilePath(chartPath, lintIgnorePath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &Ignorer{ChartPath: chartPath, Matchers: matchers}, nil | ||
} | ||
|
||
// FilterMessages Verify what messages can be kept in the output, using also the error as a verification | ||
func (i *Ignorer) FilterMessages(messages []support.Message) []support.Message { | ||
out := make([]support.Message, 0, len(messages)) | ||
for _, msg := range messages { | ||
if i.ShouldKeepMessage(msg) { | ||
out = append(out, msg) | ||
} | ||
} | ||
return out | ||
} | ||
|
||
func (i *Ignorer) ShouldKeepMessage(msg support.Message) bool { | ||
return i.ShouldKeepError(msg.Err) | ||
} | ||
|
||
// ShouldKeepError is used to verify if the error associated with the message need to be kept, or it can be ignored, called by FilterMessages and in the pkg/action/lint.go Run main function | ||
func (i *Ignorer) ShouldKeepError(err error) bool { | ||
// if any of our Matchers match the rule, we can discard it | ||
for _, rule := range i.Matchers { | ||
if rule.Match(err) { | ||
debug("lint ignore rule matched this error, we should suppress it.", "errText", err.Error(), rule.LogAttrs()) | ||
return false | ||
} | ||
} | ||
|
||
// if we can't find a reason to discard it, we keep it | ||
debug("no lint ignore rules matched this error, we should NOT suppress it.", "errText", err.Error()) | ||
return true | ||
} | ||
|
||
var defaultDebugFn = func(format string, v ...interface{}) { | ||
format = fmt.Sprintf("[debug] %s\n", format) | ||
_ = log.Output(2, fmt.Sprintf(format, v...)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package ignore | ||
|
||
import ( | ||
"log/slog" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
const pathlessRulePrefix = "error_lint_ignore=" | ||
|
||
type MatchesErrors interface { | ||
Match(error) bool | ||
LogAttrs() slog.Attr | ||
} | ||
|
||
type BadTemplateRule struct { | ||
RuleText string | ||
MessageText string | ||
BadTemplatePath string | ||
} | ||
|
||
type PathlessRule struct { | ||
RuleText string | ||
MessageText string | ||
} | ||
|
||
// Match errors that have no file path in their body with ignorer rules. | ||
// An examples of errors with no file path in their body is chart metadata errors `chart metadata is missing these dependencies` | ||
func (pr PathlessRule) Match(err error) bool { | ||
errText := err.Error() | ||
matchableParts := strings.SplitN(pr.MessageText, ":", 2) | ||
matchablePrefix := strings.TrimSpace(matchableParts[0]) | ||
|
||
if match, _ := filepath.Match(pr.MessageText, errText); match { | ||
return true | ||
} | ||
if matched, _ := filepath.Match(matchablePrefix, errText); matched { | ||
return true | ||
} | ||
|
||
return false | ||
} | ||
|
||
// LogAttrs Used for troubleshooting and gathering data | ||
func (pr PathlessRule) LogAttrs() slog.Attr { | ||
return slog.Group("BadTemplateRule", | ||
slog.String("rule_text", pr.RuleText), | ||
slog.String("value", pr.MessageText), | ||
) | ||
} | ||
|
||
// LogAttrs Used for troubleshooting and gathering data | ||
func (btr BadTemplateRule) LogAttrs() slog.Attr { | ||
return slog.Group("BadTemplateRule", | ||
slog.String("rule_text", btr.RuleText), | ||
slog.String("key", btr.BadTemplatePath), | ||
slog.String("value", btr.MessageText), | ||
) | ||
} | ||
|
||
// Match errors that have a file path in their body with ignorer rules. | ||
func (btr BadTemplateRule) Match(err error) bool { | ||
errText := err.Error() | ||
pathToOffendingFile, err := pathToOffendingFile(errText) | ||
if err != nil { | ||
return false | ||
} | ||
|
||
cleanRulePath := filepath.Clean(btr.BadTemplatePath) | ||
|
||
if strings.Contains(pathToOffendingFile, cleanRulePath) { | ||
if strings.Contains(errText, btr.MessageText) { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package ignore | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
func LoadFromFilePath(chartPath, ignoreFilePath string) ([]MatchesErrors, error) { | ||
if ignoreFilePath == "" { | ||
ignoreFilePath = filepath.Join(chartPath, DefaultIgnoreFileName) | ||
debug("\nNo helm lint ignore filepath specified, will try and use the following default: %s\n", ignoreFilePath) | ||
} | ||
|
||
// attempt to load ignore patterns from ignoreFilePath. | ||
// if none are found, return an empty ignorer so the program can keep running. | ||
debug("\nTrying to load helm lint ignore file at %s\n", ignoreFilePath) | ||
file, err := os.Open(ignoreFilePath) | ||
if err != nil { | ||
debug("failed to open helm lint ignore file: %s", ignoreFilePath) | ||
return []MatchesErrors{}, nil | ||
} | ||
defer func() { | ||
err := file.Close() | ||
if err != nil { | ||
debug("failed to close helm lint ignore file: %s", ignoreFilePath) | ||
} | ||
}() | ||
|
||
matchers := LoadFromReader(file) | ||
return matchers, nil | ||
} | ||
|
||
// debug provides [pkg/lint/ignore] with a runtime-overridable logging function | ||
// intended to match the behavior of the top level debug() method from package main. | ||
// | ||
// When no debugFn is set for the package at runtime then debug will fall back to | ||
// defaultDebugFn. | ||
func debug(format string, args ...interface{}) { | ||
if debugFn == nil { | ||
defaultDebugFn(format, args...) | ||
} else { | ||
debugFn(format, args...) | ||
} | ||
return | ||
} | ||
|
||
// TODO: figure out & fix or remove | ||
func pathToOffendingFile(errText string) (string, error) { | ||
delimiter := ":" | ||
// splits into N parts delimited by colons | ||
parts := strings.Split(errText, delimiter) | ||
// if 3 or more parts, return the second part, after trimming its spaces | ||
if len(parts) > 2 { | ||
return strings.TrimSpace(parts[1]), nil | ||
} | ||
// if fewer than 3 parts, return empty string | ||
return "", fmt.Errorf("fewer than three [%s]-delimited parts found, no path here: %s", delimiter, errText) | ||
} | ||
|
||
func LoadFromReader(rdr io.Reader) []MatchesErrors { | ||
matchers := make([]MatchesErrors, 0) | ||
|
||
scanner := bufio.NewScanner(rdr) | ||
for scanner.Scan() { | ||
line := strings.TrimSpace(scanner.Text()) | ||
if line == "" || strings.HasPrefix(line, "#") { | ||
continue | ||
} | ||
|
||
if strings.HasPrefix(line, pathlessRulePrefix) { | ||
matchers = append(matchers, buildPathlessRule(line, pathlessRulePrefix)) | ||
} else { | ||
matchers = append(matchers, buildBadTemplateRule(line)) | ||
} | ||
} | ||
|
||
return matchers | ||
} | ||
|
||
func buildPathlessRule(line string, pathlessRulePrefix string) PathlessRule { | ||
// handle chart-level errors | ||
// Drop 'error_lint_ignore=' prefix from rule before saving it | ||
const numSplits = 2 | ||
tokens := strings.SplitN(line[len(pathlessRulePrefix):], pathlessRulePrefix, numSplits) | ||
if len(tokens) == numSplits { | ||
// TODO: find an example for this one - not sure we still use it | ||
messageText, _ := tokens[0], tokens[1] | ||
return PathlessRule{RuleText: line, MessageText: messageText} | ||
} else { | ||
messageText := tokens[0] | ||
return PathlessRule{RuleText: line, MessageText: messageText} | ||
} | ||
} | ||
|
||
func buildBadTemplateRule(line string) BadTemplateRule { | ||
const noMessageText = "" | ||
const separator = " " | ||
const numSplits = 2 | ||
|
||
// handle chart yaml file errors in specific template files | ||
parts := strings.SplitN(line, separator, numSplits) | ||
if len(parts) == numSplits { | ||
messagePath, messageText := parts[0], parts[1] | ||
return BadTemplateRule{RuleText: line, BadTemplatePath: messagePath, MessageText: messageText} | ||
} else { | ||
messagePath := parts[0] | ||
return BadTemplateRule{RuleText: line, BadTemplatePath: messagePath, MessageText: noMessageText} | ||
} | ||
} |
Oops, something went wrong.