Skip to content
Draft
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
45 changes: 45 additions & 0 deletions .github/workflows/check-config-schema.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Check Config Schema

permissions:
contents: read

on:
pull_request:
paths:
- 'src/types/config.ts'
- 'scripts/generate-config-schema.ts'
push:
branches:
- main
paths:
- 'src/types/config.ts'
- 'scripts/generate-config-schema.ts'

jobs:
check-config-schema:
name: Verify config schema is up-to-date
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'

- name: Install dependencies
run: yarn install --immutable

- name: Lint config types (import guard)
run: yarn lint --no-cache -- src/types/config.ts

- name: Generate config schema
run: yarn generate:config-schema

- name: Check for uncommitted schema changes
run: |
if ! git diff --exit-code pkg/schema/config.json; then
echo "::error::Config schema is out of date. Run 'yarn generate:config-schema' and commit the changes."
exit 1
fi
echo "Config schema is up-to-date."
17 changes: 17 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,21 @@ export default defineConfig([
],
},
...baseConfig,
{
files: ['src/types/config.ts'],
rules: {
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['./*', '../*'],
message:
'src/types/config.ts must be self-contained with no local imports to ensure reliable schema generation.',
},
],
},
],
},
},
]);
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"spellcheck": "cspell -c cspell.config.json \"**/*.{ts,tsx,js,go,md,mdx,yml,yaml,json,scss,css}\"",
"test": "jest --watch --onlyChanged",
"test:ci": "jest --passWithNoTests --maxWorkers 4",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"generate:config-schema": "ts-node scripts/generate-config-schema.ts"
},
"dependencies": {
"@emotion/css": "11.13.5",
Expand Down Expand Up @@ -89,7 +90,8 @@
"webpack-cli": "6.0.1",
"webpack-livereload-plugin": "3.0.2",
"webpack-subresource-integrity": "5.1.0",
"webpack-virtual-modules": "0.6.2"
"webpack-virtual-modules": "0.6.2",
"zod": "4.3.6"
},
"resolutions": {
"@remix-run/router": "1.23.2",
Expand Down
9 changes: 9 additions & 0 deletions pkg/plugin/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package plugin
import (
"context"
"fmt"
"net/http"

"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
schemas "github.com/grafana/schemads"

"github.com/grafana/github-datasource/pkg/schema"
"github.com/grafana/github-datasource/pkg/github"
"github.com/grafana/github-datasource/pkg/models"
)
Expand All @@ -19,6 +21,13 @@ type GitHubInstanceWithSchema struct {
}

func (g *GitHubInstanceWithSchema) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
if req.Path == "schema/config" {
return sender.Send(&backend.CallResourceResponse{
Status: http.StatusOK,
Headers: map[string][]string{"Content-Type": {"application/json"}},
Body: schema.ConfigSchemaJSON,
})
}
return g.SchemaDatasource.CallResource(ctx, req, sender)
}

Expand Down
8 changes: 8 additions & 0 deletions pkg/schema/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package schema

import (
_ "embed"
)

//go:embed config.json
var ConfigSchemaJSON []byte
178 changes: 178 additions & 0 deletions pkg/schema/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "GitHubDataSourceConfig",
"description": "Configuration schema for the Grafana GitHub data source plugin",
"type": "object",
"properties": {
"jsonData": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"allOf": [
{
"anyOf": [
{
"anyOf": [
{
"type": "object",
"properties": {
"githubPlan": {
"description": "GitHub plan type (basic)",
"type": "string",
"const": "github-basic"
},
"githubUrl": {
"not": {},
"description": "Not applicable for GitHub basic plan"
}
},
"required": [
"githubUrl"
],
"additionalProperties": false,
"description": "Configuration for GitHub basic plan"
},
{
"type": "object",
"properties": {
"githubPlan": {
"type": "string",
"const": "github-enterprise-cloud",
"description": "GitHub plan type (Enterprise Cloud)"
},
"githubUrl": {
"not": {},
"description": "Not applicable for GitHub Enterprise Cloud"
}
},
"required": [
"githubPlan",
"githubUrl"
],
"additionalProperties": false,
"description": "Configuration for GitHub Enterprise Cloud plan"
}
]
},
{
"type": "object",
"properties": {
"githubPlan": {
"type": "string",
"const": "github-enterprise-server",
"description": "GitHub plan type (Enterprise Server)"
},
"githubUrl": {
"type": "string",
"description": "The URL of the GitHub Enterprise Server instance"
}
},
"required": [
"githubPlan",
"githubUrl"
],
"additionalProperties": false,
"description": "Configuration for GitHub Enterprise Server plan"
}
]
},
{
"anyOf": [
{
"type": "object",
"properties": {
"selectedAuthType": {
"description": "Authentication type (Personal Access Token)",
"type": "string",
"const": "personal-access-token"
},
"appId": {
"not": {},
"description": "Not applicable for PAT authentication"
},
"installationId": {
"not": {},
"description": "Not applicable for PAT authentication"
}
},
"required": [
"appId",
"installationId"
],
"additionalProperties": false,
"description": "Authentication options for Personal Access Token"
},
{
"type": "object",
"properties": {
"selectedAuthType": {
"type": "string",
"const": "github-app",
"description": "Authentication type (GitHub App)"
},
"appId": {
"type": "string",
"description": "The GitHub App ID"
},
"installationId": {
"type": "string",
"description": "The GitHub App installation ID"
}
},
"required": [
"selectedAuthType",
"appId",
"installationId"
],
"additionalProperties": false,
"description": "Authentication options for GitHub App"
}
]
}
],
"description": "GitHub data source configuration options (jsonData)"
},
"secureJsonData": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"anyOf": [
{
"type": "object",
"properties": {
"accessToken": {
"type": "string",
"description": "Personal access token for GitHub API authentication"
},
"privateKey": {
"not": {},
"description": "Not applicable for PAT authentication"
}
},
"required": [
"accessToken",
"privateKey"
],
"additionalProperties": false,
"description": "Secure data for Personal Access Token authentication"
},
{
"type": "object",
"properties": {
"accessToken": {
"not": {},
"description": "Not applicable for GitHub App authentication"
},
"privateKey": {
"type": "string",
"description": "Private key for GitHub App authentication (PEM format)"
}
},
"required": [
"accessToken",
"privateKey"
],
"additionalProperties": false,
"description": "Secure data for GitHub App authentication"
}
],
"description": "Secure JSON data for GitHub data source authentication (secureJsonData)"
}
}
}
23 changes: 23 additions & 0 deletions scripts/generate-config-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { z } from 'zod';
import * as fs from 'fs';
import * as path from 'path';

import { GitHubDataSourceOptionsSchema, GitHubSecureJsonDataSchema } from '../src/types/config';

const configSchema = {
$schema: 'https://json-schema.org/draft/2020-12/schema',
title: 'GitHubDataSourceConfig',
description: 'Configuration schema for the Grafana GitHub data source plugin',
type: 'object' as const,
properties: {
jsonData: z.toJSONSchema(GitHubDataSourceOptionsSchema),
secureJsonData: z.toJSONSchema(GitHubSecureJsonDataSchema),
},
};

const schemaJSON = JSON.stringify(configSchema, null, 2) + '\n';

const outPath = path.resolve(__dirname, '..', 'pkg', 'schema', 'config.json');
fs.mkdirSync(path.dirname(outPath), { recursive: true });
fs.writeFileSync(outPath, schemaJSON);
console.log(`Config JSON schema written to ${outPath}`);
18 changes: 18 additions & 0 deletions scripts/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/sh
#
# Git pre-commit hook that regenerates the config JSON schema
# when src/types/config.ts is modified.
#
# To install, run from the repository root:
# cp scripts/pre-commit .git/hooks/pre-commit
# chmod +x .git/hooks/pre-commit

STAGED=$(git diff --cached --name-only)

if echo "$STAGED" | grep -q "src/types/config.ts"; then
echo "Config types changed — regenerating config JSON schema..."
yarn generate:config-schema

git add pkg/schema/config.json
echo "Config JSON schema updated and staged."
fi
Loading
Loading