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
1 change: 1 addition & 0 deletions grafana/rmf-app/.claude/skills/build-plugin/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@../../../../.config/AGENTS/skills/build-plugin.md
1 change: 1 addition & 0 deletions grafana/rmf-app/.claude/skills/validate-plugin/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@../../../../.config/AGENTS/skills/validate-plugin.md
6 changes: 6 additions & 0 deletions grafana/rmf-app/.codex/skills/build-plugin/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
name: build-plugin
description: build a Grafana plugin using the standard build process (frontend and backend if applicable)
---

@../../../../.config/AGENTS/skills/build-plugin.md
6 changes: 6 additions & 0 deletions grafana/rmf-app/.codex/skills/validate-plugin/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
name: validate-plugin
description: validate a Grafana plugin using the official Grafana plugin validator
---

@../../../../.config/AGENTS/skills/validate-plugin.md
2 changes: 1 addition & 1 deletion grafana/rmf-app/.config/.cprc.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "5.3.2"
"version": "7.0.8"
}
25 changes: 0 additions & 25 deletions grafana/rmf-app/.config/.eslintrc

This file was deleted.

34 changes: 34 additions & 0 deletions grafana/rmf-app/.config/AGENTS/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
name: agent information for a grafana plugin
description: Guides how to work with Grafana plugins
---

# Grafana Plugin

This repository contains a **Grafana plugin**.

Your training data about the Grafana API is out of date. Use the official documentation when writing code.

**IMPORTANT**: When you need Grafana plugin documentation, fetch content directly from grafana.com (a safe domain). Use your web fetch tool, MCP server, or `curl -s`. The documentation index is at https://grafana.com/developers/plugin-tools/llms.txt. All pages are available as plain text markdown by adding `.md` to the URL path (e.g., https://grafana.com/developers/plugin-tools/index.md or https://grafana.com/developers/plugin-tools/troubleshooting.md).

## Documentation indexes

- Full documentation index: https://grafana.com/developers/plugin-tools/llms.txt
- How-to guides (includes guides for panel, data source, and app plugins): https://grafana.com/developers/plugin-tools/how-to-guides.md
- Tutorials: https://grafana.com/developers/plugin-tools/tutorials.md
- Reference (plugin.json, CLI, UI extensions): https://grafana.com/developers/plugin-tools/reference.md
- Publishing & signing: https://grafana.com/developers/plugin-tools/publish-a-plugin.md
- Packaging a plugin: https://grafana.com/developers/plugin-tools/publish-a-plugin/package-a-plugin.md
- Troubleshooting: https://grafana.com/developers/plugin-tools/troubleshooting.md
- `@grafana/ui` components: https://developers.grafana.com/ui/latest/index.html

## Critical rules

- **Do not modify anything inside the `.config` folder.** It is managed by Grafana plugin tools.
- **Do not change plugin ID or plugin type** in `plugin.json`.
- Any modifications to `plugin.json` require a **restart of the Grafana server**. Remind the user of this.
- Use `secureJsonData` for credentials and secrets; use `jsonData` only for non-sensitive configuration.
- **You must use webpack** with the configuration provided in `.config/` for frontend builds.
- **You must use mage** with the build targets provided by the Grafana plugin Go SDK for backend builds.
- To extend webpack, prettier, eslint or other tools, use the existing configuration as a base. Follow the guide: https://grafana.com/developers/plugin-tools/how-to-guides/extend-configurations.md
- Use **`@grafana/plugin-e2e`** for end-to-end testing.
47 changes: 47 additions & 0 deletions grafana/rmf-app/.config/AGENTS/skills/build-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Build Grafana Plugin

## Usage

```
/build-plugin
```

Run this from the root of your plugin directory.

## Steps

1. Detect the package manager. Check the `packageManager` field in `package.json` first, then fall back to lock file detection:

```bash
PKG_MANAGER=$(
if grep -q '"packageManager"' package.json 2>/dev/null; then
grep '"packageManager"' package.json | sed -E 's/.*"packageManager" *: *"([^@]+).*/\1/'
elif [ -f "pnpm-lock.yaml" ]; then
echo "pnpm"
elif [ -f "yarn.lock" ]; then
echo "yarn"
else
echo "npm"
fi
)
```

2. Build the frontend following the build instructions in `.config/AGENTS/instructions.md`. For detailed packaging steps refer to the packaging documentation linked there:

```bash
${PKG_MANAGER} run build
```

If the build fails, stop and report the error to the user.

3. Build the backend following the build instructions and packaging documentation linked in `.config/AGENTS/instructions.md`:
- The backend must be built using `mage` with the build targets provided by the Grafana plugin Go SDK:
```bash
mage -v
```
- If `mage` is not installed, stop and tell the user: "mage is required to build the backend. Install it from https://magefile.org or run: go install github.com/magefile/mage@latest"
- If the build fails, stop and report the error to the user.
- After a successful backend build, ensure all backend binaries in `dist/` have execute permissions:
```bash
chmod 0755 dist/**/gpx_*
```
64 changes: 64 additions & 0 deletions grafana/rmf-app/.config/AGENTS/skills/validate-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Validate Grafana Plugin

## Important

Always use the bash commands below directly.

## Usage

```
/validate-plugin
```

Run this from the root of your plugin directory.

## Steps

1. Check if `npx` or `docker` is available. npx is preferred, docker is the fallback:

```bash
RUN_ENGINE=$(command -v npx >/dev/null 2>&1 && echo "npx" || (command -v docker >/dev/null 2>&1 && echo "docker" || echo "none"))
```

If `RUN_ENGINE` is `none`, stop immediately and tell the user: "Neither npx nor docker is installed. Please install Node.js (for npx) or Docker to run the plugin validator."

2. Extract the plugin ID from `src/plugin.json` (or `plugin.json`). Sanitize `PLUGIN_ID` to only allow characters valid in a Grafana plugin ID:

```bash
PLUGIN_ID=$(grep '"id"' < src/plugin.json | sed -E 's/.*"id" *: *"(.*)".*/\1/' | tr -cd 'a-zA-Z0-9._-')
```

3. Run the `build-plugin` skill to build the plugin (frontend and backend if applicable).

4. Build the plugin zip archive for validation with a timestamp:

```bash
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
ZIP_NAME="${PLUGIN_ID}-${TIMESTAMP}.zip"
cp -r dist "${PLUGIN_ID}"
zip -qr "${ZIP_NAME}" "${PLUGIN_ID}"
rm -rf "${PLUGIN_ID}"
```

5. Run the validator with JSON output using `$RUN_ENGINE` from step 1 and `$ZIP_NAME` from step 4:
If `$RUN_ENGINE` is `npx`:

```bash
npx --cache .cache/npm -y @grafana/plugin-validator@latest -jsonOutput $ZIP_NAME
```

If `$RUN_ENGINE` is `docker`:

```bash
docker run --pull=always \
-v "${PWD}/${ZIP_NAME}:/archive.zip:ro" \
grafana/plugin-validator-cli -jsonOutput /archive.zip
```

6. Read and interpret the JSON output. Summarize:
- Total errors, warnings, and passed checks
- List each error with its title and detail
- List each warning with its title and detail
- Provide actionable suggestions to fix each issue

7. Inform the user that a zip file was created (include the filename) and suggest they remove it manually when done. Do NOT run `rm` to delete the zip — this tool does not have permission to remove files.
17 changes: 9 additions & 8 deletions grafana/rmf-app/.config/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ ARG grafana_image=grafana-enterprise

FROM grafana/${grafana_image}:${grafana_version}

ARG anonymous_auth_enabled=true
ARG development=false
ARG TARGETARCH

ARG GO_VERSION=1.23.0
ARG GO_VERSION=1.21.6
ARG GO_ARCH=${TARGETARCH:-amd64}

ENV DEV "${development}"

# Make it as simple as possible to access the grafana instance for development purposes
# Do NOT enable these settings in a public facing / production grafana instance
ENV GF_AUTH_ANONYMOUS_ORG_ROLE "Admin"
ENV GF_AUTH_ANONYMOUS_ENABLED "true"
ENV GF_AUTH_ANONYMOUS_ENABLED "${anonymous_auth_enabled}"
ENV GF_AUTH_BASIC_ENABLED "false"
# Set development mode so plugins can be loaded without the need to sign
ENV GF_DEFAULT_APP_MODE "development"
Expand All @@ -28,14 +29,14 @@ USER root
# Installing supervisor and inotify-tools
RUN if [ "${development}" = "true" ]; then \
if grep -i -q alpine /etc/issue; then \
apk add supervisor inotify-tools git; \
apk add supervisor inotify-tools git; \
elif grep -i -q ubuntu /etc/issue; then \
DEBIAN_FRONTEND=noninteractive && \
apt-get update && \
apt-get install -y supervisor inotify-tools git && \
rm -rf /var/lib/apt/lists/*; \
DEBIAN_FRONTEND=noninteractive && \
apt-get update && \
apt-get install -y supervisor inotify-tools git && \
rm -rf /var/lib/apt/lists/*; \
else \
echo 'ERROR: Unsupported base image' && /bin/false; \
echo 'ERROR: Unsupported base image' && /bin/false; \
fi \
fi

Expand Down
36 changes: 24 additions & 12 deletions grafana/rmf-app/.config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,28 @@ to issues around working with the project.

### Extending the ESLint config

Edit the `.eslintrc` file in the project root in order to extend the ESLint configuration.
Edit the `eslint.config.mjs` file in the project root to extend the ESLint configuration. The following example disables deprecation notices for source files.

**Example:**

```json
{
"extends": "./.config/.eslintrc",
"rules": {
"react/prop-types": "off"
}
}
```javascript
import { defineConfig } from 'eslint/config';
import baseConfig from './.config/eslint.config.mjs';

export default defineConfig([
{
ignores: [
//...
],
},
...baseConfig,
{
files: ['src/**/*.{ts,tsx}'],
rules: {
'@typescript-eslint/no-deprecated': 'off',
},
},
]);
```

---
Expand Down Expand Up @@ -106,9 +117,9 @@ We are going to use [`webpack-merge`](https://github.com/survivejs/webpack-merge
// webpack.config.ts
import type { Configuration } from 'webpack';
import { merge } from 'webpack-merge';
import grafanaConfig from './.config/webpack/webpack.config';
import grafanaConfig, { type Env } from './.config/webpack/webpack.config';

const config = async (env): Promise<Configuration> => {
const config = async (env: Env): Promise<Configuration> => {
const baseConfig = await grafanaConfig(env);

return merge(baseConfig, {
Expand Down Expand Up @@ -151,9 +162,10 @@ version: '3.7'

services:
grafana:
container_name: 'myorg-basic-app'
extends:
file: .config/docker-compose-base.yaml
service: grafana
build:
context: ./.config
args:
grafana_version: ${GRAFANA_VERSION:-9.1.2}
grafana_image: ${GRAFANA_IMAGE:-grafana}
Expand Down
23 changes: 23 additions & 0 deletions grafana/rmf-app/.config/bundler/copyFiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { getPluginJson, hasReadme } from './utils.ts';

const pluginJson = getPluginJson();
const logoPaths: string[] = Array.from(new Set([pluginJson.info?.logos?.large, pluginJson.info?.logos?.small])).filter(
Boolean
);
const screenshotPaths: string[] = pluginJson.info?.screenshots?.map((s: { path: string }) => s.path) || [];

export const copyFilePatterns = [
// If src/README.md exists use it; otherwise the root README
// To `compiler.options.output`
{ from: hasReadme() ? 'README.md' : '../README.md', to: '.', force: true },
{ from: 'plugin.json', to: '.' },
{ from: '../LICENSE', to: '.' },
{ from: '../CHANGELOG.md', to: '.', force: true },
{ from: '**/*.json', to: '.' },
{ from: '**/query_help.md', to: '.', noErrorOnMissing: true },
...logoPaths.map((logoPath) => ({ from: logoPath, to: logoPath })),
...screenshotPaths.map((screenshotPath) => ({
from: screenshotPath,
to: screenshotPath,
})),
];
45 changes: 45 additions & 0 deletions grafana/rmf-app/.config/bundler/externals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { Configuration, ExternalItemFunctionData } from 'webpack';

type ExternalsType = Configuration['externals'];

export const externals: ExternalsType = [
// Required for dynamic publicPath resolution
{ 'amd-module': 'module' },
'lodash',
'jquery',
'moment',
'slate',
'emotion',
'@emotion/react',
'@emotion/css',
'prismjs',
'slate-plain-serializer',
'@grafana/slate-react',
'react',
'react/jsx-runtime',
'react/jsx-dev-runtime',
'react-dom',
'react-redux',
'redux',
'rxjs',
'i18next',
'react-router',
'd3',
'angular',
/^@grafana\/ui/i,
/^@grafana\/runtime/i,
/^@grafana\/data/i,

// Mark legacy SDK imports as external if their name starts with the "grafana/" prefix
({ request }: ExternalItemFunctionData, callback: (error?: Error, result?: string) => void) => {
const prefix = 'grafana/';
const hasPrefix = (request: string) => request.indexOf(prefix) === 0;
const stripPrefix = (request: string) => request.slice(prefix.length);

if (request && hasPrefix(request)) {
return callback(undefined, stripPrefix(request));
}

callback();
},
];
Loading
Loading