Skip to main content

Set up your development environment

This guide walks you through setting up your development environment for Grafana plugin development. Including:

  • Running a development Grafana server with your plugin installed using Docker
  • Setting up GitHub workflows to automate your development and release process
  • Extending configurations for ESLint, Prettier, Jest, TypeScript, and Webpack

Docker development environment​

The create-plugin tool includes a development environment featuring Docker. It allows you to start an instance of the Grafana application for plugin developers against which you can code.

info

It's not necessary to sign a plugin during development. The Docker development environment that is scaffolded with @grafana/create-plugin will load the plugin without a signature. This is because it is configured by default to run in development mode.

Why use the Docker environment​

We have chosen to use Docker because it simplifies the process of creating, deploying, and running applications. It allows you to create consistent and isolated environments for your plugin. This makes it easy to manage dependencies and ensure that the plugin runs the same way across different machines.

With the create-plugin tool, the Docker container is configured with the necessary variables to allow easy access to Grafana and to load plugins without the need for them to be signed. The plugin tool also adds a live reload feature that allows you to make your frontend code changes to trigger refreshes in the browser, and changing the backend code will make the plugin binary to automatically reload.

The docker environment also allows you to attach a debugger to the plugin backend code, making the development process easier.

Get started with Docker​

To start your plugin development project, run the following commands in the order listed:

  1. npm install: Installs frontend dependencies.
  2. npm run dev: Builds and watches the plugin frontend code.
  3. mage -v build:linux: Builds the plugin backend code. Rerun this command every time that you edit your backend files.
  4. npm run server: Starts a Grafana development server running on http://localhost:3000.

Configure the Grafana version​

To test a plugin across different versions of Grafana, set an environment variable. Use GRAFANA_VERSION to set the Grafana version:

GRAFANA_VERSION=10.0.0 npm run server

Configure the Grafana image​

The default Docker image in the plugin tool is grafana-enterprise. If you want to override this image, alter the docker-compose.yaml by changing the grafana_image build argument like so:

docker-compose.yaml
version: '3.7'

services:
grafana:
container_name: 'myorg-basic-app'
build:
context: ./.config
args:
grafana_version: ${GRAFANA_VERSION:-9.1.2}
grafana_image: ${GRAFANA_IMAGE:-grafana}

This example assigns the environment variable GRAFANA_IMAGE to the build arg grafana_image with a default value of grafana. This gives you the option to set the value when running docker compose commands.

Debugging Backend plugin​

If you're developing a plugin with a backend part, run npm run server with DEVELOPMENT=true. The docker compose file exposes the port 2345 for debugging, from a headless delve instance that will run inside the docker environment. You can attach a debugger client to this port to debug your backend code. For example, in VSCode, you can add a launch.json configuration like this:

{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to plugin backend in docker",
"type": "go",
"request": "attach",
"mode": "remote",
"port": 2345,
"host": "127.0.0.1",
"showLog": true,
"trace": "log",
"logOutput": "rpc",
"substitutePath": [
{
"from": "${workspaceFolder}",
"to": "/root/<your-plugin-id>"
}
]
}
]
}

You can control the go version and the architecure used to build your plugin in the docker compose by setting GO_VERSION and GO_ARCH environment variables:

docker-compose.yaml
version: '3.7'

services:
grafana:
container_name: 'myorg-basic-app'
build:
context: ./.config
args:
GO_VERSION: ${GO_VERSION:-1.22}
GO_ARCH: ${GO_ARCH:-amd64}

You will also notice that the docker-compose.yaml file also has the following settings:

docker-compose.yaml
security_opt:
- 'apparmor:unconfined'
- 'seccomp:unconfined'
cap_add:
- SYS_PTRACE

they are necessary to allow delve to attach to the running process and debug it and should not be used in production environments.

Set up GitHub workflows​

Automate your development process to minimize errors and make it faster and more cost-efficient. The create-plugin tool helps you to configure your GitHub actions workflows to help automate your development process.

The CI workflow​

The CI (ci.yml) workflow is designed to lint, type check, and build the frontend and backend. It is also used to run tests on your plugin every time you push changes to your repository. The create-plugin tool helps to catch any issues early in the development process, before they become bigger problems. For more information on end-to-end testing as part of the CI workflow, refer to our documentation.

The release workflow​

The release (release.yml) workflow is designed to build, test, package and sign your plugin whenever you're ready to release a new version. This automates the process of creating releases in GitHub and provides instructions for submitting the plugin to the Grafana plugin catalog.

warning

This workflow requires a Grafana Cloud API key. Before you begin, follow the instructions for generating an Access Policy token.

Storing your Access Policy token as a repository secret in GitHub​

  1. Access Repository Settings:
  • Go to your GitHub repository.
  • Navigate to the "Settings" tab.
  1. In the left sidebar, click on Secrets and Variables -> Actions
  2. Click on the "New repository secret" button.
  3. Add Secret Information:
  • Enter name for your secret - GRAFANA_ACCESS_POLICY_TOKEN.
  • Paste the Access Policy Token value into the "Secret" field.
  1. Click on the "Add secret" button to save the secret.

Once the secret is stored, you can access it in your GitHub Actions workflow:

release.yml
name: Release

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: grafana/plugin-actions/build-plugin@release
with:
grafana_token: ${{ secrets.GRAFANA_ACCESS_POLICY_TOKEN }}

In this example, the secrets.GRAFANA_ACCESS_POLICY_TOKEN variable is used to access the stored token securely within your GitHub Actions workflow. Make sure to adjust the workflow according to your specific needs and the language/environment you are working with.

Triggering the workflow​

To trigger the release workflow, push a Git tag for the plugin version that you want to release:

git tag v1.0.0
git push origin v1.0.0

The compatibility check workflow​

The compatibility check (is-compatible.yml) workflow is designed to check the Grafana API compatibility of your plugin every time you push changes to your repository. This helps to catch potential frontend runtime issues before they occur.

The workflow contains the following steps:

  1. Finding @grafana npm packages in your plugin.
  2. Extracting the exported types of the specified version.
  3. Comparing the differences between that version and the latest version.
  4. Looking for usages of those changed APIs inside your plugin.
  5. Reporting any potential incompatibilities.

The create plugin update workflow​

The create plugin update (cp-update.yml) workflow is designed to automate keeping your plugins development environment and dependencies up to date. It periodically checks the latest version of create-plugin listed on the npm registry and compares it to the version used by your plugin. If there is a newer version available the workflow will run the create-plugin update command, update the frontend dependency lockfile, then create a PR with the changes for review.

This workflow requires content and pull request write access to your plugins repo to be able to push changes and open PRs. Choose from the following two options:

Use the default access token​

To use this option you must allow github actions to create and approve pull requests within your repository settings and use the permissions property in the workflow to elevate the default access token permissions like so:

name: Create Plugin Update

on:
workflow_dispatch:
schedule:
- cron: '0 0 1 * *' # run once a month on the 1st day

permissions:
contents: write
pull-requests: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: grafana/plugin-actions/create-plugin-update@main

Use a personal access token​

To use this option you must create a Github fine-grained personal access token with access to the plugin repository and permission to read and write both contents and pull requests. Once created add the token to the plugin repository action secrets and then pass it to the action like so:

name: Create Plugin Update

on:
workflow_dispatch:
schedule:
- cron: '0 0 1 * *' # run once a month on the 1st day

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: grafana/plugin-actions/create-plugin-update@main
with:
token: ${{ secrets.GH_PAT_TOKEN }}

The bundle stats workflow​

The bundle stats (bundle-stats.yml) workflow is intended to help developers keep an eye on the size of their plugins frontend assets. Changes in PRs trigger this workflow which will compare two webpack stats files; one from the default branch and the other from the PR. It then calculates differences between these assets sizes and posts a formatted comment to the PR giving an overview of any size differences.

bundle-stats.yml
name: Bundle Stats

on:
pull_request:
branches:
- main

permissions:
contents: write
pull-requests: write
actions: read

jobs:
compare:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- uses: grafana/plugin-actions/bundle-size@main

Extend configurations​

The .config/ directory holds the preferred configuration for the different tools used to develop, test, and build a Grafana plugin. Although you can make changes, we recommend against doing so. Instead, follow the guidance in this topic to customize your tooling configs.

danger

Do not edit the .config/ directory or extend the tooling configurations. If you attempt to do so, then you may experience issues such as failure to compile or load in Grafana. Instead of changing the files directly, follow the instructions in this topic to make advanced configurations.

Extend the ESLint config​

Edit the .eslintrc file in the project root to extend the ESLint configuration:

Example:

.eslintrc
{
// Eslint configuration provided by @grafana/create-plugin
"extends": "./.config/.eslintrc",
"rules": {
"react/prop-types": "off"
}
}

The following example can be used to disable deprecation notices.

.eslintrc
{
// Eslint configuration provided by @grafana/create-plugin
"extends": "./.config/.eslintrc",
"overrides": [
{
"files": ["src/**/*.{ts,tsx}"],
"rules": {
"deprecation/deprecation": "off"
}
}
]
}

Extend the Prettier config​

Edit the .prettierrc.js file in the project root to extend the Prettier configuration:

Example:

.prettierrc.js
module.exports = {
// Prettier configuration provided by @grafana/create-plugin
...require('./.config/.prettierrc.js'),
semi: false,
};

Extend the Jest config​

There are two files in the project root that belong to Jest: jest-setup.js and jest.config.js.

jest-setup.js: This file is run before each test file in the suite is executed. It sets up Jest DOM for the testing library and applies some polyfills. For more information, refer to the Jest documentation.

jest.config.js: This is the Jest config file that extends the Grafana config. For more information, refer to the Jest configuration documentation.

ESM errors with Jest​

If you see SyntaxError: Cannot use import statement outside a module when running Jest or npm run test see Troubleshooting.


Extend the TypeScript config​

To extend the TS configuration, edit the tsconfig.json file in the project root:

Example:

tsconfig.json
{
// TypeScript configuration provided by @grafana/create-plugin
"extends": "./.config/tsconfig.json",
"compilerOptions": {
"preserveConstEnums": true
}
}

Extend the Webpack config​

Follow these steps to extend the Webpack configuration that lives in .config/:

1. Create a new Webpack configuration file​

Create a webpack.config.ts file in the project root. This file extends the Webpack config provided by create-plugin.

2. Merge the Grafana config with your custom config​

Use the following webpack-merge command:

webpack.config.ts
import type { Configuration } from 'webpack';
import { merge } from 'webpack-merge';
import grafanaConfig from './.config/webpack/webpack.config';

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

return merge(baseConfig, {
// Add custom config here...
output: {
asyncChunks: true,
},
});
};

export default config;

The following alternative customization excludes "libs" via rules in addition to "node_modules". It also provides fallbacks that are no longer present in Webpack v5.

webpack.config.ts
import type { Configuration } from 'webpack';
import { mergeWithRules } from 'webpack-merge';
import grafanaConfig from './.config/webpack/webpack.config';

const config = async (env: any): Promise<Configuration> => {
const baseConfig = await grafanaConfig(env);
const customConfig = {
module: {
rules: [
{
exclude: /(node_modules|libs)/,
},
],
},
resolve: {
fallback: {
crypto: require.resolve('crypto-browserify'),
fs: false,
path: require.resolve('path-browserify'),
stream: require.resolve('stream-browserify'),
util: require.resolve('util'),
},
},
};
return mergeWithRules({
module: {
rules: {
exclude: 'replace',
},
},
})(baseConfig, customConfig);
};

export default config;

3. Update the package.json to use the new Webpack config​

Update the scripts in the package.json to use the extended Webpack configuration:

package.json
-"build": "webpack -c ./.config/webpack/webpack.config.ts --env production",
+"build": "webpack -c ./webpack.config.ts --env production",
-"dev": "webpack -w -c ./.config/webpack/webpack.config.ts --env development",
+"dev": "webpack -w -c ./webpack.config.ts --env development",