@grafana/create-plugin 7.10.0 → 7.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/codemods/additions/additions.js +5 -0
  3. package/dist/codemods/additions/scripts/experimental-app-sdk.js +275 -0
  4. package/dist/codemods/context.js +13 -1
  5. package/dist/codemods/migrations/migrations.js +6 -0
  6. package/dist/codemods/migrations/scripts/013-jest-esmodules.js +65 -0
  7. package/dist/codemods/runner.js +2 -1
  8. package/dist/codemods/utils.goMod.js +45 -0
  9. package/dist/codemods/utils.js +25 -1
  10. package/dist/commands/add.command.js +9 -4
  11. package/dist/commands/generate/print-success-message.js +1 -1
  12. package/package.json +3 -3
  13. package/src/codemods/additions/additions.ts +5 -0
  14. package/src/codemods/additions/scripts/experimental-app-sdk.test.ts +655 -0
  15. package/src/codemods/additions/scripts/experimental-app-sdk.ts +431 -0
  16. package/src/codemods/context.test.ts +7 -1
  17. package/src/codemods/context.ts +23 -1
  18. package/src/codemods/migrations/migrations.ts +7 -0
  19. package/src/codemods/migrations/scripts/013-jest-esmodules.test.ts +99 -0
  20. package/src/codemods/migrations/scripts/013-jest-esmodules.ts +79 -0
  21. package/src/codemods/runner.ts +3 -1
  22. package/src/codemods/utils.goMod.test.ts +86 -0
  23. package/src/codemods/utils.goMod.ts +70 -0
  24. package/src/codemods/utils.test.ts +49 -0
  25. package/src/codemods/utils.ts +39 -0
  26. package/src/commands/add.command.ts +10 -5
  27. package/src/commands/generate/print-success-message.ts +1 -1
  28. package/templates/app-sdk/.config/AGENTS/app-sdk.md +87 -0
  29. package/templates/app-sdk/.config/app-sdk/README.md +60 -0
  30. package/templates/app-sdk/.config/app-sdk/generate-kinds.mjs +138 -0
  31. package/templates/app-sdk/.github/workflows/generate-kinds-drift.yml +84 -0
  32. package/templates/app-sdk/kinds/README.md +3 -0
  33. package/templates/app-sdk/kinds/config.cue +39 -0
  34. package/templates/app-sdk/kinds/cue.mod/module.cue +4 -0
  35. package/templates/app-sdk/kinds/example.cue +29 -0
  36. package/templates/app-sdk/kinds/manifest.cue +29 -0
  37. package/templates/app-sdk/pkg/generated/example/v1alpha1/doc.go +11 -0
  38. package/templates/app-sdk/pkg/generated/manifestdata/doc.go +12 -0
  39. package/templates/app-sdk/pkg/provider/provider.go +30 -0
  40. package/templates/common/.config/jest/utils.js +2 -0
  41. package/templates/common/_package.json +1 -1
  42. package/vitest.setup.ts +2 -2
@@ -0,0 +1,84 @@
1
+ name: Kinds drift check
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ - main
8
+ paths:
9
+ - 'kinds/**'
10
+ - 'pkg/generated/**'
11
+ - 'src/generated/**'
12
+ - 'src/app-sdk-manifest.json'
13
+ - '.github/workflows/generate-kinds-drift.yml'
14
+ pull_request:
15
+ branches:
16
+ - master
17
+ - main
18
+ paths:
19
+ - 'kinds/**'
20
+ - 'pkg/generated/**'
21
+ - 'src/generated/**'
22
+ - 'src/app-sdk-manifest.json'
23
+ - '.github/workflows/generate-kinds-drift.yml'
24
+
25
+ permissions:
26
+ contents: read
27
+
28
+ jobs:
29
+ check-drift:
30
+ name: Check generated kinds are up to date
31
+ runs-on: ubuntu-latest
32
+ steps:
33
+ - uses: actions/checkout@v6
34
+ with:
35
+ persist-credentials: false
36
+ {{#if_eq packageManagerName "pnpm"}}
37
+ # pnpm action uses the packageManager field in package.json to
38
+ # understand which version to install.
39
+ - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
40
+ {{/if_eq}}
41
+ - name: Setup Node.js environment
42
+ uses: actions/setup-node@v6
43
+ with:
44
+ node-version: '22'
45
+ cache: '{{ packageManagerName }}'
46
+
47
+ - name: Check for backend
48
+ id: check-for-backend
49
+ run: |
50
+ if [ -f "Magefile.go" ]
51
+ then
52
+ echo "has-backend=true" >> $GITHUB_OUTPUT
53
+ fi
54
+
55
+ - name: Setup Go environment
56
+ if: steps.check-for-backend.outputs.has-backend == 'true'
57
+ uses: actions/setup-go@v6
58
+ with:
59
+ go-version-file: go.mod
60
+
61
+ - name: Install dependencies
62
+ run: {{ packageManagerInstallCmd }}
63
+
64
+ - name: Cache grafana-app-sdk CLI
65
+ uses: actions/cache@v4
66
+ with:
67
+ path: node_modules/.cache/grafana-app-sdk
68
+ key: grafana-app-sdk-cli-${{ '{{' }} hashFiles('.config/app-sdk/generate-kinds.mjs') {{ '}}' }}
69
+
70
+ - name: Generate kinds
71
+ run: {{ packageManagerName }} run generate:kinds
72
+
73
+ - name: Fail if generated code is out of date
74
+ run: |
75
+ # Scoped to kinds/ and the generated output: installing dependencies above can itself touch
76
+ # files (e.g. package-lock.json, if CI's Node version differs from the one used to generate
77
+ # the committed lockfile), which is not kind drift and shouldn't fail this check.
78
+ paths="kinds src/generated pkg/generated src/app-sdk-manifest.json"
79
+ if [ -n "$(git status --porcelain -- $paths)" ]; then
80
+ echo "::error::Generated code is out of date. Run '{{ packageManagerName }} run generate:kinds' and commit the result."
81
+ git status --porcelain -- $paths
82
+ git diff --stat -- $paths
83
+ exit 1
84
+ fi
@@ -0,0 +1,3 @@
1
+ # Kinds (grafana-app-sdk)
2
+
3
+ See [`.config/app-sdk/README.md`](../.config/app-sdk/README.md) for the full workflow.
@@ -0,0 +1,39 @@
1
+ package kinds
2
+
3
+ // config holds the grafana-app-sdk code generation settings, and is read by
4
+ // `grafana-app-sdk generate` (via the default `-c config` selector) when you run
5
+ // `{{ packageManagerName }} run generate:kinds`.
6
+ //
7
+ // Paths are relative to the plugin root and match the layout scaffolded by @grafana/create-plugin:
8
+ // the frontend lives in src/.
9
+ config: {
10
+ definitions: {
11
+ manifestVersion: "v1alpha2"
12
+
13
+ // Do not edit this section; the generated manifest must be named exactly as below.
14
+ // The manifest tells Grafana which kinds and capabilities your app serves. Write it
15
+ // straight into src/ so the frontend build picks it up as-is.
16
+ manifestSchemas: true
17
+ path: "src"
18
+ manifestFileName: "app-sdk-manifest.json"
19
+ encoding: "json"
20
+ // Do not generate separate files per-CRD, they are not used.
21
+ genCRDs: false
22
+ }
23
+
24
+ codegen: {
25
+ {{#if hasBackend}}
26
+ // Generated Go types land alongside the plugin backend.
27
+ goEnabled: true
28
+ goGenPath: "pkg/generated/"
29
+ {{else}}
30
+ // This plugin has no Go backend, so skip Go code generation entirely: only TypeScript and
31
+ // the definitions below are emitted, and no Go toolchain is needed to generate them.
32
+ goEnabled: false
33
+ {{/if}}
34
+ // Generated TypeScript types land in the frontend source dir.
35
+ tsGenPath: "src/generated/"
36
+ enableK8sPostProcessing: false
37
+ enableOperatorStatusGeneration: false
38
+ }
39
+ }
@@ -0,0 +1,4 @@
1
+ module: "github.com/{{ kebabCase orgName }}/{{ kebabCase pluginName }}/kinds"
2
+ language: {
3
+ version: "v0.11.0"
4
+ }
@@ -0,0 +1,29 @@
1
+ package kinds
2
+
3
+ // exampleKind holds the information about the Example kind that does not change between versions.
4
+ // Rename this kind (and its file) to model your own resource.
5
+ exampleKind: {
6
+ kind: "Example"
7
+ pluralName: "Examples"
8
+ // Namespaced resources are created per-tenant. Use "Cluster" for global resources.
9
+ scope: "Namespaced"
10
+ codegen: {
11
+ ts: {
12
+ enabled: true
13
+ }
14
+ go: {
15
+ enabled: true
16
+ }
17
+ }
18
+ }
19
+
20
+ // examplev1alpha1 is the v1alpha1 version of the Example kind: the common kind information plus
21
+ // this version's schema. Edit the spec to model your resource, then re-run code generation.
22
+ examplev1alpha1: exampleKind & {
23
+ schema: {
24
+ spec: {
25
+ title: string
26
+ description: string
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,29 @@
1
+ package kinds
2
+
3
+ manifest: {
4
+ // appName is the unique name of your app. It is used to reference the app from other config
5
+ // objects, and to derive the API group your app serves (by default,
6
+ // LOWER(strip dashes)+".ext.grafana.app"). Set `groupOverride` if you need to pin a shorter or
7
+ // different group.
8
+ appName: "{{ pluginId }}"
9
+
10
+ // versions maps each version your app serves to the kinds it exposes. Version names follow the
11
+ // format "v<integer>" or "v<integer>(alpha|beta)<integer>".
12
+ versions: {
13
+ "v1alpha1": v1alpha1
14
+ }
15
+
16
+ // extraPermissions declares any additional permissions your app needs, e.g. access to kinds
17
+ // owned by other apps.
18
+ extraPermissions: {
19
+ accessKinds: []
20
+ }
21
+ }
22
+
23
+ // v1alpha1 is the v1alpha1 version of the app's API.
24
+ v1alpha1: {
25
+ // kinds lists the version-specific kind values served by this version.
26
+ kinds: [examplev1alpha1]
27
+ // served indicates whether this version is served by the API server. Defaults to true.
28
+ served: true
29
+ }
@@ -0,0 +1,11 @@
1
+ // Package v1alpha1 is a stub that exists purely so `go mod tidy` resolves its dependencies before
2
+ // code generation has run. `generate:kinds` fills this package with its own generated files, without
3
+ // removing this one — it's left behind, harmless, since it exports nothing.
4
+ package v1alpha1
5
+
6
+ import (
7
+ _ "k8s.io/apimachinery/pkg/apis/meta/v1"
8
+ _ "k8s.io/apimachinery/pkg/runtime"
9
+ _ "k8s.io/apimachinery/pkg/runtime/schema"
10
+ _ "k8s.io/apimachinery/pkg/types"
11
+ )
@@ -0,0 +1,12 @@
1
+ // Package manifestdata is a stub that exists purely so `go mod tidy` resolves its dependencies before
2
+ // code generation has run. `generate:kinds` fills this package with its own generated files, without
3
+ // removing this one — it's left behind, harmless, since it exports nothing.
4
+ package manifestdata
5
+
6
+ import (
7
+ _ "github.com/grafana/grafana-app-sdk/app"
8
+ _ "github.com/grafana/grafana-app-sdk/resource"
9
+ _ "k8s.io/apimachinery/pkg/runtime"
10
+ _ "k8s.io/kube-openapi/pkg/spec3"
11
+ _ "k8s.io/kube-openapi/pkg/validation/spec"
12
+ )
@@ -0,0 +1,30 @@
1
+ package provider
2
+
3
+ import (
4
+ "github.com/grafana/grafana-app-sdk/app"
5
+ "github.com/grafana/grafana-app-sdk/simple"
6
+
7
+ examplev1alpha1 "github.com/{{ kebabCase orgName }}/{{ kebabCase pluginName }}/pkg/generated/example/v1alpha1"
8
+ "github.com/{{ kebabCase orgName }}/{{ kebabCase pluginName }}/pkg/generated/manifestdata"
9
+ )
10
+
11
+ // New returns the app.Provider, the single entry point to this package. main.go uses it to read the
12
+ // manifest and instantiate the app.App.
13
+ func New() app.Provider {
14
+ return simple.NewAppProvider(manifestdata.LocalManifest(), nil, newApp)
15
+ }
16
+
17
+ // newApp is the factory passed to simple.NewAppProvider; it builds the app.App from the resolved
18
+ // config. Attach a Validator, Mutator, or Reconciler per kind as you need one — see
19
+ // simple.AppManagedKind and ./.config/AGENTS/app-sdk.md.
20
+ func newApp(cfg app.Config) (app.App, error) {
21
+ return simple.NewApp(simple.AppConfig{
22
+ Name: "{{ pluginId }}",
23
+ KubeConfig: cfg.KubeConfig,
24
+ ManagedKinds: []simple.AppManagedKind{
25
+ {
26
+ Kind: examplev1alpha1.Kind(),
27
+ },
28
+ },
29
+ })
30
+ }
@@ -14,6 +14,8 @@ const nodeModulesToTransform = (moduleNames) => `node_modules\/(?!.*(${moduleNam
14
14
  const grafanaESModules = [
15
15
  '.pnpm', // Support using pnpm symlinked packages
16
16
  '@grafana/schema',
17
+ '@react-hookz/web',
18
+ '@ver0/deep-equal',
17
19
  '@wojtekmaj/date-utils',
18
20
  'd3',
19
21
  'd3-color',
@@ -17,7 +17,7 @@
17
17
  "license": "Apache-2.0",
18
18
  "devDependencies": {
19
19
  "@grafana/eslint-config": "^9.0.0",
20
- "@grafana/plugin-e2e": "^3.11.0",
20
+ "@grafana/plugin-e2e": "^3.12.0",
21
21
  "@grafana/sign-plugin": "^3.3.3",
22
22
  "@grafana/tsconfig": "^2.2.0",
23
23
  "@playwright/test": "^1.62.1",{{#if useExperimentalRspack}}
package/vitest.setup.ts CHANGED
@@ -28,10 +28,10 @@ const parseContent = (content?: string) => (content ? JSON.parse(content) : '');
28
28
  expect.extend({
29
29
  async toBeIdempotent(migrate: (context: Context) => Promise<Context>, context: Context) {
30
30
  const firstRun = await migrate(context);
31
- const firstRunDeepCopy = structuredClone(firstRun) as unknown as ClonedContext;
31
+ const firstRunDeepCopy = { files: structuredClone(firstRun.listChanges()) } as unknown as ClonedContext;
32
32
 
33
33
  const secondRun = await migrate(firstRun);
34
- const secondRunDeepCopy = structuredClone(secondRun) as unknown as ClonedContext;
34
+ const secondRunDeepCopy = { files: structuredClone(secondRun.listChanges()) } as unknown as ClonedContext;
35
35
 
36
36
  const result = await compareContexts(firstRunDeepCopy, secondRunDeepCopy);
37
37