From 532543f65adddcaf71326f6893a5c8d7d84d216a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 7 Nov 2025 14:07:21 +0100 Subject: [PATCH 1/2] update workflows --- .github/workflows/bundle-stats.yml | 27 ++++++++ .github/workflows/ci.yml | 102 +++++++++++++++++----------- .github/workflows/cp-update.yml | 2 +- .github/workflows/is-compatible.yml | 22 +++++- .github/workflows/release.yml | 5 +- playwright.config.ts | 2 +- 6 files changed, 114 insertions(+), 46 deletions(-) create mode 100644 .github/workflows/bundle-stats.yml diff --git a/.github/workflows/bundle-stats.yml b/.github/workflows/bundle-stats.yml new file mode 100644 index 0000000..d66f180 --- /dev/null +++ b/.github/workflows/bundle-stats.yml @@ -0,0 +1,27 @@ +name: Bundle Stats + +on: + workflow_dispatch: + pull_request: + branches: + - main + push: + branches: + - main + +permissions: + contents: write + pull-requests: write + actions: read + +jobs: + compare: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + persist-credentials: false + + - uses: grafana/plugin-actions/bundle-size@bundle-size/v1.0.2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a9958d..1794fd2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,17 +9,13 @@ on: branches: - master - main - schedule: - - cron: '12 13 * * *' #once a day at 11 UTC - -permissions: - contents: read - id-token: write jobs: build: name: Build, lint and unit tests runs-on: ubuntu-latest + permissions: + contents: read outputs: plugin-id: ${{ steps.metadata.outputs.plugin-id }} plugin-version: ${{ steps.metadata.outputs.plugin-version }} @@ -29,6 +25,8 @@ jobs: GRAFANA_ACCESS_POLICY_TOKEN: ${{ secrets.GRAFANA_ACCESS_POLICY_TOKEN }} steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Node.js environment uses: actions/setup-node@v4 with: @@ -63,14 +61,14 @@ jobs: - name: Test backend if: steps.check-for-backend.outputs.has-backend == 'true' - uses: magefile/mage-action@v3 + uses: magefile/mage-action@6f50bbb8ea47d56e62dee92392788acbc8192d0b # v3.1.0 with: version: latest args: coverage - name: Build backend if: steps.check-for-backend.outputs.has-backend == 'true' - uses: magefile/mage-action@v3 + uses: magefile/mage-action@6f50bbb8ea47d56e62dee92392788acbc8192d0b # v3.1.0 with: version: latest args: buildAll @@ -103,14 +101,19 @@ jobs: - name: Package plugin id: package-plugin run: | - mv dist ${{ steps.metadata.outputs.plugin-id }} - zip ${{ steps.metadata.outputs.archive }} ${{ steps.metadata.outputs.plugin-id }} -r + mv dist ${PLUGIN_ID} + zip ${ARCHIVE} ${PLUGIN_ID} -r + env: + ARCHIVE: ${{ steps.metadata.outputs.archive }} + PLUGIN_ID: ${{ steps.metadata.outputs.plugin-id }} - # - name: Check plugin.json - # run: | - # docker run --pull=always \ - # -v $PWD/$:/archive.zip \ - # grafana/plugin-validator-cli -analyzer=metadatavalid /archive.zip + - name: Check plugin.json + run: | + docker run --pull=always \ + -v $PWD/${ARCHIVE}:/archive.zip \ + grafana/plugin-validator-cli -analyzer=metadatavalid /archive.zip + env: + ARCHIVE: ${{ steps.metadata.outputs.archive }} - name: Archive Build uses: actions/upload-artifact@v4 @@ -122,6 +125,8 @@ jobs: resolve-versions: name: Resolve e2e images runs-on: ubuntu-latest + permissions: + contents: read timeout-minutes: 3 needs: build if: ${{ needs.build.outputs.has-e2e == 'true' }} @@ -130,16 +135,18 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Resolve Grafana E2E versions id: resolve-versions - uses: grafana/plugin-actions/e2e-version@main - with: - version-resolver-type: plugin-grafana-dependency - grafana-dependency: '>=8.5.0' + uses: grafana/plugin-actions/e2e-version@e2e-version/v1.1.2 playwright-tests: needs: [resolve-versions, build] timeout-minutes: 15 + permissions: + contents: read strategy: fail-fast: false matrix: @@ -148,6 +155,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Download plugin uses: actions/download-artifact@v4 @@ -172,10 +181,12 @@ jobs: - name: Start Grafana run: | docker compose pull - DEVELOPMENT=false GRAFANA_VERSION=${{ matrix.GRAFANA_IMAGE.VERSION }} GRAFANA_IMAGE=${{ matrix.GRAFANA_IMAGE.NAME }} docker compose up -d + ANONYMOUS_AUTH_ENABLED=false DEVELOPMENT=false GRAFANA_VERSION=${{ matrix.GRAFANA_IMAGE.VERSION }} GRAFANA_IMAGE=${{ matrix.GRAFANA_IMAGE.NAME }} docker compose up -d - name: Wait for grafana server - uses: grafana/plugin-actions/wait-for-grafana@main + uses: grafana/plugin-actions/wait-for-grafana@wait-for-grafana/v1.0.2 + with: + url: http://localhost:3000/login - name: Install Playwright Browsers run: npm exec playwright install chromium --with-deps @@ -184,6 +195,14 @@ jobs: id: run-tests run: npm run e2e + - name: Upload e2e test summary + uses: grafana/plugin-actions/playwright-gh-pages/upload-report-artifacts@upload-report-artifacts/v1.0.1 + if: ${{ always() && !cancelled() }} + with: + upload-report: true + github-token: ${{ secrets.GITHUB_TOKEN }} + test-outcome: ${{ steps.run-tests.outcome }} + - name: Docker logs if: ${{ always() && steps.run-tests.outcome == 'failure' }} run: | @@ -192,27 +211,28 @@ jobs: - name: Stop grafana docker run: docker compose down - - name: Upload server log - uses: actions/upload-artifact@v4 - if: ${{ always() && steps.run-tests.outcome == 'failure' }} - with: - name: ${{ matrix.GRAFANA_IMAGE.NAME }}-v${{ matrix.GRAFANA_IMAGE.VERSION }}-${{github.run_id}}-server-log - path: grafana-server.log - retention-days: 5 - - - name: Publish report to GCS - if: ${{ github.repository_owner == 'grafana' && (failure() && steps.run-tests.outcome == 'failure') }} - uses: grafana/plugin-actions/publish-report@main - with: - grafana-version: ${{ matrix.GRAFANA_IMAGE.VERSION }} - path: playwright-report - - # Uncomment this step to upload the Playwright report to Github artifacts. - # If your repository is public, the report will be public on the Internet so beware not to expose sensitive information. - # - name: Upload artifacts + # Uncomment this step to upload the server log to Github artifacts. Remember Github artifacts are public on the Internet if the repository is public. + # - name: Upload server log # uses: actions/upload-artifact@v4 # if: ${{ always() && steps.run-tests.outcome == 'failure' }} # with: - # name: playwright-report-${{ matrix.GRAFANA_IMAGE.NAME }}-v${{ matrix.GRAFANA_IMAGE.VERSION }}-${{github.run_id}} - # path: playwright-report/ + # name: ${{ matrix.GRAFANA_IMAGE.NAME }}-v${{ matrix.GRAFANA_IMAGE.VERSION }}-${{github.run_id}}-server-log + # path: grafana-server.log # retention-days: 5 + + publish-report: + if: ${{ always() && !cancelled() }} + permissions: + contents: write + pull-requests: write + needs: [playwright-tests] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Publish report + uses: grafana/plugin-actions/playwright-gh-pages/deploy-report-pages@pw-gh-pages/cleanup-git-history + with: + github-token: ${{ github.token }} diff --git a/.github/workflows/cp-update.yml b/.github/workflows/cp-update.yml index edfec47..8e0aaf6 100644 --- a/.github/workflows/cp-update.yml +++ b/.github/workflows/cp-update.yml @@ -18,7 +18,7 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: grafana/plugin-actions/create-plugin-update@main + - uses: grafana/plugin-actions/create-plugin-update@create-plugin-update/v1.1.0 # Uncomment to use a fine-grained personal access token instead of default github token # (For more info on how to generate the token see https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) # with: diff --git a/.github/workflows/is-compatible.yml b/.github/workflows/is-compatible.yml index 8b300f4..4426bea 100644 --- a/.github/workflows/is-compatible.yml +++ b/.github/workflows/is-compatible.yml @@ -4,16 +4,34 @@ on: [pull_request] jobs: compatibilitycheck: runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Setup Node.js environment - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '22' cache: 'npm' + - name: Install dependencies run: npm ci + - name: Build plugin run: npm run build + + - name: Find module.ts or module.tsx + id: find-module-ts + run: | + MODULETS="$(find ./src -type f \( -name "module.ts" -o -name "module.tsx" \))" + echo "modulets=${MODULETS}" >> $GITHUB_OUTPUT + - name: Compatibility check - run: npx @grafana/levitate@latest is-compatible --path src/module.ts --target @grafana/data,@grafana/ui,@grafana/runtime + uses: grafana/plugin-actions/is-compatible@is-compatible/v1.0.2 + with: + module: ${{ steps.find-module-ts.outputs.modulets }} + comment-pr: 'no' + fail-if-incompatible: 'yes' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3189c56..334ad57 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: grafana/plugin-actions/build-plugin@main + with: + persist-credentials: false + + - uses: grafana/plugin-actions/build-plugin@build-plugin/v1.0.2 # Uncomment to enable plugin signing # (For more info on how to generate the access policy token see https://grafana.com/developers/plugin-tools/publish-a-plugin/sign-a-plugin#generate-an-access-policy-token) #with: diff --git a/playwright.config.ts b/playwright.config.ts index 9e52149..f7e0154 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -20,7 +20,7 @@ export default defineConfig({ /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, /* Retry on CI only */ - retries: process.env.CI ? 2 : 0, + retries: process.env.CI ? 1 : 0, /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ From 8e85a064452a87b478824a1ab87aefc43aca2220 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 7 Nov 2025 14:16:09 +0100 Subject: [PATCH 2/2] run update command --- .config/.cprc.json | 3 +- .config/.eslintrc | 31 - .config/README.md | 9 +- .config/bundler/externals.ts | 44 + .config/docker-compose-base.yaml | 31 + .config/eslint.config.mjs | 31 + .config/jest-setup.js | 2 +- .config/jest.config.js | 3 +- .config/jest/utils.js | 6 + .config/tsconfig.json | 2 +- .config/types/bundler-rules.d.ts | 37 + .config/types/webpack-plugins.d.ts | 83 + .config/webpack/BuildModeWebpackPlugin.ts | 2 +- .config/webpack/utils.ts | 25 +- .config/webpack/webpack.config.ts | 69 +- .cprc.json | 3 +- .eslintrc | 7 - docker-compose.yaml | 20 +- eslint.config.mjs | 45 + go.mod | 118 +- go.sum | 274 +- package-lock.json | 5069 +++++++++++---------- package.json | 38 +- 23 files changed, 3155 insertions(+), 2797 deletions(-) delete mode 100644 .config/.eslintrc create mode 100644 .config/bundler/externals.ts create mode 100644 .config/docker-compose-base.yaml create mode 100644 .config/eslint.config.mjs create mode 100644 .config/types/bundler-rules.d.ts create mode 100644 .config/types/webpack-plugins.d.ts delete mode 100644 .eslintrc create mode 100644 eslint.config.mjs diff --git a/.config/.cprc.json b/.config/.cprc.json index 4065506..c093d3e 100644 --- a/.config/.cprc.json +++ b/.config/.cprc.json @@ -1,3 +1,4 @@ { - "version": "5.12.4" + "version": "6.1.8", + "features": {} } diff --git a/.config/.eslintrc b/.config/.eslintrc deleted file mode 100644 index b133be4..0000000 --- a/.config/.eslintrc +++ /dev/null @@ -1,31 +0,0 @@ -/* - * ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️ - * - * In order to extend the configuration follow the steps in - * https://grafana.com/developers/plugin-tools/get-started/set-up-development-environment#extend-the-eslint-config - */ -{ - "extends": ["@grafana/eslint-config"], - "root": true, - "rules": { - "react/prop-types": "off" - }, - "overrides": [ - { - "plugins": ["deprecation"], - "files": ["src/**/*.{ts,tsx}"], - "rules": { - "deprecation/deprecation": "warn" - }, - "parserOptions": { - "project": "./tsconfig.json" - } - }, - { - "files": ["./tests/**/*"], - "rules": { - "react-hooks/rules-of-hooks": "off" - } - } - ] -} diff --git a/.config/README.md b/.config/README.md index 4de64b5..5ba3186 100644 --- a/.config/README.md +++ b/.config/README.md @@ -106,9 +106,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 => { +const config = async (env: Env): Promise => { const baseConfig = await grafanaConfig(env); return merge(baseConfig, { @@ -151,9 +151,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} diff --git a/.config/bundler/externals.ts b/.config/bundler/externals.ts new file mode 100644 index 0000000..9409493 --- /dev/null +++ b/.config/bundler/externals.ts @@ -0,0 +1,44 @@ +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-dom', + 'react-redux', + 'redux', + 'rxjs', + 'i18next', + 'react-router', + 'react-router-dom', + '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(); + }, +]; diff --git a/.config/docker-compose-base.yaml b/.config/docker-compose-base.yaml new file mode 100644 index 0000000..0435765 --- /dev/null +++ b/.config/docker-compose-base.yaml @@ -0,0 +1,31 @@ +services: + grafana: + user: root + container_name: 'grafana-test-datasource' + + build: + context: . + args: + grafana_image: ${GRAFANA_IMAGE:-grafana-enterprise} + grafana_version: ${GRAFANA_VERSION:-12.2.0} + development: ${DEVELOPMENT:-false} + anonymous_auth_enabled: ${ANONYMOUS_AUTH_ENABLED:-true} + ports: + - 3000:3000/tcp + - 2345:2345/tcp # delve + security_opt: + - 'apparmor:unconfined' + - 'seccomp:unconfined' + cap_add: + - SYS_PTRACE + volumes: + - ../dist:/var/lib/grafana/plugins/grafana-test-datasource + - ../provisioning:/etc/grafana/provisioning + - ..:/root/grafana-test-datasource + + environment: + NODE_ENV: development + GF_LOG_FILTERS: plugin.grafana-test-datasource:debug + GF_LOG_LEVEL: debug + GF_DATAPROXY_LOGGING: 1 + GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: grafana-test-datasource diff --git a/.config/eslint.config.mjs b/.config/eslint.config.mjs new file mode 100644 index 0000000..968c071 --- /dev/null +++ b/.config/eslint.config.mjs @@ -0,0 +1,31 @@ +import { defineConfig } from 'eslint/config'; +import grafanaConfig from '@grafana/eslint-config/flat.js'; + +export default defineConfig([ + ...grafanaConfig, + { + rules: { + 'react/prop-types': 'off', + }, + }, + { + files: ['src/**/*.{ts,tsx}'], + + languageOptions: { + parserOptions: { + project: './tsconfig.json', + }, + }, + + rules: { + '@typescript-eslint/no-deprecated': 'warn', + }, + }, + { + files: ['./tests/**/*'], + + rules: { + 'react-hooks/rules-of-hooks': 'off', + }, + }, +]); diff --git a/.config/jest-setup.js b/.config/jest-setup.js index 74832e3..7b1771e 100644 --- a/.config/jest-setup.js +++ b/.config/jest-setup.js @@ -2,7 +2,7 @@ * ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️ * * In order to extend the configuration follow the steps in - * https://grafana.com/developers/plugin-tools/get-started/set-up-development-environment#extend-the-jest-config + * https://grafana.com/developers/plugin-tools/how-to-guides/extend-configurations#extend-the-jest-config */ import '@testing-library/jest-dom'; diff --git a/.config/jest.config.js b/.config/jest.config.js index 09704b4..efe1938 100644 --- a/.config/jest.config.js +++ b/.config/jest.config.js @@ -2,7 +2,7 @@ * ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️ * * In order to extend the configuration follow the steps in - * https://grafana.com/developers/plugin-tools/get-started/set-up-development-environment#extend-the-jest-config + * https://grafana.com/developers/plugin-tools/how-to-guides/extend-configurations#extend-the-jest-config */ const path = require('path'); @@ -40,4 +40,5 @@ module.exports = { // Jest will throw `Cannot use import statement outside module` if it tries to load an // ES module without it being transformed first. ./config/README.md#esm-errors-with-jest transformIgnorePatterns: [nodeModulesToTransform(grafanaESModules)], + watchPathIgnorePatterns: ['/node_modules', '/dist'], }; diff --git a/.config/jest/utils.js b/.config/jest/utils.js index fdca0de..55d9cb6 100644 --- a/.config/jest/utils.js +++ b/.config/jest/utils.js @@ -14,12 +14,18 @@ const nodeModulesToTransform = (moduleNames) => `node_modules\/(?!.*(${moduleNam const grafanaESModules = [ '.pnpm', // Support using pnpm symlinked packages '@grafana/schema', + '@wojtekmaj/date-utils', 'd3', 'd3-color', 'd3-force', 'd3-interpolate', 'd3-scale-chromatic', + 'get-user-locale', + 'marked', + 'memoize', + 'mimic-function', 'ol', + 'react-calendar', 'react-colorful', 'rxjs', 'uuid', diff --git a/.config/tsconfig.json b/.config/tsconfig.json index 15e64ac..c0fc6d4 100644 --- a/.config/tsconfig.json +++ b/.config/tsconfig.json @@ -2,7 +2,7 @@ * ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️ * * In order to extend the configuration follow the steps in - * https://grafana.com/developers/plugin-tools/get-started/set-up-development-environment#extend-the-typescript-config + * https://grafana.com/developers/plugin-tools/how-to-guides/extend-configurations#extend-the-typescript-config */ { "compilerOptions": { diff --git a/.config/types/bundler-rules.d.ts b/.config/types/bundler-rules.d.ts new file mode 100644 index 0000000..e67197c --- /dev/null +++ b/.config/types/bundler-rules.d.ts @@ -0,0 +1,37 @@ +// Image declarations +declare module '*.gif' { + const src: string; + export default src; +} + +declare module '*.jpg' { + const src: string; + export default src; +} + +declare module '*.jpeg' { + const src: string; + export default src; +} + +declare module '*.png' { + const src: string; + export default src; +} + +declare module '*.webp' { + const src: string; + export default src; +} + +declare module '*.svg' { + const src: string; + export default src; +} + +// Font declarations +declare module '*.woff'; +declare module '*.woff2'; +declare module '*.eot'; +declare module '*.ttf'; +declare module '*.otf'; diff --git a/.config/types/webpack-plugins.d.ts b/.config/types/webpack-plugins.d.ts new file mode 100644 index 0000000..6dbab10 --- /dev/null +++ b/.config/types/webpack-plugins.d.ts @@ -0,0 +1,83 @@ +declare module 'replace-in-file-webpack-plugin' { + import { Compiler, Plugin } from 'webpack'; + + interface ReplaceRule { + search: string | RegExp; + replace: string | ((match: string) => string); + } + + interface ReplaceOption { + dir?: string; + files?: string[]; + test?: RegExp | RegExp[]; + rules: ReplaceRule[]; + } + + class ReplaceInFilePlugin extends Plugin { + constructor(options?: ReplaceOption[]); + options: ReplaceOption[]; + apply(compiler: Compiler): void; + } + + export = ReplaceInFilePlugin; +} + +declare module 'webpack-livereload-plugin' { + import { ServerOptions } from 'https'; + import { Compiler, Plugin, Stats, Compilation } from 'webpack'; + + interface Options extends Pick { + /** + * protocol for livereload `