[Prototyping] load linter config from repository (#97325)
This commit is contained in:
1 parent
a722b485da
commit
8058b3fa89
5 files changed
+69
-20
No files matched your search
@@ -8,10 +8,35 @@ import (
|
||||
|
||||
"github.com/grafana/dashboard-linter/lint"
|
||||
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type DashboardLinterFactory struct{}
|
||||
|
||||
func NewDashboardLinterFactory() *DashboardLinterFactory {
|
||||
return &DashboardLinterFactory{}
|
||||
}
|
||||
|
||||
func (f *DashboardLinterFactory) New() (Linter, error) {
|
||||
return &DashboardLinter{rules: lint.NewRuleSet()}, nil
|
||||
}
|
||||
|
||||
func (f *DashboardLinterFactory) NewFromConfig(cfg []byte) (Linter, error) {
|
||||
var lintCfg lint.ConfigurationFile
|
||||
if err := yaml.Unmarshal(cfg, &lintCfg); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal linter config: %w", err)
|
||||
}
|
||||
|
||||
return &DashboardLinter{rules: lint.NewRuleSet(), cfg: lintCfg}, nil
|
||||
}
|
||||
|
||||
func (f *DashboardLinterFactory) ConfigPath() string {
|
||||
return ".dashboard-lint"
|
||||
}
|
||||
|
||||
type DashboardLinter struct {
|
||||
rules lint.RuleSet
|
||||
cfg lint.ConfigurationFile
|
||||
}
|
||||
|
||||
// FIXME: what would be a good place to put all the schema validation?
|
||||
@@ -40,6 +65,8 @@ func (l *DashboardLinter) Lint(ctx context.Context, fileData []byte) ([]provisio
|
||||
return nil, fmt.Errorf("failed to lint dashboard: %v", err)
|
||||
}
|
||||
|
||||
results.Configure(&l.cfg)
|
||||
|
||||
byRule := results.ByRule()
|
||||
rules := make([]string, 0, len(byRule))
|
||||
for r := range byRule {
|
||||
|
||||
@@ -9,3 +9,9 @@ import (
|
||||
type Linter interface {
|
||||
Lint(ctx context.Context, data []byte) ([]provisioning.LintIssue, error)
|
||||
}
|
||||
|
||||
type LinterFactory interface {
|
||||
ConfigPath() string
|
||||
NewFromConfig(cfg []byte) (Linter, error)
|
||||
New() (Linter, error)
|
||||
}
|
||||
@@ -242,15 +242,14 @@ func (b *ProvisioningAPIBuilder) asRepository(ctx context.Context, obj runtime.O
|
||||
return nil, fmt.Errorf("invalid base URL: %w", err)
|
||||
}
|
||||
|
||||
linter := lint.NewDashboardLinter()
|
||||
|
||||
// HACK: replace resource name placeholder in the webhook URL with the actual resource name
|
||||
// In case the name was automatically generated by k8s
|
||||
if r.Spec.GitHub.WebhookURL != "" {
|
||||
r.Spec.GitHub.WebhookURL = strings.ReplaceAll(r.Spec.GitHub.WebhookURL, resourceNamePlaceholder, r.GetName())
|
||||
}
|
||||
|
||||
return repository.NewGitHub(ctx, r, b.ghFactory, baseURL, linter, b.renderer), nil
|
||||
linterFactory := lint.NewDashboardLinterFactory()
|
||||
return repository.NewGitHub(ctx, r, b.ghFactory, baseURL, linterFactory, b.renderer), nil
|
||||
case provisioning.S3RepositoryType:
|
||||
return repository.NewS3(r), nil
|
||||
default:
|
||||
|
||||
@@ -25,12 +25,12 @@ import (
|
||||
)
|
||||
|
||||
type githubRepository struct {
|
||||
logger *slog.Logger
|
||||
config *provisioning.Repository
|
||||
gh pgh.Client
|
||||
baseURL *url.URL
|
||||
linter lint.Linter
|
||||
renderer PreviewRenderer
|
||||
logger *slog.Logger
|
||||
config *provisioning.Repository
|
||||
gh pgh.Client
|
||||
baseURL *url.URL
|
||||
linterFactory lint.LinterFactory
|
||||
renderer PreviewRenderer
|
||||
}
|
||||
|
||||
var _ Repository = (*githubRepository)(nil)
|
||||
@@ -40,16 +40,16 @@ func NewGitHub(
|
||||
config *provisioning.Repository,
|
||||
factory pgh.ClientFactory,
|
||||
baseURL *url.URL,
|
||||
linter lint.Linter,
|
||||
linterFactory lint.LinterFactory,
|
||||
renderer PreviewRenderer,
|
||||
) *githubRepository {
|
||||
return &githubRepository{
|
||||
config: config,
|
||||
logger: slog.Default().With("logger", "github-repository"),
|
||||
gh: factory.New(ctx, config.Spec.GitHub.Token),
|
||||
baseURL: baseURL,
|
||||
linter: linter,
|
||||
renderer: renderer,
|
||||
config: config,
|
||||
logger: slog.Default().With("logger", "github-repository"),
|
||||
gh: factory.New(ctx, config.Spec.GitHub.Token),
|
||||
baseURL: baseURL,
|
||||
linterFactory: linterFactory,
|
||||
renderer: renderer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,7 +681,7 @@ func (r *githubRepository) onPullRequestEvent(ctx context.Context, logger *slog.
|
||||
var lintDashboardIssuesTemplate = `Hey there! 👋
|
||||
Grafana found some linting issues in this dashboard you may want to check:
|
||||
{{ range .}}
|
||||
{{ if eq .Severity 4 }}❌{{ else if eq .Severity 3 }}⚠️ {{ end }} [dashboard-linter/{{ .Rule }}](https://github.com/grafana/dashboard-linter/blob/main/docs/rules/{{ .Rule }}.md): {{ .Message }}.
|
||||
{{ if eq .Severity "error" }}❌{{ else if eq .Severity "warning" }}⚠️ {{ end }} [dashboard-linter/{{ .Rule }}](https://github.com/grafana/dashboard-linter/blob/main/docs/rules/{{ .Rule }}.md): {{ .Message }}.
|
||||
{{- end }}
|
||||
`
|
||||
|
||||
@@ -695,6 +695,22 @@ func (r *githubRepository) lintPullRequest(ctx context.Context, logger *slog.Log
|
||||
|
||||
logger.InfoContext(ctx, "lint pull request")
|
||||
|
||||
// Load linter config
|
||||
cfg, err := r.Read(ctx, logger, r.linterFactory.ConfigPath(), ref)
|
||||
switch {
|
||||
case err == nil:
|
||||
logger.InfoContext(ctx, "linter config found", "config", string(cfg.Data))
|
||||
case errors.Is(err, pgh.ErrResourceNotFound):
|
||||
logger.InfoContext(ctx, "no linter config found")
|
||||
default:
|
||||
return fmt.Errorf("read linter config: %w", err)
|
||||
}
|
||||
|
||||
linter, err := r.linterFactory.NewFromConfig(cfg.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create linter: %w", err)
|
||||
}
|
||||
|
||||
// Clear all previous comments because we don't know if the files have changed
|
||||
if err := r.gh.ClearAllPullRequestFileComments(ctx, r.config.Spec.GitHub.Owner, r.config.Spec.GitHub.Repository, prNumber); err != nil {
|
||||
return fmt.Errorf("clear pull request comments: %w", err)
|
||||
@@ -706,7 +722,7 @@ func (r *githubRepository) lintPullRequest(ctx context.Context, logger *slog.Log
|
||||
}
|
||||
|
||||
logger := logger.With("file", resource.Path)
|
||||
if err := r.lintPullRequestFile(ctx, logger, prNumber, ref, resource); err != nil {
|
||||
if err := r.lintPullRequestFile(ctx, logger, prNumber, ref, resource, linter); err != nil {
|
||||
logger.ErrorContext(ctx, "failed to lint file", "error", err)
|
||||
}
|
||||
}
|
||||
@@ -715,8 +731,8 @@ func (r *githubRepository) lintPullRequest(ctx context.Context, logger *slog.Log
|
||||
}
|
||||
|
||||
// lintPullRequestFile lints a file and comments the issues found.
|
||||
func (r *githubRepository) lintPullRequestFile(ctx context.Context, logger *slog.Logger, prNumber int, ref string, resource changedResource) error {
|
||||
issues, err := r.linter.Lint(ctx, resource.Data)
|
||||
func (r *githubRepository) lintPullRequestFile(ctx context.Context, logger *slog.Logger, prNumber int, ref string, resource changedResource, linter lint.Linter) error {
|
||||
issues, err := linter.Lint(ctx, resource.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("lint file: %w", err)
|
||||
}
|
||||
|
||||
@@ -148,6 +148,7 @@ func (r *FileParser) Parse(ctx context.Context, logger *slog.Logger, info *repos
|
||||
}
|
||||
}
|
||||
linter := lint.NewDashboardLinter()
|
||||
// TODO: pass the config file
|
||||
parsed.Lint, err = linter.Lint(ctx, raw)
|
||||
if err != nil {
|
||||
parsed.Errors = append(parsed.Errors, err)
|
||||
|
||||
Reference in new issue
Block a user