From 8584ff74434bb4cb6a220dec3948f33b23ff1927 Mon Sep 17 00:00:00 2001 From: "Syerikjan(Sam)" Date: Mon, 16 Feb 2026 10:25:04 -0500 Subject: [PATCH 1/7] feat: validate grafanaDep with semver --- .../passes/metadatavalid/metadatavalid.go | 31 +++++++++++- .../metadatavalid/metadatavalid_test.go | 26 ++++++++++ .../testdata/invalid/grafana-dep/plugin.json | 48 +++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 pkg/analysis/passes/metadatavalid/testdata/invalid/grafana-dep/plugin.json diff --git a/pkg/analysis/passes/metadatavalid/metadatavalid.go b/pkg/analysis/passes/metadatavalid/metadatavalid.go index 9a741580..86e46fe8 100644 --- a/pkg/analysis/passes/metadatavalid/metadatavalid.go +++ b/pkg/analysis/passes/metadatavalid/metadatavalid.go @@ -2,11 +2,14 @@ package metadatavalid import ( "bytes" + "encoding/json" "fmt" "io" "os" "path/filepath" + "strings" + "github.com/hashicorp/go-version" "github.com/xeipuuv/gojsonschema" "github.com/grafana/plugin-validator/pkg/analysis" @@ -84,6 +87,25 @@ func run(pass *analysis.Pass) (interface{}, error) { case err == nil: break } + + pluginJsonBytes, _ := os.ReadFile(metadataPath) + if pluginJsonBytes != nil { + var data metadata.Metadata + if err := json.Unmarshal(pluginJsonBytes, &data); err == nil { + fmt.Println(data.Dependencies.GrafanaDependency) + _, err = version.NewConstraint(data.Dependencies.GrafanaDependency) + fmt.Println(err) + if err != nil { + pass.ReportResult( + pass.AnalyzerName, + invalidMetadata, + fmt.Sprintf("plugin.json: grafanaDependency field has invalid version constraint: %s", data.Dependencies.GrafanaDependency), + "Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/#grafanadependency", + ) + } + } + } + schemaLoader := gojsonschema.NewReferenceLoader("file:///" + schemaPath) documentLoader := gojsonschema.NewReferenceLoader("file:///" + metadataPath) @@ -92,7 +114,14 @@ func run(pass *analysis.Pass) (interface{}, error) { return nil, err } + errLen := len(result.Errors()) for _, desc := range result.Errors() { + // we validate grafanaDependency at line 91-100, + // so we ignore the error from schema validation + if strings.Contains(desc.Field(), "grafanaDependency") { + errLen -= 1 + continue + } pass.ReportResult( pass.AnalyzerName, invalidMetadata, @@ -100,7 +129,7 @@ func run(pass *analysis.Pass) (interface{}, error) { "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/", ) } - if len(result.Errors()) == 0 && validMetadata.ReportAll { + if errLen == 0 && validMetadata.ReportAll { pass.ReportResult(pass.AnalyzerName, validMetadata, "plugin.json: metadata is valid", "") } diff --git a/pkg/analysis/passes/metadatavalid/metadatavalid_test.go b/pkg/analysis/passes/metadatavalid/metadatavalid_test.go index 96dd8eb9..b949f5f1 100644 --- a/pkg/analysis/passes/metadatavalid/metadatavalid_test.go +++ b/pkg/analysis/passes/metadatavalid/metadatavalid_test.go @@ -53,6 +53,32 @@ func TestMetadataInvalid(t *testing.T) { ) } +func TestGrafanaDependencyInvalid(t *testing.T) { + var interceptor testpassinterceptor.TestPassInterceptor + pass := &analysis.Pass{ + RootDir: filepath.Join("./"), + ResultOf: map[*analysis.Analyzer]interface{}{ + archive.Analyzer: filepath.Join("testdata", "invalid", "grafana-dep"), + metadataschema.Analyzer: getSchema(), + }, + Report: interceptor.ReportInterceptor(), + } + + _, err := Analyzer.Run(pass) + require.NoError(t, err) + require.Len(t, interceptor.Diagnostics, 2) + require.Equal( + t, + "plugin.json: grafanaDependency field has invalid version constraint: >=invalid", + interceptor.Diagnostics[0].Title, + ) + require.Equal( + t, + "plugin.json: (root): Additional property invalid is not allowed", + interceptor.Diagnostics[1].Title, + ) +} + func getSchema() []byte { if len(schemaContent) > 0 { return schemaContent diff --git a/pkg/analysis/passes/metadatavalid/testdata/invalid/grafana-dep/plugin.json b/pkg/analysis/passes/metadatavalid/testdata/invalid/grafana-dep/plugin.json new file mode 100644 index 00000000..a7733aa6 --- /dev/null +++ b/pkg/analysis/passes/metadatavalid/testdata/invalid/grafana-dep/plugin.json @@ -0,0 +1,48 @@ +{ + "type": "panel", + "name": "Clock", + "id": "grafana-clock-panel", + "skipDataQuery": true, + "invalid": "invalid", + "info": { + "description": "Clock panel for grafana", + "author": { + "name": "Grafana Labs", + "url": "https://grafana.com" + }, + "keywords": [ + "clock", + "panel" + ], + "logos": { + "small": "img/clock.svg", + "large": "img/clock.svg" + }, + "links": [ + { + "name": "Project site", + "url": "https://github.com/grafana/clock-panel" + }, + { + "name": "MIT License", + "url": "https://github.com/grafana/clock-panel/blob/master/LICENSE" + } + ], + "screenshots": [ + { + "name": "Showcase", + "path": "img/screenshot-showcase.png" + }, + { + "name": "Options", + "path": "img/screenshot-clock-options.png" + } + ], + "version": "%VERSION%", + "updated": "%TODAY%" + }, + "dependencies": { + "grafanaDependency": ">=invalid", + "plugins": [] + } +} \ No newline at end of file From 3d58842343525c6f3a511089f3b63e41d65e70f0 Mon Sep 17 00:00:00 2001 From: "Syerikjan(Sam)" Date: Mon, 16 Feb 2026 10:50:39 -0500 Subject: [PATCH 2/7] test: add parent key --- pkg/analysis/passes/metadatavalid/metadatavalid.go | 2 +- pkg/analysis/passes/metadatavalid/metadatavalid_test.go | 2 +- pkg/runner/runner_test.go | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/analysis/passes/metadatavalid/metadatavalid.go b/pkg/analysis/passes/metadatavalid/metadatavalid.go index 86e46fe8..3578dd99 100644 --- a/pkg/analysis/passes/metadatavalid/metadatavalid.go +++ b/pkg/analysis/passes/metadatavalid/metadatavalid.go @@ -99,7 +99,7 @@ func run(pass *analysis.Pass) (interface{}, error) { pass.ReportResult( pass.AnalyzerName, invalidMetadata, - fmt.Sprintf("plugin.json: grafanaDependency field has invalid version constraint: %s", data.Dependencies.GrafanaDependency), + fmt.Sprintf("plugin.json: Dependencies.grafanaDependency field has invalid or empty version constraint: %q", data.Dependencies.GrafanaDependency), "Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/#grafanadependency", ) } diff --git a/pkg/analysis/passes/metadatavalid/metadatavalid_test.go b/pkg/analysis/passes/metadatavalid/metadatavalid_test.go index b949f5f1..8ced6e8b 100644 --- a/pkg/analysis/passes/metadatavalid/metadatavalid_test.go +++ b/pkg/analysis/passes/metadatavalid/metadatavalid_test.go @@ -69,7 +69,7 @@ func TestGrafanaDependencyInvalid(t *testing.T) { require.Len(t, interceptor.Diagnostics, 2) require.Equal( t, - "plugin.json: grafanaDependency field has invalid version constraint: >=invalid", + "plugin.json: Dependencies.grafanaDependency field has invalid or empty version constraint: \">=invalid\"", interceptor.Diagnostics[0].Title, ) require.Equal( diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index 12454671..ca618b22 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -50,6 +50,7 @@ var tests = []struct { "plugin.json: plugin id should follow the format org-name-type", "LLM review skipped due to errors in metadatavalid", "Code diff skipped due to errors in metadatavalid", + "plugin.json: grafanaDependency field has invalid or empty version constraint: \"\"", }}, } From ebfb600d076b7096de31d3e5819e450828ec54db Mon Sep 17 00:00:00 2001 From: "Syerikjan(Sam)" Date: Mon, 16 Feb 2026 11:13:58 -0500 Subject: [PATCH 3/7] test: error msg change --- pkg/analysis/passes/metadatavalid/metadatavalid.go | 6 ++---- pkg/cmd/plugincheck2/main_test.go | 12 ++++++------ pkg/runner/runner_test.go | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/pkg/analysis/passes/metadatavalid/metadatavalid.go b/pkg/analysis/passes/metadatavalid/metadatavalid.go index 3578dd99..53f08502 100644 --- a/pkg/analysis/passes/metadatavalid/metadatavalid.go +++ b/pkg/analysis/passes/metadatavalid/metadatavalid.go @@ -92,15 +92,13 @@ func run(pass *analysis.Pass) (interface{}, error) { if pluginJsonBytes != nil { var data metadata.Metadata if err := json.Unmarshal(pluginJsonBytes, &data); err == nil { - fmt.Println(data.Dependencies.GrafanaDependency) _, err = version.NewConstraint(data.Dependencies.GrafanaDependency) - fmt.Println(err) if err != nil { pass.ReportResult( pass.AnalyzerName, invalidMetadata, fmt.Sprintf("plugin.json: Dependencies.grafanaDependency field has invalid or empty version constraint: %q", data.Dependencies.GrafanaDependency), - "Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/#grafanadependency", + "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/#grafanadependency", ) } } @@ -118,7 +116,7 @@ func run(pass *analysis.Pass) (interface{}, error) { for _, desc := range result.Errors() { // we validate grafanaDependency at line 91-100, // so we ignore the error from schema validation - if strings.Contains(desc.Field(), "grafanaDependency") { + if strings.Contains(desc.Field(), "grafanaDependency") || strings.Contains(desc.Description(), "grafanaDependency") { errLen -= 1 continue } diff --git a/pkg/cmd/plugincheck2/main_test.go b/pkg/cmd/plugincheck2/main_test.go index 0d8a39e7..2eaa8f55 100644 --- a/pkg/cmd/plugincheck2/main_test.go +++ b/pkg/cmd/plugincheck2/main_test.go @@ -258,8 +258,8 @@ func TestIntegration(t *testing.T) { "metadatavalid": { { Severity: "error", - Title: "plugin.json: dependencies: grafanaDependency is required", - Detail: "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/", + Title: "plugin.json: Dependencies.grafanaDependency field has invalid or empty version constraint: \"\"", + Detail: "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/#grafanadependency", Name: "invalid-metadata", }, }, @@ -317,8 +317,8 @@ func TestIntegration(t *testing.T) { "metadatavalid": { { Severity: "error", - Title: "plugin.json: dependencies: grafanaDependency is required", - Detail: "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/", + Title: "plugin.json: Dependencies.grafanaDependency field has invalid or empty version constraint: \"\"", + Detail: "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/#grafanadependency", Name: "invalid-metadata", }, }, @@ -336,8 +336,8 @@ func TestIntegration(t *testing.T) { "metadatavalid": { { Severity: "warning", - Title: "plugin.json: dependencies: grafanaDependency is required", - Detail: "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/", + Title: "plugin.json: Dependencies.grafanaDependency field has invalid or empty version constraint: \"\"", + Detail: "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/#grafanadependency", Name: "invalid-metadata", }, }, diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index ca618b22..f29253fa 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -50,7 +50,7 @@ var tests = []struct { "plugin.json: plugin id should follow the format org-name-type", "LLM review skipped due to errors in metadatavalid", "Code diff skipped due to errors in metadatavalid", - "plugin.json: grafanaDependency field has invalid or empty version constraint: \"\"", + "plugin.json: Dependencies.grafanaDependency field has invalid or empty version constraint: \"\"", }}, } From efc922eb6fb9c0ff4a4bd1c347348e5e3ad78cac Mon Sep 17 00:00:00 2001 From: "Syerikjan(Sam)" Date: Mon, 16 Feb 2026 11:48:29 -0500 Subject: [PATCH 4/7] ref: log the error --- pkg/analysis/passes/metadatavalid/metadatavalid.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/analysis/passes/metadatavalid/metadatavalid.go b/pkg/analysis/passes/metadatavalid/metadatavalid.go index 53f08502..731aa1f6 100644 --- a/pkg/analysis/passes/metadatavalid/metadatavalid.go +++ b/pkg/analysis/passes/metadatavalid/metadatavalid.go @@ -16,6 +16,7 @@ import ( "github.com/grafana/plugin-validator/pkg/analysis/passes/archive" "github.com/grafana/plugin-validator/pkg/analysis/passes/metadata" "github.com/grafana/plugin-validator/pkg/analysis/passes/metadataschema" + "github.com/grafana/plugin-validator/pkg/logme" ) var ( @@ -88,7 +89,11 @@ func run(pass *analysis.Pass) (interface{}, error) { break } - pluginJsonBytes, _ := os.ReadFile(metadataPath) + pluginJsonBytes, err := os.ReadFile(metadataPath) + if err != nil { + // log the error and continue with schema validation + logme.ErrorF("failed to read plugin.json in metadatavalid check: %q", err) + } if pluginJsonBytes != nil { var data metadata.Metadata if err := json.Unmarshal(pluginJsonBytes, &data); err == nil { From 309ccac108202d365aacd04b610e831e72a7f579 Mon Sep 17 00:00:00 2001 From: "Syerikjan(Sam)" Date: Tue, 17 Feb 2026 14:48:33 -0500 Subject: [PATCH 5/7] ref: extract to separate analyzer, use semver --- README.md | 1 + go.mod | 1 + go.sum | 2 + pkg/analysis/passes/analysis.go | 2 + .../grafanadependency/grafanadependency.go | 57 ++++++++++++++++ .../grafanadependency_test.go | 68 +++++++++++++++++++ .../passes/metadatavalid/metadatavalid.go | 34 +--------- .../metadatavalid/metadatavalid_test.go | 26 ------- .../testdata/invalid/grafana-dep/plugin.json | 48 ------------- pkg/cmd/plugincheck2/main_test.go | 20 ++++-- pkg/runner/runner_test.go | 2 +- 11 files changed, 147 insertions(+), 114 deletions(-) create mode 100644 pkg/analysis/passes/grafanadependency/grafanadependency.go create mode 100644 pkg/analysis/passes/grafanadependency/grafanadependency_test.go delete mode 100644 pkg/analysis/passes/metadatavalid/testdata/invalid/grafana-dep/plugin.json diff --git a/README.md b/README.md index 3d9713da..babf03b0 100644 --- a/README.md +++ b/README.md @@ -274,6 +274,7 @@ Run "mage gen:readme" to regenerate this section. | Logos / `logos` | Detects whether the plugin includes small and large logos to display in the plugin catalog. | None | | Manifest (Signing) / `manifest` | When a plugin is signed, the zip file will contain a signed `MANIFEST.txt` file. | None | | Metadata / `metadata` | Checks that `plugin.json` exists and is valid. | None | +| Metadata Grafana Dependency / `grafanadependency` | Checks that dependencies.grafanaDependency in `plugin.json` is valid. | None | | Metadata Paths / `metadatapaths` | Ensures all paths are valid and images referenced exist. | None | | Metadata Validity / `metadatavalid` | Ensures metadata is valid and matches plugin schema. | None | | module.js (exists) / `modulejs` | All plugins require a `module.js` to be loaded. | None | diff --git a/go.mod b/go.mod index c74343bd..2d5c3646 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/grafana/plugin-validator go 1.25.5 require ( + github.com/Masterminds/semver/v3 v3.4.0 github.com/bmatcuk/doublestar/v4 v4.9.2 github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 github.com/fatih/color v1.18.0 diff --git a/go.sum b/go.sum index ecfdbe41..bf30440c 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ github.com/CycloneDX/cyclonedx-go v0.9.3 h1:Pyk/lwavPz7AaZNvugKFkdWOm93MzaIyWmBw github.com/CycloneDX/cyclonedx-go v0.9.3/go.mod h1:vcK6pKgO1WanCdd61qx4bFnSsDJQ6SbM2ZuMIgq86Jg= github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcvmjQJcQGg+w+UaafSy8G5Kcb5tBhI= github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5/go.mod h1:exZ0C/1emQJAw5tHOaUDyY1ycttqBAPcxuzf7QbY6ec= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= diff --git a/pkg/analysis/passes/analysis.go b/pkg/analysis/passes/analysis.go index 821561e4..874354cb 100644 --- a/pkg/analysis/passes/analysis.go +++ b/pkg/analysis/passes/analysis.go @@ -17,6 +17,7 @@ import ( "github.com/grafana/plugin-validator/pkg/analysis/passes/discoverability" "github.com/grafana/plugin-validator/pkg/analysis/passes/gomanifest" "github.com/grafana/plugin-validator/pkg/analysis/passes/gosec" + "github.com/grafana/plugin-validator/pkg/analysis/passes/grafanadependency" "github.com/grafana/plugin-validator/pkg/analysis/passes/includesnested" "github.com/grafana/plugin-validator/pkg/analysis/passes/jargon" "github.com/grafana/plugin-validator/pkg/analysis/passes/jssourcemap" @@ -103,4 +104,5 @@ var Analyzers = []*analysis.Analyzer{ virusscan.Analyzer, circulardependencies.Analyzer, codediff.Analyzer, + grafanadependency.Analyzer, } diff --git a/pkg/analysis/passes/grafanadependency/grafanadependency.go b/pkg/analysis/passes/grafanadependency/grafanadependency.go new file mode 100644 index 00000000..0f38c2d0 --- /dev/null +++ b/pkg/analysis/passes/grafanadependency/grafanadependency.go @@ -0,0 +1,57 @@ +package grafanadependency + +import ( + "encoding/json" + "fmt" + + "github.com/Masterminds/semver/v3" + "github.com/grafana/plugin-validator/pkg/analysis" + "github.com/grafana/plugin-validator/pkg/analysis/passes/metadata" +) + +var ( + invalidGrafanaDependency = &analysis.Rule{Name: "invalid-grafana-dependency", Severity: analysis.Error} + validGrafanaDependency = &analysis.Rule{Name: "valid-grafana-dependency", Severity: analysis.OK} +) + +var Analyzer = &analysis.Analyzer{ + Name: "grafanadependency", + Requires: []*analysis.Analyzer{metadata.Analyzer}, + Run: run, + Rules: []*analysis.Rule{invalidGrafanaDependency, validGrafanaDependency}, + ReadmeInfo: analysis.ReadmeInfo{ + Name: "Metadata Grafana Dependency", + Description: "Checks that dependencies.grafanaDependency in `plugin.json` is valid.", + }, +} + +func run(pass *analysis.Pass) (interface{}, error) { + metadataBytes, ok := pass.ResultOf[metadata.Analyzer].([]byte) + if !ok { + return nil, nil + } + + var data metadata.Metadata + if err := json.Unmarshal(metadataBytes, &data); err != nil { + // if we fail to unmarshall it means the schema is incorrect + // we will let the metadatavalid validator handle it + return nil, nil + } + + _, err := semver.NewConstraint(data.Dependencies.GrafanaDependency) + if err != nil { + pass.ReportResult( + pass.AnalyzerName, + invalidGrafanaDependency, + fmt.Sprintf("plugin.json: dependencies.grafanaDependency field has invalid or empty version constraint: %q", data.Dependencies.GrafanaDependency), + "The plugin.json file has an invalid or empty grafanaDependency field. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/#grafanadependency", + ) + return nil, nil + } + + if validGrafanaDependency.ReportAll { + pass.ReportResult(pass.AnalyzerName, validGrafanaDependency, "plugin.json: dependencies.grafanaDependency field is valid", "") + } + + return nil, nil +} diff --git a/pkg/analysis/passes/grafanadependency/grafanadependency_test.go b/pkg/analysis/passes/grafanadependency/grafanadependency_test.go new file mode 100644 index 00000000..1584b8a0 --- /dev/null +++ b/pkg/analysis/passes/grafanadependency/grafanadependency_test.go @@ -0,0 +1,68 @@ +package grafanadependency + +import ( + "path/filepath" + "testing" + + "github.com/grafana/plugin-validator/pkg/analysis" + "github.com/grafana/plugin-validator/pkg/analysis/passes/metadata" + "github.com/grafana/plugin-validator/pkg/testpassinterceptor" + "github.com/stretchr/testify/require" +) + +func TestGrafanaDependency(t *testing.T) { + for _, tc := range []struct { + name string + pluginJSON string + titleMsg string + }{ + { + name: "valid grafanaDependency constraint", + pluginJSON: `{ + "id": "test-org-app", + "dependencies": { "grafanaDependency": ">=11.6.0" } + }`, + titleMsg: "", + }, + { + name: "complex but valid grafanaDependency constraint", + pluginJSON: `{ + "id": "test-org-app", + "dependencies": { "grafanaDependency": ">=11.6.11 <12 || >=12.0.10 <12.1 || >=12.1.7 <12.2 || >=12.2.5" } + }`, + titleMsg: "", + }, + { + name: "invalid grafanaDependency constraint", + pluginJSON: `{ + "id": "test-org-app", + "dependencies": { "grafanaDependency": ">=invalid" } + }`, + titleMsg: "plugin.json: dependencies.grafanaDependency field has invalid or empty version constraint: \">=invalid\"", + }, + } { + t.Run(tc.name, func(t *testing.T) { + var interceptor testpassinterceptor.TestPassInterceptor + pass := &analysis.Pass{ + RootDir: filepath.Join("./"), + ResultOf: map[*analysis.Analyzer]interface{}{ + metadata.Analyzer: []byte(tc.pluginJSON), + }, + Report: interceptor.ReportInterceptor(), + } + + _, err := Analyzer.Run(pass) + require.NoError(t, err) + if len(tc.titleMsg) > 0 { + require.Len(t, interceptor.Diagnostics, 1) + require.Equal( + t, + tc.titleMsg, + interceptor.Diagnostics[0].Title, + ) + } else { + require.Len(t, interceptor.Diagnostics, 0) + } + }) + } +} diff --git a/pkg/analysis/passes/metadatavalid/metadatavalid.go b/pkg/analysis/passes/metadatavalid/metadatavalid.go index 731aa1f6..9a741580 100644 --- a/pkg/analysis/passes/metadatavalid/metadatavalid.go +++ b/pkg/analysis/passes/metadatavalid/metadatavalid.go @@ -2,21 +2,17 @@ package metadatavalid import ( "bytes" - "encoding/json" "fmt" "io" "os" "path/filepath" - "strings" - "github.com/hashicorp/go-version" "github.com/xeipuuv/gojsonschema" "github.com/grafana/plugin-validator/pkg/analysis" "github.com/grafana/plugin-validator/pkg/analysis/passes/archive" "github.com/grafana/plugin-validator/pkg/analysis/passes/metadata" "github.com/grafana/plugin-validator/pkg/analysis/passes/metadataschema" - "github.com/grafana/plugin-validator/pkg/logme" ) var ( @@ -88,27 +84,6 @@ func run(pass *analysis.Pass) (interface{}, error) { case err == nil: break } - - pluginJsonBytes, err := os.ReadFile(metadataPath) - if err != nil { - // log the error and continue with schema validation - logme.ErrorF("failed to read plugin.json in metadatavalid check: %q", err) - } - if pluginJsonBytes != nil { - var data metadata.Metadata - if err := json.Unmarshal(pluginJsonBytes, &data); err == nil { - _, err = version.NewConstraint(data.Dependencies.GrafanaDependency) - if err != nil { - pass.ReportResult( - pass.AnalyzerName, - invalidMetadata, - fmt.Sprintf("plugin.json: Dependencies.grafanaDependency field has invalid or empty version constraint: %q", data.Dependencies.GrafanaDependency), - "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/#grafanadependency", - ) - } - } - } - schemaLoader := gojsonschema.NewReferenceLoader("file:///" + schemaPath) documentLoader := gojsonschema.NewReferenceLoader("file:///" + metadataPath) @@ -117,14 +92,7 @@ func run(pass *analysis.Pass) (interface{}, error) { return nil, err } - errLen := len(result.Errors()) for _, desc := range result.Errors() { - // we validate grafanaDependency at line 91-100, - // so we ignore the error from schema validation - if strings.Contains(desc.Field(), "grafanaDependency") || strings.Contains(desc.Description(), "grafanaDependency") { - errLen -= 1 - continue - } pass.ReportResult( pass.AnalyzerName, invalidMetadata, @@ -132,7 +100,7 @@ func run(pass *analysis.Pass) (interface{}, error) { "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/", ) } - if errLen == 0 && validMetadata.ReportAll { + if len(result.Errors()) == 0 && validMetadata.ReportAll { pass.ReportResult(pass.AnalyzerName, validMetadata, "plugin.json: metadata is valid", "") } diff --git a/pkg/analysis/passes/metadatavalid/metadatavalid_test.go b/pkg/analysis/passes/metadatavalid/metadatavalid_test.go index 8ced6e8b..96dd8eb9 100644 --- a/pkg/analysis/passes/metadatavalid/metadatavalid_test.go +++ b/pkg/analysis/passes/metadatavalid/metadatavalid_test.go @@ -53,32 +53,6 @@ func TestMetadataInvalid(t *testing.T) { ) } -func TestGrafanaDependencyInvalid(t *testing.T) { - var interceptor testpassinterceptor.TestPassInterceptor - pass := &analysis.Pass{ - RootDir: filepath.Join("./"), - ResultOf: map[*analysis.Analyzer]interface{}{ - archive.Analyzer: filepath.Join("testdata", "invalid", "grafana-dep"), - metadataschema.Analyzer: getSchema(), - }, - Report: interceptor.ReportInterceptor(), - } - - _, err := Analyzer.Run(pass) - require.NoError(t, err) - require.Len(t, interceptor.Diagnostics, 2) - require.Equal( - t, - "plugin.json: Dependencies.grafanaDependency field has invalid or empty version constraint: \">=invalid\"", - interceptor.Diagnostics[0].Title, - ) - require.Equal( - t, - "plugin.json: (root): Additional property invalid is not allowed", - interceptor.Diagnostics[1].Title, - ) -} - func getSchema() []byte { if len(schemaContent) > 0 { return schemaContent diff --git a/pkg/analysis/passes/metadatavalid/testdata/invalid/grafana-dep/plugin.json b/pkg/analysis/passes/metadatavalid/testdata/invalid/grafana-dep/plugin.json deleted file mode 100644 index a7733aa6..00000000 --- a/pkg/analysis/passes/metadatavalid/testdata/invalid/grafana-dep/plugin.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "type": "panel", - "name": "Clock", - "id": "grafana-clock-panel", - "skipDataQuery": true, - "invalid": "invalid", - "info": { - "description": "Clock panel for grafana", - "author": { - "name": "Grafana Labs", - "url": "https://grafana.com" - }, - "keywords": [ - "clock", - "panel" - ], - "logos": { - "small": "img/clock.svg", - "large": "img/clock.svg" - }, - "links": [ - { - "name": "Project site", - "url": "https://github.com/grafana/clock-panel" - }, - { - "name": "MIT License", - "url": "https://github.com/grafana/clock-panel/blob/master/LICENSE" - } - ], - "screenshots": [ - { - "name": "Showcase", - "path": "img/screenshot-showcase.png" - }, - { - "name": "Options", - "path": "img/screenshot-clock-options.png" - } - ], - "version": "%VERSION%", - "updated": "%TODAY%" - }, - "dependencies": { - "grafanaDependency": ">=invalid", - "plugins": [] - } -} \ No newline at end of file diff --git a/pkg/cmd/plugincheck2/main_test.go b/pkg/cmd/plugincheck2/main_test.go index 2eaa8f55..8cd71385 100644 --- a/pkg/cmd/plugincheck2/main_test.go +++ b/pkg/cmd/plugincheck2/main_test.go @@ -258,8 +258,8 @@ func TestIntegration(t *testing.T) { "metadatavalid": { { Severity: "error", - Title: "plugin.json: Dependencies.grafanaDependency field has invalid or empty version constraint: \"\"", - Detail: "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/#grafanadependency", + Title: "plugin.json: dependencies: grafanaDependency is required", + Detail: "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/", Name: "invalid-metadata", }, }, @@ -303,6 +303,14 @@ func TestIntegration(t *testing.T) { Name: "code-diff-skipped", }, }, + "grafanadependency": { + { + Severity: "error", + Title: "plugin.json: dependencies.grafanaDependency field has invalid or empty version constraint: \"\"", + Detail: "The plugin.json file has an invalid or empty grafanaDependency field. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/#grafanadependency", + Name: "invalid-grafana-dependency", + }, + }, }, }, }, @@ -317,8 +325,8 @@ func TestIntegration(t *testing.T) { "metadatavalid": { { Severity: "error", - Title: "plugin.json: Dependencies.grafanaDependency field has invalid or empty version constraint: \"\"", - Detail: "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/#grafanadependency", + Title: "plugin.json: dependencies: grafanaDependency is required", + Detail: "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/", Name: "invalid-metadata", }, }, @@ -336,8 +344,8 @@ func TestIntegration(t *testing.T) { "metadatavalid": { { Severity: "warning", - Title: "plugin.json: Dependencies.grafanaDependency field has invalid or empty version constraint: \"\"", - Detail: "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/#grafanadependency", + Title: "plugin.json: dependencies: grafanaDependency is required", + Detail: "The plugin.json file is not following the schema. Please refer to the documentation for more information. https://grafana.com/docs/grafana/latest/developers/plugins/metadata/", Name: "invalid-metadata", }, }, diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index f29253fa..86fc8eb7 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -50,7 +50,7 @@ var tests = []struct { "plugin.json: plugin id should follow the format org-name-type", "LLM review skipped due to errors in metadatavalid", "Code diff skipped due to errors in metadatavalid", - "plugin.json: Dependencies.grafanaDependency field has invalid or empty version constraint: \"\"", + "plugin.json: dependencies.grafanaDependency field has invalid or empty version constraint: \"\"", }}, } From cfb934ff25065cca9a9aeed5d2dee879c360997e Mon Sep 17 00:00:00 2001 From: "Syerikjan(Sam)" Date: Thu, 19 Feb 2026 11:11:24 -0500 Subject: [PATCH 6/7] test: empty or missing grafana dep --- .../grafanadependency/grafanadependency_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pkg/analysis/passes/grafanadependency/grafanadependency_test.go b/pkg/analysis/passes/grafanadependency/grafanadependency_test.go index 1584b8a0..241e45c6 100644 --- a/pkg/analysis/passes/grafanadependency/grafanadependency_test.go +++ b/pkg/analysis/passes/grafanadependency/grafanadependency_test.go @@ -40,6 +40,22 @@ func TestGrafanaDependency(t *testing.T) { }`, titleMsg: "plugin.json: dependencies.grafanaDependency field has invalid or empty version constraint: \">=invalid\"", }, + { + name: "empty grafanaDependency", + pluginJSON: `{ + "id": "test-org-app", + "dependencies": { "grafanaDependency": "" } + }`, + titleMsg: "plugin.json: dependencies.grafanaDependency field has invalid or empty version constraint: \"\"", + }, + { + name: "missing grafanaDependency", + pluginJSON: `{ + "id": "test-org-app", + "dependencies": {"plugins": [ ]} + }`, + titleMsg: "plugin.json: dependencies.grafanaDependency field has invalid or empty version constraint: \"\"", + }, } { t.Run(tc.name, func(t *testing.T) { var interceptor testpassinterceptor.TestPassInterceptor From 0ce9e9a922593df7dbe3067b801afc701be44baf Mon Sep 17 00:00:00 2001 From: "Syerikjan(Sam)" Date: Fri, 20 Feb 2026 08:53:29 -0500 Subject: [PATCH 7/7] chore: retrigger ci --- .../passes/grafanadependency/grafanadependency_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/analysis/passes/grafanadependency/grafanadependency_test.go b/pkg/analysis/passes/grafanadependency/grafanadependency_test.go index 241e45c6..1c8d0512 100644 --- a/pkg/analysis/passes/grafanadependency/grafanadependency_test.go +++ b/pkg/analysis/passes/grafanadependency/grafanadependency_test.go @@ -36,9 +36,9 @@ func TestGrafanaDependency(t *testing.T) { name: "invalid grafanaDependency constraint", pluginJSON: `{ "id": "test-org-app", - "dependencies": { "grafanaDependency": ">=invalid" } + "dependencies": { "grafanaDependency": ">=invalid2" } }`, - titleMsg: "plugin.json: dependencies.grafanaDependency field has invalid or empty version constraint: \">=invalid\"", + titleMsg: "plugin.json: dependencies.grafanaDependency field has invalid or empty version constraint: \">=invalid2\"", }, { name: "empty grafanaDependency",