Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
- renovate/*

jobs:
test-mcp-server:
test:
runs-on: ubuntu-x64
permissions:
contents: read
Expand All @@ -20,8 +20,38 @@ jobs:
with:
go-version-file: go.mod

- name: Run MCP server tests
run: go test -v ./pkg/cmd/mcpserver/...
# node is needed for the passes that shell out to npx
# (reactcompat, plugindocs) and for the MCP server tests.
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22

- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.13"

# semgrep backs the coderules pass; its tests skip when the binary
# is missing, so install it to keep them in the run.
- name: Install semgrep
run: python3 -m pip install semgrep

# The plugincheck2 integration tests run in parallel and each one
# shells out to `npx @grafana/react-detect`. Concurrent cold-cache
# npx runs corrupt npm's _npx cache (TAR_ENTRY_ERROR, exit 127), so
# fetch the pinned version once up front. The version is read from
# the Go constant so it can't drift.
- name: Warm the react-detect npx cache
run: |
version="$(grep -o 'reactDetectVersion = "[^"]*"' pkg/analysis/passes/reactcompat/reactcompat.go | cut -d'"' -f2)"
cd "$(mktemp -d)"
npx -y "@grafana/react-detect@${version}" --help >/dev/null 2>&1 || true

# The plugincheck2 integration tests run the built binary from bin/.
- name: Build plugincheck2
run: go build -o bin/linux_amd64/plugincheck2 ./pkg/cmd/plugincheck2

- name: Run tests
run: go test ./pkg/...

test-docker-build:
runs-on: ubuntu-x64
Expand Down
18 changes: 14 additions & 4 deletions pkg/analysis/passes/reactcompat/reactcompat.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"os/exec"
"path/filepath"
"slices"
"strings"
"time"
Expand Down Expand Up @@ -231,14 +232,23 @@ func reportIssues(pass *analysis.Pass, output *reactDetectOutput, archiveDir str

// relativeToArchive strips the archive directory prefix from a file path
// emitted by react-detect, so reported paths are reproducible across machines.
// Falls back to the original path if it doesn't share the archive prefix.
// react-detect may report paths with symlinks resolved (on macOS the temp dir
// /var/folders/... resolves to /private/var/folders/...), so the resolved
// archiveDir is tried as well. Falls back to the original path if it doesn't
// share the archive prefix.
func relativeToArchive(file, archiveDir string) string {
if archiveDir == "" {
return file
}
prefix := strings.TrimRight(archiveDir, "/") + "/"
if strings.HasPrefix(file, prefix) {
return strings.TrimPrefix(file, prefix)
dirs := []string{archiveDir}
if resolved, err := filepath.EvalSymlinks(archiveDir); err == nil && resolved != archiveDir {
dirs = append(dirs, resolved)
}
for _, dir := range dirs {
prefix := strings.TrimRight(dir, "/") + "/"
if rel, ok := strings.CutPrefix(file, prefix); ok {
return rel
}
}
return file
}
20 changes: 20 additions & 0 deletions pkg/analysis/passes/reactcompat/reactcompat_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package reactcompat

import (
"os"
"path/filepath"
"testing"

Expand Down Expand Up @@ -248,3 +249,22 @@ func TestNoArchiveDir(t *testing.T) {
require.Nil(t, result)
require.Len(t, interceptor.Diagnostics, 0)
}

// TestRelativeToArchiveSymlinkedDir verifies that paths reported with the
// archive dir's symlinks resolved (e.g. macOS /var/folders -> /private/var)
// are still made relative.
func TestRelativeToArchiveSymlinkedDir(t *testing.T) {
base := t.TempDir()
target := filepath.Join(base, "target")
require.NoError(t, os.Mkdir(target, 0o755))
link := filepath.Join(base, "link")
require.NoError(t, os.Symlink(target, link))

resolved, err := filepath.EvalSymlinks(link)
require.NoError(t, err)

require.Equal(t, "src/module.ts", relativeToArchive(resolved+"/src/module.ts", link))
require.Equal(t, "src/module.ts", relativeToArchive(link+"/src/module.ts", link))
require.Equal(t, "/elsewhere/src/module.ts", relativeToArchive("/elsewhere/src/module.ts", link))
require.Equal(t, "/abs/path.ts", relativeToArchive("/abs/path.ts", ""))
}
33 changes: 31 additions & 2 deletions pkg/archivetool/archivetool_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,45 @@
package archivetool

import (
"archive/zip"
"bytes"
"net/http"
"net/http/httptest"
"testing"

. "github.com/smartystreets/goconvey/convey"
)

// testPluginZip builds a minimal plugin archive in memory so the tests
// don't depend on downloading a real release over the network.
func testPluginZip(t *testing.T) []byte {
t.Helper()
var buf bytes.Buffer
w := zip.NewWriter(&buf)
f, err := w.Create("myorg-plugin/plugin.json")
if err != nil {
t.Fatal(err)
}
if _, err := f.Write([]byte(`{"id": "myorg-plugin"}`)); err != nil {
t.Fatal(err)
}
if err := w.Close(); err != nil {
t.Fatal(err)
}
return buf.Bytes()
}

func TestArchive(t *testing.T) {
zipContent := testPluginZip(t)
server := httptest.NewServer(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write(zipContent)
}),
)
defer server.Close()
pluginToCheck := server.URL + "/myorg-plugin-1.0.0.zip"

Convey("Test Archive", t, func() {
pluginToCheck := "https://github.com/marcusolsson/grafana-jsonapi-datasource/releases/download/v0.6.0/marcusolsson-json-datasource-0.6.0.zip"
Convey("Test readArchive", func() {
content, err := ReadArchive(pluginToCheck)
So(err, ShouldBeNil)
Expand All @@ -19,7 +49,6 @@ func TestArchive(t *testing.T) {
content, err := ReadArchive(pluginToCheck)
So(err, ShouldBeNil)
So(len(content), ShouldBeGreaterThan, 0)
bytes.NewReader(content)
archiveDir, cleanup, err := ExtractPlugin(bytes.NewReader(content))
So(err, ShouldBeNil)
So(archiveDir, ShouldStartWith, "/")
Expand Down
Loading