Merge origin/main into iortega/transform-v1-to-v2
CodeQL checks / Detect whether code changed (push) Waiting to run Details
CodeQL checks / Analyze (actions) (push) Blocked by required conditions Details
CodeQL checks / Analyze (go) (push) Blocked by required conditions Details
CodeQL checks / Analyze (javascript) (push) Blocked by required conditions Details

Resolved conflicts in:
- apps/dashboard/pkg/migration/conversion/conversion.go: Updated conversion functions to use withConversionMetrics
- pkg/registry/apis/dashboard/register.go: Updated migration initialization
- pkg/storage/unified/testing/storage_backend.go: Fixed test helper function
- public/app/features/dashboard/state/DashboardMigratorToBackend.test.ts: Updated variable adapters registration
- Removed .betterer.results file as it was deleted in origin/main
This commit is contained in:
Ivan Ortega 2025-10-07 14:53:34 +02:00
commit 0b0282050f
4017 changed files with 370031 additions and 82770 deletions

View File

@ -1,13 +1,13 @@
[build]
bin = "./bin/grafana"
bin = "./bin/grafana-air"
args_bin = ["server", "-profile", "-profile-addr=127.0.0.1", "-profile-port=6000", "-profile-block-rate=1", "-profile-mutex-rate=5", "-packaging=dev", "cfg:app_mode=development"]
cmd = "make GO_BUILD_DEV=1 build-backend"
cmd = "make GO_BUILD_DEV=1 build-air"
exclude_regex = ["_test.go", "_gen.go"]
exclude_unchanged = true
follow_symlink = true
include_dir = ["apps", "conf", "devenv/dev-dashboards", "pkg", "public/views"]
include_dir = ["apps", "conf", "pkg", "public/views"]
include_ext = ["go", "ini", "toml", "html", "json"]
stop_on_error = false
stop_on_error = true
send_interrupt = true
kill_delay = 500

View File

@ -1,181 +0,0 @@
// @ts-check
const emotionPlugin = require('@emotion/eslint-plugin');
const importPlugin = require('eslint-plugin-import');
const jestPlugin = require('eslint-plugin-jest');
const jsxA11yPlugin = require('eslint-plugin-jsx-a11y');
const lodashPlugin = require('eslint-plugin-lodash');
const barrelPlugin = require('eslint-plugin-no-barrel-files');
const reactPlugin = require('eslint-plugin-react');
const testingLibraryPlugin = require('eslint-plugin-testing-library');
const grafanaConfig = require('@grafana/eslint-config/flat');
const grafanaPlugin = require('@grafana/eslint-plugin');
const grafanaI18nPlugin = require('@grafana/i18n/eslint-plugin');
// Include the base Grafana configs and remove the rules,
// as we just want to pull in all of the necessary configuration but not run the rules
// (this should only be concerned with checking rules that we want to improve,
// so there's no need to try and run the rules that will be linted properly anyway)
const mappedBaseConfigs = grafanaConfig.map((config) => {
const { rules, ...baseConfig } = config;
return baseConfig;
});
/**
* @type {Array<import('eslint').Linter.Config>}
*/
module.exports = [
{
name: 'grafana/betterer-ignores',
ignores: [
'.github',
'.yarn',
'**/.*',
'**/*.gen.ts',
'**/build/',
'**/compiled/',
'**/dist/',
'data/',
'deployment_tools_config.json',
'devenv',
'e2e-playwright/test-plugins',
'e2e/tmp',
'packages/grafana-ui/src/components/Icon/iconBundle.ts',
'pkg',
'playwright-report',
'public/lib/monaco/',
'public/locales/_build',
'public/locales/**/*.js',
'public/vendor/',
'scripts/grafana-server/tmp',
'!.betterer.eslint.config.js',
],
},
{
name: 'react/jsx-runtime-rules',
rules: reactPlugin.configs.flat['jsx-runtime'].rules,
},
...mappedBaseConfigs,
{
files: ['**/*.{ts,tsx,js}'],
plugins: {
'@emotion': emotionPlugin,
lodash: lodashPlugin,
jest: jestPlugin,
import: importPlugin,
'jsx-a11y': jsxA11yPlugin,
'no-barrel-files': barrelPlugin,
'@grafana': grafanaPlugin,
'testing-library': testingLibraryPlugin,
'@grafana/i18n': grafanaI18nPlugin,
},
linterOptions: {
// This reports unused disable directives that we can clean up but
// it also conflicts with the betterer eslint rules so disabled
reportUnusedDisableDirectives: false,
},
},
{
files: ['**/*.{js,jsx,ts,tsx}'],
rules: {
'react-hooks/rules-of-hooks': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@grafana/no-aria-label-selectors': 'error',
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['@grafana/ui*', '*/Layout/*'],
importNames: ['Layout', 'HorizontalGroup', 'VerticalGroup'],
message: 'Use Stack component instead.',
},
{
group: ['@grafana/ui/src/*', '@grafana/runtime/src/*', '@grafana/data/src/*'],
message: 'Import from the public export instead.',
},
],
},
],
},
},
{
files: ['**/*.{js,jsx,ts,tsx}'],
ignores: [
'**/*.{test,spec}.{ts,tsx}',
'**/__mocks__/**',
'**/public/test/**',
'**/mocks.{ts,tsx}',
'**/mocks/**/*.{ts,tsx}',
'**/spec/**/*.{ts,tsx}',
],
rules: {
'@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'never' }],
},
},
{
files: ['**/*.{js,jsx,ts,tsx}'],
ignores: [
'**/*.{test,spec}.{ts,tsx}',
'**/__mocks__/**',
'**/public/test/**',
'**/mocks.{ts,tsx}',
'**/spec/**/*.{ts,tsx}',
],
rules: {
'no-restricted-syntax': [
'error',
{
selector: 'Identifier[name=localStorage]',
message: 'Direct usage of localStorage is not allowed. import store from @grafana/data instead',
},
{
selector: 'MemberExpression[object.name=localStorage]',
message: 'Direct usage of localStorage is not allowed. import store from @grafana/data instead',
},
{
selector:
'Program:has(ImportDeclaration[source.value="@grafana/ui"] ImportSpecifier[imported.name="Card"]) JSXOpeningElement[name.name="Card"]:not(:has(JSXAttribute[name.name="noMargin"]))',
message:
'Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.',
},
{
selector:
'Program:has(ImportDeclaration[source.value="@grafana/ui"] ImportSpecifier[imported.name="Field"]) JSXOpeningElement[name.name="Field"]:not(:has(JSXAttribute[name.name="noMargin"]))',
message:
'Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.',
},
{
selector: 'CallExpression[callee.type="MemberExpression"][callee.property.name="localeCompare"]',
message:
'Using localeCompare() can cause performance issues when sorting large datasets. Consider using Intl.Collator for better performance when sorting arrays, or add an eslint-disable comment if sorting a small, known dataset.',
},
],
},
},
{
files: ['public/app/**/*.{ts,tsx}'],
rules: {
'no-barrel-files/no-barrel-files': 'error',
},
},
{
// custom rule for Table to avoid performance regressions
files: ['packages/grafana-ui/src/components/Table/TableNG/Cells/**/*.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['**/themes/ThemeContext'],
importNames: ['useStyles2', 'useTheme2'],
message:
'Do not use "useStyles2" or "useTheme2" in a cell directly. Instead, provide styles to cells via `getDefaultCellStyles` or `getCellSpecificStyles`.',
},
],
},
],
},
},
];

File diff suppressed because it is too large Load Diff

View File

@ -1,119 +0,0 @@
import { BettererFileTest } from '@betterer/betterer';
import { ESLint } from 'eslint';
import { promises as fs } from 'fs';
// Why are we ignoring these?
// They're all deprecated/being removed so doesn't make sense to fix types
const eslintPathsToIgnore = [
'packages/grafana-ui/src/graveyard', // will be removed alongside angular in Grafana 12
'public/app/angular', // will be removed in Grafana 12
'public/app/plugins/panel/graph', // will be removed alongside angular in Grafana 12
'public/app/plugins/panel/table-old', // will be removed alongside angular in Grafana 12
];
// Avoid using functions that report the position of the issues, as this causes a lot of merge conflicts
export default {
'better eslint': () =>
countEslintErrors()
.include('**/*.{ts,tsx}')
.exclude(new RegExp(eslintPathsToIgnore.join('|'))),
'no undocumented stories': () => countUndocumentedStories().include('**/*.story.tsx'),
'no skipping a11y tests in stories': () => countSkippedA11yTestStories().include('**/*.story.tsx'),
'no gf-form usage': () =>
regexp(/gf-form/gm, 'gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.')
.include('**/*.{ts,tsx,html}')
.exclude(new RegExp('packages/grafana-ui/src/themes/GlobalStyles')),
};
function countSkippedA11yTestStories() {
return new BettererFileTest(async (filePaths, fileTestResult) => {
await Promise.all(
filePaths.map(async (filePath) => {
// look for skipped a11y tests
const skipRegex = new RegExp("a11y: { test: 'off' }", 'gm');
const fileText = await fs.readFile(filePath, 'utf8');
const hasSkip = skipRegex.test(fileText);
if (hasSkip) {
// In this case the file contents don't matter:
const file = fileTestResult.addFile(filePath, '');
// Add the issue to the first character of the file:
file.addIssue(0, 0, 'No skipping of a11y tests in stories. Please fix the component or story instead.');
}
})
);
});
}
function countUndocumentedStories() {
return new BettererFileTest(async (filePaths, fileTestResult) => {
await Promise.all(
filePaths.map(async (filePath) => {
// look for .mdx import in the story file
const mdxImportRegex = new RegExp("^import.*\\.mdx';$", 'gm');
// Looks for the "autodocs" string in the file
const autodocsStringRegex = /autodocs/;
const fileText = await fs.readFile(filePath, 'utf8');
const hasMdxImport = mdxImportRegex.test(fileText);
const hasAutodocsString = autodocsStringRegex.test(fileText);
// If both .mdx import and autodocs string are missing, add an issue
if (!hasMdxImport && !hasAutodocsString) {
// In this case the file contents don't matter:
const file = fileTestResult.addFile(filePath, '');
// Add the issue to the first character of the file:
file.addIssue(0, 0, 'No undocumented stories are allowed, please add an .mdx file with some documentation');
}
})
);
});
}
/**
* Generic regexp pattern matcher, similar to @betterer/regexp.
* The only difference is that the positions of the errors are not reported, as this may cause a lot of merge conflicts.
*/
function regexp(pattern: RegExp, issueMessage: string) {
return new BettererFileTest(async (filePaths, fileTestResult) => {
await Promise.all(
filePaths.map(async (filePath) => {
const fileText = await fs.readFile(filePath, 'utf8');
const matches = fileText.match(pattern);
if (matches) {
// File contents doesn't matter, since we're not reporting the position
const file = fileTestResult.addFile(filePath, '');
matches.forEach(() => {
file.addIssue(0, 0, issueMessage);
});
}
})
);
});
}
function countEslintErrors() {
return new BettererFileTest(async (filePaths, fileTestResult) => {
// Just bail early if there's no files to test. Prevents trying to get the base config from failing
if (filePaths.length === 0) {
return;
}
const runner = new ESLint({
overrideConfigFile: './.betterer.eslint.config.js',
warnIgnored: false,
});
const lintResults = await runner.lintFiles(Array.from(filePaths));
lintResults.forEach(({ messages, filePath }) => {
const file = fileTestResult.addFile(filePath, '');
messages
.sort((a, b) => (a.message > b.message ? 1 : -1))
.forEach((message, index) => {
file.addIssue(0, 0, message.message, `${index}`);
});
});
});
}

View File

@ -65,7 +65,7 @@ require (
github.com/go-toolsmith/astp v1.1.0 // indirect
github.com/go-toolsmith/strparse v1.1.0 // indirect
github.com/go-toolsmith/typep v1.1.0 // indirect
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gofrs/flock v0.12.1 // indirect

View File

@ -142,8 +142,8 @@ github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQi
github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ=
github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus=
github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY=
github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=

View File

@ -18,7 +18,7 @@ require (
github.com/evilmartians/lefthook v1.4.8 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect

View File

@ -29,8 +29,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=

View File

@ -24,7 +24,7 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37 // indirect
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/handlers v1.5.2 // indirect

View File

@ -41,8 +41,8 @@ github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3Bum
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37 h1:KFcZmKdZmapAog2+eL1buervAYrYolBZk7fMecPPDmo=
github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37/go.mod h1:i1/E+d8iPNReSE7y04FaVu5OPKB3il5cn+T1Egogg3I=
github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=

View File

@ -4,6 +4,7 @@
.gitignore
.vscode
bin
!bin/grafana-linux-k8s
data*
dist
docker

44
.github/CODEOWNERS vendored
View File

@ -33,6 +33,8 @@
# START Technical documentation
/.vale.ini @grafana/docs-tooling
/AGENTS.md @grafana/docs-tooling
# `make docs` procedure and related workflows are owned @grafana/docs-tooling. Slack #docs.
/docs/ @grafana/docs-tooling
@ -72,6 +74,7 @@
# Git Sync / App Platform Provisioning
/apps/provisioning/ @grafana/grafana-git-ui-sync-team
/pkg/operators @grafana/grafana-git-ui-sync-team
/public/app/features/provisioning @grafana/grafana-git-ui-sync-team
/pkg/registry/apis/provisioning @grafana/grafana-git-ui-sync-team
/pkg/tests/apis/provisioning @grafana/grafana-git-ui-sync-team
@ -85,10 +88,12 @@
/apps/preferences/ @grafana/grafana-app-platform-squad @grafana/grafana-frontend-platform
/apps/shorturl/ @grafana/sharing-squad
/apps/secret/ @grafana/grafana-operator-experience-squad
/apps/scope/ @grafana/grafana-operator-experience-squad
/apps/investigations/ @fcjack @matryer @svennergr
/apps/advisor/ @grafana/plugins-platform-backend
/apps/iam/ @grafana/access-squad
/apps/sdk.mk @grafana/grafana-app-platform-squad
/apps/correlations @grafana/datapro
/pkg/api/ @grafana/grafana-backend-group
/pkg/apis/ @grafana/grafana-app-platform-squad
/pkg/apis/query @grafana/grafana-datasources-core-services
@ -138,6 +143,7 @@
/pkg/apimachinery @grafana/grafana-app-platform-squad
/pkg/apimachinery/identity/ @grafana/identity-squad
/pkg/apimachinery/errutil/ @grafana/grafana-backend-group
/pkg/operators/iam @grafana/access-squad
/pkg/promlib @grafana/oss-big-tent
/pkg/storage/ @grafana/grafana-search-and-storage
/pkg/storage/secret/ @grafana/grafana-operator-experience-squad
@ -185,7 +191,7 @@
/pkg/setting/ @grafana/grafana-backend-services-squad
/pkg/tests/ @grafana/grafana-backend-services-squad
/pkg/tests/apis/ @grafana/grafana-app-platform-squad
/pkg/tests/apis/alerting @grafana/grafana-app-platform-squad @grafana/alerting-backend
/pkg/tests/apis/alerting @grafana/alerting-backend
/pkg/tests/apis/features @grafana/grafana-backend-services-squad
/pkg/tests/apis/folder @grafana/grafana-search-and-storage
/pkg/tests/apis/iam @grafana/identity-access-team
@ -307,6 +313,7 @@
/devenv/docker/blocks/webdav/ @grafana/alerting-backend
/devenv/docker/buildcontainer/ @bergquist
/devenv/docker/compose_header.yml @grafana/grafana-backend-services-squad
/devenv/docker/compose_volume_section.yml @grafana/grafana-backend-services-squad
/devenv/docker/debtest/ @bergquist
/devenv/docker/ha-test-unified-alerting/ @grafana/alerting-backend
/devenv/docker/ha_test/ @grafana/grafana-backend-services-squad
@ -409,12 +416,20 @@
/e2e/ @grafana/grafana-frontend-platform
/e2e-playwright/cloud-plugins-suite/ @grafana/partner-datasources
/e2e-playwright/dashboard-new-layouts/ @grafana/dashboards-squad
/e2e-playwright/dashboard-cujs/ @grafana/dashboards-squad
/e2e-playwright/dashboards-search-suite/ @grafana/dashboards-squad
/e2e-playwright/dashboards/cujs/ @grafana/dashboards-squad
/e2e-playwright/dashboards/DashboardForConditionalRendering.json @grafana/dashboards-squad
/e2e-playwright/dashboards/DashboardWithAllConditionalRendering.json @grafana/dashboards-squad
/e2e-playwright/dashboards/README.md @grafana/dashboards-squad
/e2e-playwright/dashboards/AdHocFilterTest.json @grafana/datapro
/e2e-playwright/dashboards/DashboardLiveTest.json @grafana/dashboards-squad
/e2e-playwright/dashboards/DataLinkWithoutSlugTest.json @grafana/dashboards-squad
/e2e-playwright/dashboards/PanelSandboxDashboard.json @grafana/plugins-platform-frontend
/e2e-playwright/dashboards/TestDashboard.json @grafana/dashboards-squad @grafana/grafana-search-navigate-organise
/e2e-playwright/dashboards/TestV2Dashboard.json @grafana/dashboards-squad
/e2e-playwright/dashboards/V2DashWithRepeats.json @grafana/dashboards-squad
/e2e-playwright/dashboards-suite/adhoc-filter-from-panel.spec.ts @grafana/datapro
/e2e-playwright/dashboards-suite/dashboard-browse-nested.spec.ts @grafana/grafana-search-navigate-organise
/e2e-playwright/dashboards-suite/dashboard-browse.spec.ts @grafana/grafana-search-navigate-organise
/e2e-playwright/dashboards-suite/dashboard-export-image.spec.ts @grafana/sharing-squad
@ -451,6 +466,8 @@
/e2e-playwright/fixtures/exemplars-query-response.json @grafana/observability-traces-and-profiling
/e2e-playwright/fixtures/long-trace-response.json @grafana/observability-traces-and-profiling
/e2e-playwright/fixtures/tempo-response.json @grafana/oss-big-tent
/e2e-playwright/fixtures/prometheus-response.json @grafana/datapro
/e2e-playwright/panels-suite/canvas-scene.spec.ts @grafana/dataviz-squad
/e2e-playwright/panels-suite/dashlist.spec.ts @grafana/grafana-search-navigate-organise
/e2e-playwright/panels-suite/datagrid-data-change.spec.ts @grafana/dataviz-squad
/e2e-playwright/panels-suite/datagrid-editing-features.spec.ts @grafana/dataviz-squad
@ -461,9 +478,11 @@
/e2e-playwright/panels-suite/panelEdit_base.spec.ts @grafana/dashboards-squad
/e2e-playwright/panels-suite/panelEdit_queries.spec.ts @grafana/dashboards-squad
/e2e-playwright/panels-suite/panelEdit_transforms.spec.ts @grafana/datapro
/e2e-playwright/panels-suite/table-footer.spec.ts @grafana/dataviz-squad
/e2e-playwright/panels-suite/table-kitchenSink.spec.ts @grafana/dataviz-squad
/e2e-playwright/panels-suite/table-markdown.spec.ts @grafana/dataviz-squad
/e2e-playwright/panels-suite/table-sparkline.spec.ts @grafana/dataviz-squad
/e2e-playwright/panels-suite/table-utils.ts @grafana/dataviz-squad
/e2e-playwright/plugin-e2e/ @grafana/oss-big-tent @grafana/partner-datasources
/e2e-playwright/plugin-e2e/plugin-e2e-api-tests/ @grafana/plugins-platform-frontend
/e2e-playwright/smoke-tests-suite/ @grafana/grafana-frontend-platform
@ -611,6 +630,7 @@
/packages/grafana-runtime/rollup.config.ts @grafana/grafana-frontend-platform
/packages/grafana-runtime/src/index.ts @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend
/packages/grafana-runtime/src/internal/index.ts @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend
/packages/grafana-runtime/src/internal/openFeature @grafana/grafana-frontend-platform
/packages/grafana-runtime/src/unstable.ts @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend
/packages/grafana-runtime/tsconfig.build.json @grafana/grafana-frontend-platform
/packages/grafana-runtime/tsconfig.json @grafana/grafana-frontend-platform
@ -727,7 +747,6 @@
/scripts/tsconfig.base.json @grafana/frontend-ops
/.editorconfig @grafana/frontend-ops
/eslint.config.js @grafana/frontend-ops
/.betterer.eslint.config.js @grafana/frontend-ops
/.gitattributes @grafana/frontend-ops
/.gitignore @grafana/frontend-ops
/.ignore @grafana/frontend-ops
@ -753,6 +772,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
# public folder
/public/app/api/ @grafana/grafana-frontend-platform
/public/app/api/clients/folder/ @grafana/grafana-search-navigate-organise
/public/app/core/actions/ @grafana/grafana-frontend-platform
/public/app/core/app_events.ts @grafana/grafana-frontend-platform
/public/app/core/components/AccessControl/ @grafana/identity-access-team
@ -857,6 +877,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/public/app/core/utils/accessControl.ts @grafana/identity-access-team
/public/app/core/utils/applyStateChanges.ts @grafana/dashboards-squad
/public/app/core/utils/arrayMove.ts @grafana/grafana-frontend-platform
/public/app/core/utils/isFrontendService.ts @grafana/grafana-frontend-platform
/public/app/core/utils/auth.ts @grafana/identity-access-team
/public/app/core/utils/browser* @grafana/grafana-frontend-platform
/public/app/core/utils/colors.ts @grafana/grafana-frontend-platform
@ -910,6 +931,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/public/app/features/inspector/ @grafana/dashboards-squad
/public/app/features/logs/ @grafana/observability-logs
/public/app/features/live/ @grafana/dashboards-squad
/public/app/features/stars/ @grafana/grafana-search-navigate-organise
/public/app/features/apiserver/ @grafana/grafana-app-platform-squad
/public/app/features/manage-dashboards/ @grafana/dashboards-squad
/public/app/features/migrate-to-cloud @grafana/grafana-operator-experience-squad
@ -934,6 +956,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/public/app/features/variables/ @grafana/dashboards-squad
/public/app/features/preferences/ @grafana/grafana-frontend-platform
/public/app/features/bookmarks/ @grafana/grafana-search-navigate-organise
/public/app/plugins/panel/* @grafana/dataviz-squad
/public/app/plugins/panel/alertlist/ @grafana/alerting-frontend
/public/app/plugins/panel/annolist/ @grafana/dashboards-squad
/public/app/plugins/panel/barchart/ @grafana/dataviz-squad
@ -946,7 +969,6 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/public/app/plugins/panel/heatmap/ @grafana/dataviz-squad
/public/app/plugins/panel/histogram/ @grafana/dataviz-squad
/public/app/plugins/panel/logs/ @grafana/observability-logs
/public/app/plugins/panel/logs-new/ @grafana/observability-logs
/public/app/plugins/panel/nodeGraph/ @grafana/observability-traces-and-profiling @grafana/app-o11y-visualizations
/public/app/plugins/panel/traces/ @grafana/observability-traces-and-profiling
/public/app/plugins/panel/flamegraph/ @grafana/observability-traces-and-profiling
@ -969,7 +991,6 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/public/app/routes/ @grafana/grafana-search-navigate-organise
/public/app/store/ @grafana/grafana-frontend-platform
/public/app/types/ @grafana/grafana-frontend-platform
/public/app/types/alerting.ts @grafana/alerting-frontend
/public/app/types/unified-alerting-dto.ts @grafana/alerting-frontend
/public/app/types/unified-alerting.ts @grafana/alerting-frontend
/public/dashboards/ @grafana/dashboards-squad
@ -1004,7 +1025,6 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/public/app/index.ts @grafana/frontend-ops
/public/app/initApp.ts @grafana/frontend-ops
/public/app/AppWrapper.tsx @grafana/frontend-ops
/public/app/partials/ @grafana/grafana-frontend-platform
/scripts/benchmark-access-control.sh @grafana/access-squad
/scripts/check-breaking-changes.sh @grafana/plugins-platform-frontend
@ -1012,7 +1032,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/scripts/circle-* @grafana/grafana-developer-enablement-squad
/scripts/publish-npm-packages.sh @grafana/grafana-developer-enablement-squad @grafana/plugins-platform-frontend
/scripts/validate-npm-packages.sh @grafana/grafana-developer-enablement-squad @grafana/plugins-platform-frontend
/scripts/ci-frontend-metrics.sh @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend @grafana/dataviz-squad @grafana/datapro
/scripts/ci-frontend-metrics.sh @grafana/grafana-frontend-platform @grafana/frontend-ops
/scripts/cli/ @grafana/grafana-frontend-platform
/scripts/clean-git-or-error.sh @grafana/grafana-as-code
/scripts/grafana-server/ @grafana/grafana-frontend-platform
@ -1041,8 +1061,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/scripts/**/generate-transformations* @grafana/datapro
/scripts/webpack/ @grafana/frontend-ops
/scripts/generate-a11y-report.sh @grafana/grafana-frontend-platform
.betterer.results @grafanabot
.betterer.ts @grafana/grafana-frontend-platform
eslint-suppressions.json @grafanabot
# Design system
/public/img/icons/unicons/ @grafana/design-system
@ -1130,6 +1149,8 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
# Feature toggles
/pkg/services/featuremgmt/ @grafana/grafana-backend-services-squad
# Data source migrations
/pkg/services/promtypemigration/ @grafana/partner-datasources @grafana/aws-datasources
# Kind definitions
/kinds/dashboard @grafana/dashboards-squad
@ -1235,6 +1256,7 @@ embed.go @grafana/grafana-as-code
/.github/workflows/i18n-verify.yml @grafana/grafana-frontend-platform
/.github/workflows/deploy-storybook-preview.yml @grafana/grafana-frontend-platform
/.github/workflows/scripts/crowdin/create-tasks.ts @grafana/grafana-frontend-platform
/.github/workflows/scripts/publish-frontend-metrics.mts @grafana/grafana-frontend-platform
/.github/workflows/pr-go-workspace-check.yml @grafana/grafana-app-platform-squad
/.github/workflows/pr-dependabot-update-go-workspace.yml @grafana/grafana-app-platform-squad
/.github/workflows/pr-k8s-codegen-check.yml @grafana/grafana-app-platform-squad
@ -1244,6 +1266,7 @@ embed.go @grafana/grafana-as-code
/.github/workflows/changelog.yml @zserge
/.github/workflows/shellcheck.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/release-build.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/cleanup-branches.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/publish-artifact.yml @grafana/grafana-developer-enablement-squad
/.github/actions/changelog @zserge
/.github/workflows/swagger-gen.yml @grafana/grafana-backend-group
@ -1253,9 +1276,14 @@ embed.go @grafana/grafana-as-code
/.github/workflows/pr-e2e-tests.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/skye-add-to-project.yml @grafana/grafana-frontend-platform
/.github/workflows/frontend-perf-tests.yaml @grafana/grafana-frontend-platform
/.github/workflows/release-npm.yml @grafana/grafana-frontend-platform
/.github/workflows/scripts/determine-npm-tag.sh @grafana/grafana-frontend-platform
/.github/workflows/scripts/validate-commit-in-head.sh @grafana/grafana-frontend-platform
/.github/zizmor.yml @grafana/grafana-developer-enablement-squad
/.github/license_finder.yaml @bergquist
/.github/actionlint.yaml @grafana/grafana-developer-enablement-squad
/.github/workflows/pr-test-docker.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/update-schema-types.yml @grafana/plugins-platform-frontend
# Generated files not requiring owner approval
/packages/grafana-data/src/types/featureToggles.gen.ts @grafanabot

View File

@ -139,6 +139,7 @@ runs:
with:
verb: run
dagger-flags: --verbose=0
version: 0.18.8
args: go run -C ${GRAFANA_PATH} ./pkg/build/cmd artifacts --artifacts ${ARTIFACTS} --grafana-dir=${GRAFANA_PATH} --alpine-base=${ALPINE_BASE} --ubuntu-base=${UBUNTU_BASE} --enterprise-dir=${ENTERPRISE_PATH} --version=${VERSION} --patches-repo=${PATCHES_REPO} --patches-ref=${PATCHES_REF} --patches-path=${PATCHES_PATH} --build-id=${BUILD_ID} --tag-format="${TAG_FORMAT}" --ubuntu-tag-format="${UBUNTU_TAG_FORMAT}" --org=${DOCKER_ORG} --registry=${DOCKER_REGISTRY} --checksum=${CHECKSUM} --verify=${VERIFY} > $OUTFILE
- id: output
shell: bash

View File

@ -28,6 +28,9 @@ outputs:
docs:
description: Whether the docs or self have changed in any way
value: ${{ steps.changed-files.outputs.docs_any_changed || 'true' }}
dockerfile:
description: Whether the dockerfile or self have changed in any way
value: ${{ steps.changed-files.outputs.dockerfile_any_changed || 'true' }}
runs:
using: composite
steps:
@ -42,6 +45,8 @@ runs:
self:
- '.github/actions/change-detection/**'
- '${{ inputs.self }}'
dockerfile:
- 'Dockerfile'
backend:
- '!*.md'
- '!docs/**'
@ -81,7 +86,6 @@ runs:
- '.github/actions/change-detection/**'
- '**.cue'
- '.prettier*'
- '.betterer*'
- '.yarnrc.yml'
- 'eslint.config.js'
- 'jest.config.js'
@ -151,3 +155,5 @@ runs:
echo " --> ${{ steps.changed-files.outputs.dev_tooling_all_changed_files }}"
echo "Docs: ${{ steps.changed-files.outputs.docs_any_changed || 'true' }}"
echo " --> ${{ steps.changed-files.outputs.docs_all_changed_files }}"
echo "Dockerfile: ${{ steps.changed-files.outputs.dockerfile_any_changed || 'true' }}"
echo " --> ${{ steps.changed-files.outputs.dockerfile_all_changed_files }}"

View File

@ -34,7 +34,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
with:
persist-credentials: false

View File

@ -10,7 +10,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 2
persist-credentials: false

View File

@ -17,7 +17,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4 # 4.2.2
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Check if update branch exists

View File

@ -12,7 +12,7 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false

View File

@ -23,7 +23,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@ -42,7 +42,7 @@ jobs:
name: Validate Backend Configs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
@ -58,6 +58,7 @@ jobs:
run: |
CODEGEN_VERIFY=1 make gen-cue
CODEGEN_VERIFY=1 make gen-jsonnet
CODEGEN_VERIFY=1 make gen-apps
- name: Validate go.mod
run: go run scripts/modowners/modowners.go check go.mod

View File

@ -24,7 +24,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@ -55,7 +55,7 @@ jobs:
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
@ -68,7 +68,7 @@ jobs:
run: |
set -euo pipefail
readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/shard.sh -N"$SHARD")"
go test -short -timeout=30m "${PACKAGES[@]}"
CGO_ENABLED=0 go test -short -timeout=30m "${PACKAGES[@]}"
grafana-enterprise:
# Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks)
@ -90,7 +90,7 @@ jobs:
steps:
# Set up repository clone
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
@ -118,7 +118,7 @@ jobs:
readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/shard.sh -N"$SHARD")"
# This tee requires pipefail to be set, otherwise `go test`'s exit code is thrown away.
# That means having no `-o pipefail` => failing tests => exit code 0, which is wrong.
go test -short -timeout=30m "${PACKAGES[@]}"
CGO_ENABLED=0 go test -short -timeout=30m "${PACKAGES[@]}"
# This is the job that is actually required by rulesets.
# We need to require EITHER the OSS or the Enterprise job to pass.

View File

@ -62,7 +62,7 @@ jobs:
echo "PR number: $PR_NUMBER"
- name: Checkout Grafana
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
ref: ${{ github.event.repository.default_branch }}
fetch-depth: 2

View File

@ -13,17 +13,29 @@ on:
required: false
permissions:
contents: write
pull-requests: write
id-token: write
contents: read
jobs:
bump-version:
runs-on: ubuntu-latest
steps:
- name: Checkout Grafana
uses: actions/checkout@v4
- uses: grafana/shared-workflows/actions/get-vault-secrets@main
with:
persist-credentials: false
repo_secrets: |
GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY
- name: Generate token
id: generate_token
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a
with:
app_id: ${{ vars.DELIVERY_BOT_APP_ID }}
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
repositories: '["grafana"]'
permissions: '{"contents": "write", "pull_requests": "write", "workflows": "write"}'
- name: Checkout Grafana
uses: actions/checkout@v5
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Update package.json versions
uses: ./pkg/build/actions/bump-version
with:
@ -35,10 +47,10 @@ jobs:
DRY_RUN: ${{ inputs.dry_run }}
REF_NAME: ${{ github.ref_name }}
RUN_ID: ${{ github.run_id }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
git config --local user.name "github-actions[bot]"
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "grafana-delivery-bot[bot]"
git config --local user.email "grafana-delivery-bot[bot]@users.noreply.github.com"
git config --local --add --bool push.autoSetupRemote true
git checkout -b "bump-version/${RUN_ID}/${VERSION}"
git add .

View File

@ -84,7 +84,7 @@ jobs:
app_id: ${{ vars.DELIVERY_BOT_APP_ID }}
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
- name: "Checkout Grafana repo"
uses: "actions/checkout@v4"
uses: "actions/checkout@v5"
with:
ref: main
sparse-checkout: |

18
.github/workflows/cleanup-branches.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: Clean up orphaned branches
on:
workflow_dispatch:
schedule:
- cron: "0 9 * * 1"
jobs:
cleanup-branches:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
steps:
- uses: actions/checkout@v5
- uses: grafana/shared-workflows/actions/cleanup-branches@cleanup-branches/v1.0.0
with:
dry-run: true
max-date: "1 month ago"

View File

@ -13,7 +13,7 @@ jobs:
contents: read
steps:
# Checks-out your repository, which is validated in the next step
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: GitHub CODEOWNERS Validator

View File

@ -33,7 +33,7 @@ jobs:
go: ${{ steps.detect-changes.outputs.backend }}
actions: 'true'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@ -63,7 +63,7 @@ jobs:
steps:
- name: Checkout repository
if: needs.detect-changes.outputs[matrix.language] == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.

View File

@ -53,7 +53,7 @@ jobs:
private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }}
- name: Checkout Actions
uses: actions/checkout@v4 # v4.2.2
uses: actions/checkout@v5
with:
repository: "grafana/grafana-github-actions"
path: ./actions

View File

@ -43,7 +43,7 @@ jobs:
version: ${{ steps.build_frontend.outputs.version }}
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Verify inputs
@ -62,7 +62,7 @@ jobs:
with:
credentials_json: '${{ env.PLUGINS_GOOGLE_CREDENTIALS }}'
- name: 'Set up Cloud SDK'
uses: 'google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a'
uses: 'google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db'
- name: Setup nodejs environment
uses: actions/setup-node@v4
with:

View File

@ -61,7 +61,7 @@ jobs:
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
token: ${{ steps.generate_token.outputs.token }}
repository: ${{ inputs.repository }}

View File

@ -27,7 +27,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false

View File

@ -29,7 +29,7 @@ jobs:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
path: './pr'
persist-credentials: false
@ -80,7 +80,7 @@ jobs:
working-directory: './base'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
path: './base'
ref: ${{ github.event.pull_request.base.ref }}
@ -132,7 +132,7 @@ jobs:
id-token: 'write'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
@ -165,7 +165,7 @@ jobs:
project_id: 'grafanalabs-global'
- name: 'Set up Cloud SDK'
uses: 'google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a'
uses: 'google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db'
if: github.event.pull_request.head.repo.full_name == github.repository
with:
version: '>= 363.0.0'
@ -220,7 +220,7 @@ jobs:
app-id: ${{ env.GITHUB_APP_ID }}
private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }}
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false

View File

@ -29,7 +29,7 @@ jobs:
if: github.event.pull_request.head.repo.full_name == github.repository
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false

View File

@ -17,7 +17,7 @@ jobs:
container:
image: grafana/vale:latest # zizmor: ignore[unpinned-images]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: grafana/writers-toolkit/vale-action@vale-action/v1 # zizmor: ignore[unpinned-uses]

View File

@ -10,7 +10,7 @@ permissions: {}
jobs:
handle-ephemeral-instances:
if: ${{ github.event.issue.pull_request && (startsWith(github.event.comment.body, '/deploy-to-hg') || github.event.action == 'closed') && github.repository_owner == 'grafana' }}
if: ${{ github.repository_owner == 'grafana' && ((github.event.issue.pull_request && startsWith(github.event.comment.body, '/deploy-to-hg')) || github.event.action == 'closed') }}
runs-on:
labels: ubuntu-x64-xlarge
continue-on-error: true
@ -42,7 +42,7 @@ jobs:
private_key: ${{ env.APP_PEM }}
- name: Checkout ephemeral instances repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
with:
repository: grafana/ephemeral-grafana-instances-github-action
token: ${{ steps.generate_token.outputs.token }}

View File

@ -11,13 +11,15 @@ permissions: {}
jobs:
test:
name: Feature toggles documentation is in sync with source
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false

View File

@ -18,7 +18,7 @@ jobs:
changed: ${{ steps.detect-changes.outputs.frontend }}
prettier: ${{ steps.detect-changes.outputs.frontend == 'true' || steps.detect-changes.outputs.docs == 'true' }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@ -39,7 +39,7 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4
@ -60,7 +60,7 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4
@ -86,7 +86,7 @@ jobs:
name: Typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4
@ -106,7 +106,7 @@ jobs:
name: Typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4
@ -120,25 +120,6 @@ jobs:
github-app-name: 'grafana-ci-bot'
- run: yarn install --immutable --check-cache
- run: yarn run typecheck
lint-frontend-betterer:
needs: detect-changes
permissions:
contents: read
id-token: write
if: needs.detect-changes.outputs.changed == 'true'
name: Betterer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
cache-dependency-path: 'yarn.lock'
- run: yarn install --immutable --check-cache
- run: yarn run betterer:ci
lint-frontend-api-clients:
permissions:
contents: read
@ -149,7 +130,7 @@ jobs:
name: Verify API clients
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4
@ -180,7 +161,7 @@ jobs:
name: Verify API clients (enterprise)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4

View File

@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@ -82,7 +82,7 @@ jobs:
-e PROMETHEUS_URL="$PROMETHEUS_URL" \
-e PROMETHEUS_USER="$PROMETHEUS_USER" \
-e PROMETHEUS_PASSWORD="$PROMETHEUS_TOKEN" \
us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench:v0.6.0 report \
us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench:v0.6.1 report \
--grafana-url "http://fsperfbaseline.grafana-dev.net" \
--test-suite-name "FrontendPerfTests" \
--report-input playwright \
@ -104,7 +104,7 @@ jobs:
-e PROMETHEUS_URL="$PROMETHEUS_URL" \
-e PROMETHEUS_USER="$PROMETHEUS_USER" \
-e PROMETHEUS_PASSWORD="$PROMETHEUS_TOKEN" \
us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench:v0.6.0 report \
us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench:v0.6.1 report \
--grafana-url "http://fsperf.grafana-dev.net" \
--test-suite-name "FrontendPerfTests" \
--report-input playwright \

View File

@ -23,7 +23,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@ -33,12 +33,33 @@ jobs:
with:
self: .github/workflows/go-lint.yml
go-fmt:
needs: detect-changes
if: needs.detect-changes.outputs.changed == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-go@v5.5.0
with:
go-version-file: ./go.mod
- name: Run gofmt
run: |
GOFMT="$(go list -m -f '{{.Dir}}' | xargs -I{} sh -c 'test ! -f {}/.nolint && echo {}' | xargs gofmt -s -e -l 2>&1 | grep -v '/pkg/build/' || true)"
if [ -n "$GOFMT" ]; then
echo "Found files that are not gofmt'ed"
echo "Run 'gofmt -s -w .' or 'make gofmt' or install the pre-commit hook with 'make lefthook-install'"
echo "${GOFMT}"
exit 1
fi
lint-go:
needs: detect-changes
if: needs.detect-changes.outputs.changed == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-go@v5.5.0

View File

@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout Actions
uses: actions/checkout@v4 # v4.2.2
uses: actions/checkout@v5
with:
repository: "grafana/grafana-github-actions"
path: ./actions
@ -97,7 +97,7 @@ jobs:
ACTOR: ${{ github.actor }}
- name: Checkout
if: steps.check-if-grafana-org-member.outputs.is_grafana_org_member != 'true' && github.event.issue.author_association != 'MEMBER' && github.event.issue.author_association != 'OWNER'
uses: actions/checkout@v4 # v4.2.2
uses: actions/checkout@v5
with:
persist-credentials: false
sparse-checkout: |

View File

@ -30,7 +30,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false

View File

@ -31,7 +31,7 @@ jobs:
if: github.event.pull_request.draft == false
steps:
- name: Checkout Actions
uses: actions/checkout@v4 # v4.2.2
uses: actions/checkout@v5
with:
repository: "grafana/grafana-github-actions"
path: ./actions

View File

@ -20,7 +20,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.

View File

@ -18,7 +18,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.

View File

@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Actions
uses: actions/checkout@v4 # v4.2.2
uses: actions/checkout@v5
with:
repository: "grafana/grafana-github-actions"
path: ./actions

View File

@ -37,7 +37,7 @@ jobs:
private-key: ${{ env.PRIVATE_KEY }}
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}

View File

@ -30,7 +30,7 @@ jobs:
changed: ${{ steps.detect-changes.outputs.e2e }}
cloud_plugins_changed: ${{ steps.detect-changes.outputs.e2e-cloud-plugins }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@ -48,7 +48,7 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
@ -67,6 +67,7 @@ jobs:
if: steps.cache.outputs.cache-hit != 'true'
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 -a docker:grafana:linux/amd64 --grafana-dir="${PWD}" > out.txt
- name: Cat built artifact
@ -117,7 +118,7 @@ jobs:
outputs:
artifact: ${{ steps.artifact.outputs.artifact }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
@ -148,19 +149,11 @@ jobs:
needs:
- build-grafana
steps:
- id: vault-secrets
uses: grafana/shared-workflows/actions/get-vault-secrets@main
- id: get-github-token
name: "create github app token"
uses: grafana/shared-workflows/actions/create-github-app-token@eb02241ed0a92aff205feab8ac3afcdf51c757c8 # create-github-app-token-v0.2.0
with:
repo_secrets: |
GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY
- name: Generate token
id: generate_token
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a
with:
app_id: ${{ vars.DELIVERY_BOT_APP_ID }}
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
repositories: '["grafana"]'
permissions: '{"checks": "write"}'
github_app: "delivery-bot-app"
- uses: grafana/shared-workflows/actions/login-to-gar@main
id: login-to-gar
with:
@ -183,7 +176,7 @@ jobs:
echo "IMAGE=${DOCKER_IMAGE}" >> "$GITHUB_ENV"
- name: Add PR status check
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
GH_TOKEN: ${{ steps.get-github-token.outputs.token }}
SHA: ${{ github.event.pull_request.head.sha }}
run: |
gh api \
@ -207,8 +200,6 @@ jobs:
fail-fast: false
matrix:
include:
- suite: various-suite
path: e2e/various-suite
- suite: various-suite (old arch)
path: e2e/old-arch/various-suite
flags: --flags="--env dashboardScene=false"
@ -227,7 +218,7 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/download-artifact@v4
@ -241,6 +232,7 @@ jobs:
- name: Run E2E tests
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/e2e --package=grafana.tar.gz
--suite=${{ matrix.path }}
@ -270,7 +262,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@ -303,7 +295,7 @@ jobs:
shardTotal: [8]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/download-artifact@v4
@ -312,6 +304,7 @@ jobs:
- name: Run E2E tests
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/e2e-playwright --package=grafana.tar.gz --shard=${{ matrix.shard }}/${{ matrix.shardTotal }} --blob-dir=./blob-report
- uses: actions/upload-artifact@v4
@ -322,7 +315,7 @@ jobs:
retention-days: 1
run-azure-monitor-e2e:
if: needs.detect-changes.outputs.cloud_plugins_changed == 'true' && github.event.pull_request.head.repo.fork == false
if: needs.detect-changes.outputs.cloud_plugins_changed == 'true' && github.event.pull_request.head.repo.fork == false && github.event_name == 'pull_request'
runs-on: ubuntu-x64-large
needs:
- build-grafana
@ -331,7 +324,7 @@ jobs:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
@ -374,6 +367,7 @@ jobs:
- name: Run E2E tests
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/e2e-playwright --package=grafana.tar.gz --playwright-command="yarn e2e:playwright:cloud-plugins" --cloud-plugin-creds=/tmp/outputs.json
@ -397,7 +391,7 @@ jobs:
name: All Playwright tests complete
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
@ -482,7 +476,7 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/download-artifact@v4
@ -492,14 +486,58 @@ jobs:
if: github.event_name == 'pull_request'
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/a11y --package=grafana.tar.gz
- name: Run non-PR a11y test
if: github.event_name != 'pull_request'
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/a11y --package=grafana.tar.gz --no-threshold-fail
args: go run ./pkg/build/a11y --package=grafana.tar.gz --no-threshold-fail --results=./pa11y-ci-results.json
- name: Upload pa11y results
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v4
with:
retention-days: 1
name: pa11y-ci-results
path: pa11y-ci-results.json
publish-metrics:
needs:
- run-a11y-test
name: Publish metrics
# Run on `grafana/grafana` main branch only
if: github.event_name == 'push' && github.repository == 'grafana/grafana' && github.ref_name == 'main'
permissions:
contents: read
id-token: write
runs-on: ubuntu-latest
steps:
- id: vault-secrets
uses: grafana/shared-workflows/actions/get-vault-secrets@main
with:
repo_secrets: |
GRAFANA_MISC_STATS_API_KEY=grafana-misc-stats:api_key
- name: Checkout code
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- name: Install dependencies
run: yarn install --immutable
- name: Get pa11y results
uses: actions/download-artifact@v4
with:
name: pa11y-ci-results
- name: Extract and publish metrics
run: ./scripts/ci-frontend-metrics.sh | node --experimental-strip-types .github/workflows/scripts/publish-frontend-metrics.mts
env:
GRAFANA_MISC_STATS_API_KEY: ${{ env.GRAFANA_MISC_STATS_API_KEY}}
# This is the job that is actually required by rulesets.
# We want to only require one job instead of all the individual tests.
@ -518,7 +556,7 @@ jobs:
name: All E2E tests complete
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false

View File

@ -19,7 +19,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.frontend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@ -46,7 +46,7 @@ jobs:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
total: [16]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Node.js
@ -78,7 +78,7 @@ jobs:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
total: [16]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Enterprise
@ -107,7 +107,7 @@ jobs:
runs-on: ubuntu-x64-large
name: "Decoupled plugin tests"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Node.js
@ -128,7 +128,7 @@ jobs:
runs-on: ubuntu-x64-large
name: "Packages unit tests"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Node.js
@ -160,7 +160,7 @@ jobs:
name: All frontend unit tests complete
runs-on: ubuntu-x64-small
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Check test suites

View File

@ -16,7 +16,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@ -34,7 +34,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@ -71,7 +71,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@ -123,7 +123,7 @@ jobs:
echo "No changes in generated Go files"
else
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "false" ]]; then
echo "> !!! Please link Enterprise and run 'make gen-go', then commit the changes to both repositories."
echo "> !!! Please synchronize the grafana OSS and grafana enterprise code bases as defined in the enterprise readme, then run 'make gen-go' in the OSS folder and commit the changes to both repositories."
else
echo "> !!! Please run 'make gen-go' and commit the changes."
fi

View File

@ -19,7 +19,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false

39
.github/workflows/pr-test-docker.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: Test Dockerfile
on:
pull_request:
jobs:
detect-changes:
# Run on `grafana/grafana` main branch, or on pull requests to prevent double-running on mirrors
name: Detect whether code changed
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
changed: ${{ steps.detect-changes.outputs.backend || steps.detect-changes.outputs.frontend || steps.detect-changes.outputs.dockerfile }}
steps:
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
- name: Detect changes
id: detect-changes
uses: ./.github/actions/change-detection
with:
self: .github/workflows/pr-test-integration.yml
build-dockerfile:
needs: detect-changes
if: needs.detect-changes.outputs.changed == 'true'
runs-on: ubuntu-x64-large-io
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
persist-credentials: false
- uses: docker/setup-docker-action@b60f85385d03ac8acfca6d9996982511d8620a19 # v4
- name: Build Dockerfile
run: make build-docker-full

View File

@ -28,7 +28,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@ -37,7 +37,6 @@ jobs:
uses: ./.github/actions/change-detection
with:
self: .github/workflows/pr-test-integration.yml
sqlite:
needs: detect-changes
if: needs.detect-changes.outputs.changed == 'true'
@ -55,7 +54,7 @@ jobs:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
@ -70,6 +69,39 @@ jobs:
set -euo pipefail
readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -N"$SHARD" -d-)"
go test -tags=sqlite -timeout=8m -run '^TestIntegration' "${PACKAGES[@]}"
sqlite_nocgo:
needs: detect-changes
if: needs.detect-changes.outputs.changed == 'true'
strategy:
matrix:
# We don't need more than this since it has to wait for the other tests.
shard: [
1/4, 2/4, 3/4, 4/4,
]
fail-fast: false
name: Sqlite Without CGo (${{ matrix.shard }})
runs-on: ubuntu-x64-large-io
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
uses: actions/setup-go@v5.5.0
with:
go-version-file: go.mod
cache: true
- name: Run tests
env:
SHARD: ${{ matrix.shard }}
run: |
set -euo pipefail
readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -N"$SHARD" -d-)"
CGO_ENABLED=0 go test -tags=sqlite -timeout=8m -run '^TestIntegration' "${PACKAGES[@]}"
mysql:
needs: detect-changes
if: needs.detect-changes.outputs.changed == 'true'
@ -103,7 +135,7 @@ jobs:
- 3306:3306
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
@ -152,7 +184,7 @@ jobs:
- 5432:5432
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go

View File

@ -33,9 +33,13 @@ on:
type: string
required: false
default: github-prerelease-writer@grafanalabs-workload-identity.iam.gserviceaccount.com
runs-on:
type: string
required: false
default: github-hosted-ubuntu-x64-small
jobs:
publish:
runs-on: github-hosted-ubuntu-x64-small
runs-on: ${{ inputs.runs-on }}
name: Publish
permissions:
id-token: write

View File

@ -21,7 +21,7 @@ jobs:
steps:
- name: "Checkout Grafana repo"
uses: "actions/checkout@v4"
uses: "actions/checkout@v5"
with:
fetch-depth: 0
persist-credentials: false

View File

@ -23,7 +23,7 @@ jobs:
steps:
- name: "Checkout Grafana repo"
uses: "actions/checkout@v4"
uses: "actions/checkout@v5"
with:
# required for the `grafana/grafana-github-actions/has-matching-release-tag` action to work
fetch-depth: 0
@ -38,7 +38,7 @@ jobs:
run: go run .github/workflows/scripts/kinds/verify-kinds.go
- name: "Checkout Actions library"
uses: "actions/checkout@v4"
uses: "actions/checkout@v5"
with:
repository: "grafana/grafana-github-actions"
path: "./actions"

View File

@ -15,7 +15,7 @@ jobs:
id-token: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: grafana/writers-toolkit/publish-technical-documentation@publish-technical-documentation/v1 # zizmor: ignore[unpinned-uses]

View File

@ -17,7 +17,7 @@ jobs:
id-token: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false

View File

@ -20,7 +20,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
with:
persist-credentials: false

View File

@ -56,7 +56,7 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Set up version (Release Branches)
@ -140,7 +140,7 @@ jobs:
# The downside to this is that the frontend will be built for each one when it could be reused for all of them.
# This could be a future improvement.
include:
- name: linux-amd64
- name: linux-amd64 # publish-npm relies on this step building npm packages
artifacts: targz:grafana:linux/amd64,deb:grafana:linux/amd64,rpm:grafana:linux/amd64,docker:grafana:linux/amd64,docker:grafana:linux/amd64:ubuntu,npm:grafana,storybook
verify: true
- name: linux-arm64
@ -169,7 +169,7 @@ jobs:
verify: true
steps:
- uses: grafana/shared-workflows/actions/dockerhub-login@dockerhub-login/v1.0.2
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Set up QEMU
@ -197,6 +197,7 @@ jobs:
name: artifacts-${{ matrix.name }}
path: ${{ steps.build.outputs.dist-dir }}
retention-days: 1
publish-artifacts:
name: Upload artifacts
uses: grafana/grafana/.github/workflows/publish-artifact.yml@main
@ -211,6 +212,7 @@ jobs:
run-id: ${{ github.run_id }}
bucket-path: ${{ needs.setup.outputs.version }}_${{ github.run_id }}
environment: prod
publish-dockerhub:
if: github.ref_name == 'main'
permissions:
@ -268,3 +270,77 @@ jobs:
docker manifest push grafana/grafana:main-ubuntu
docker manifest push "grafana/grafana-dev:${VERSION}"
docker manifest push "grafana/grafana-dev:${VERSION}-ubuntu"
dispatch-npm-canaries:
if: github.ref_name == 'main'
name: Dispatch publish NPM canaries
permissions:
actions: write
contents: read
runs-on: ubuntu-x64-small
needs:
- setup
steps:
- name: Dispatch action
env:
GRAFANA_COMMIT: ${{ needs.setup.outputs.grafana-commit }}
VERSION: ${{ needs.setup.outputs.version }}
BUILD_ID: ${{ github.run_id }}
GH_TOKEN: ${{ github.token }}
run: |
gh workflow run release-npm.yml \
--repo grafana/grafana \
--ref main \
--field grafana_commit="$GRAFANA_COMMIT" \
--field version="$VERSION" \
--field build_id="$BUILD_ID"\
--field version_type="canary"
# notify-pr creates (or updates) a comment in a pull request to link to this workflow where the release artifacts are
# being built.
notify-pr:
runs-on: ubuntu-x64-small
permissions:
contents: read
id-token: write
needs:
- setup
steps:
- id: vault-secrets
uses: grafana/shared-workflows/actions/get-vault-secrets@main
with:
repo_secrets: |
GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY
- name: Generate token
id: generate_token
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a
with:
app_id: ${{ vars.DELIVERY_BOT_APP_ID }}
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
repositories: '["grafana"]'
permissions: '{"issues": "write", "pull_requests": "write", "contents": "read"}'
- name: Find PR
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
GRAFANA_COMMIT: ${{ needs.setup.outputs.grafana-commit }}
run: echo "ISSUE_NUMBER=$(gh api "/repos/grafana/grafana/commits/${GRAFANA_COMMIT}/pulls" | jq -r '.[0].number')" >> "$GITHUB_ENV"
- name: Find Comment
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3
id: fc
with:
issue-number: ${{ env.ISSUE_NUMBER }}
comment-author: 'grafana-delivery-bot[bot]'
body-includes: GitHub Actions Build
token: ${{ steps.generate_token.outputs.token }}
- name: Create or update comment
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
token: ${{ steps.generate_token.outputs.token }}
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ env.ISSUE_NUMBER }}
body: |
:rocket: Your submission is now being built and packaged.
- [GitHub Actions Build](https://github.com/grafana/grafana/actions/runs/${{ github.run_id }})
- Version: ${{ needs.setup.outputs.version }}
edit-mode: replace

134
.github/workflows/release-npm.yml vendored Normal file
View File

@ -0,0 +1,134 @@
name: Release NPM packages
run-name: Publish NPM ${{ inputs.version_type }} ${{ inputs.version }}
on:
workflow_call:
inputs:
grafana_commit:
description: 'Grafana commit SHA to build against'
required: true
type: string
version:
description: 'Version to publish as'
required: true
type: string
build_id:
description: 'Run ID from the original release-build workflow'
required: true
type: string
version_type:
description: 'Version type (canary, nightly, stable)'
required: true
type: string
workflow_dispatch:
inputs:
grafana_commit:
description: 'Grafana commit SHA to build against'
required: true
version:
description: 'Version to publish as'
required: true
build_id:
description: 'Run ID from the original release-build workflow'
required: true
version_type:
description: 'Version type (canary, nightly, stable)'
required: true
permissions: {}
jobs:
# If called with version_type 'canary' or 'stable', build + publish to NPM
# If called with version_type 'nightly', do nothing (we're not yet tagging them with the nightly tag)
publish:
name: Publish NPM packages
runs-on: github-hosted-ubuntu-x64-small
if: inputs.version_type == 'canary' || inputs.version_type == 'stable'
# Required for this workflow to have permission to publish NPM packages
environment: npm-publish
permissions:
contents: read
id-token: write
steps:
- name: Info
env:
GITHUB_REF: ${{ github.ref }}
GRAFANA_COMMIT: ${{ inputs.grafana_commit }}
run: |
echo "GRAFANA_COMMIT: $GRAFANA_COMMIT"
echo "github.ref: $GITHUB_REF"
- name: Checkout workflow ref
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 100
fetch-tags: false
# this will fail with "{commit} is not a valid commit" if the commit is valid but
# not in the last 100 commits.
- name: Verify commit is in workflow HEAD
env:
GIT_COMMIT: ${{ inputs.grafana_commit }}
run: ./.github/workflows/scripts/validate-commit-in-head.sh
shell: bash
- name: Map version type to NPM tag
id: npm-tag
env:
VERSION: ${{ inputs.version }}
VERSION_TYPE: ${{ inputs.version_type }}
REFERENCE_PKG: "@grafana/runtime"
run: |
TAG=$(./.github/workflows/scripts/determine-npm-tag.sh)
echo "NPM_TAG=$TAG" >> "$GITHUB_OUTPUT"
shell: bash
- name: Checkout build commit
uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{ inputs.grafana_commit }}
- name: Setup Node
uses: ./.github/actions/setup-node
# Trusted Publishing is only available in npm v11.5.1 and later
- name: Update npm
run: npm install -g npm@^11.5.1
- name: Install dependencies
run: yarn install --immutable
- name: Typecheck packages
run: yarn run packages:typecheck
- name: Version, build, and pack packages
env:
VERSION: ${{ inputs.version }}
run: |
yarn run packages:build
yarn lerna version "$VERSION" \
--exact \
--no-git-tag-version \
--no-push \
--force-publish \
--yes
yarn run packages:pack
- name: Debug packed files
run: tree -a ./npm-artifacts
- name: Validate packages
run: ./scripts/validate-npm-packages.sh
- name: Debug OIDC Claims
uses: github/actions-oidc-debugger@2e9ba5d3f4bebaad1f91a2cede055115738b7ae8
with:
audience: '${{ github.server_url }}/${{ github.repository_owner }}'
- name: Publish packages
env:
NPM_TAG: ${{ steps.npm-tag.outputs.NPM_TAG }}
run: ./scripts/publish-npm-packages.sh --dist-tag "$NPM_TAG" --registry 'https://registry.npmjs.org/'

View File

@ -118,14 +118,14 @@ jobs:
permissions: "{\"contents\": \"write\", \"pull_requests\": \"write\", \"workflows\":\"write\"}"
- run: echo "RELEASE_BRANCH=release-${VERSION}" >> "$GITHUB_ENV"
- name: Checkout Grafana
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
token: ${{ steps.generate_changelog_token.outputs.token }}
ref: ${{ env.RELEASE_BRANCH }}
fetch-tags: true
fetch-depth: 0
- name: Checkout Grafana (main)
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
token: ${{ steps.generate_changelog_token.outputs.token }}
ref: main
@ -198,6 +198,7 @@ jobs:
if: ${{ inputs.bump == true || inputs.bump == 'true' }}
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run -C .grafana-main ./pkg/build/actions/bump-version -version="patch"

View File

@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false

View File

@ -25,7 +25,7 @@ jobs:
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Pin Go version to mod file
@ -96,7 +96,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Restore Cached Node Modules

View File

@ -18,7 +18,7 @@ jobs:
if: github.event.pull_request.draft == false
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Pin Go version to mod file

View File

@ -0,0 +1,66 @@
#!/usr/bin/env bash
set -euo pipefail
fail() { echo "Error: $*" >&2; exit 1; }
# Ensure required variables are set
if [[ -z "${REFERENCE_PKG}" || -z "${VERSION_TYPE}" || -z "${VERSION}" ]]; then
fail "Missing required environment variables: REFERENCE_PKG, VERSION_TYPE, VERSION"
fi
semver_cmp () {
IFS='.' read -r -a arr_a <<< "$1"
IFS='.' read -r -a arr_b <<< "$2"
for i in 0 1 2; do
local aa=${arr_a[i]:-0}
local bb=${arr_b[i]:-0}
# shellcheck disable=SC2004
if (( 10#$aa > 10#$bb )); then echo gt; return 0; fi
if (( 10#$aa < 10#$bb )); then echo lt; return 0; fi
done
echo "eq"
}
STABLE_REGEX='^([0-9]+)\.([0-9]+)\.([0-9]+)$' # x.y.z
PRE_REGEX='^([0-9]+)\.([0-9]+)\.([0-9]+)-([0-9]+)$' # x.y.z-123456
# Validate that the VERSION matches VERSION_TYPE
# - stable must be x.y.z
# - nightly/canary must be x.y.z-123456
case "$VERSION_TYPE" in
stable)
[[ $VERSION =~ $STABLE_REGEX ]] || fail "For 'stable', version must match x.y.z" ;;
nightly|canary)
[[ $VERSION =~ $PRE_REGEX ]] || fail "For '$VERSION_TYPE', version must match x.y.z-123456" ;;
*)
fail "Unknown version_type '$VERSION_TYPE'" ;;
esac
# Extract major, minor from VERSION
IFS=.- read -r major minor patch _ <<< "$VERSION"
# Determine NPM tag
case "$VERSION_TYPE" in
canary) TAG="canary" ;;
nightly) TAG="nightly" ;;
stable)
# Use npm dist-tag "latest" as the reference
LATEST="$(npm view --silent "$REFERENCE_PKG" dist-tags.latest 2>/dev/null || true)"
echo "Latest for $REFERENCE_PKG is ${LATEST:-<none>}" >&2
if [[ -z ${LATEST:-} ]]; then
TAG="latest" # first ever publish
else
case "$(semver_cmp "$VERSION" "$LATEST")" in
gt) TAG="latest" ;; # newer than reference -> latest
lt|eq) TAG="v${major}.${minor}-latest" ;; # older or equal -> vX.Y-latest
esac
fi
;;
esac
echo "Resolved NPM_TAG=$TAG (VERSION=$VERSION, current latest=${LATEST:-none})" 1>&2 # stderr
printf '%s' "$TAG"

View File

@ -0,0 +1,75 @@
import fs from 'node:fs'
interface Payload {
name: string;
value: number;
interval: number;
mtype: string;
time: number;
}
console.log("Publishing metrics");
// Get API key from environment variable
const key = process.env.GRAFANA_MISC_STATS_API_KEY;
if (!key) {
throw new Error("API key is required. Provide it via the GRAFANA_MISC_STATS_API_KEY environment variable");
}
const unixTimestamp = Math.floor(Date.now() / 1000);
const data: Payload[] = [];
const input = fs.readFileSync(0, "utf-8");
// parse metrics from input
const regexp = /^Metrics: (\{.+\})/ms;
const matches = input.match(regexp);
if (!matches) {
throw new Error("No metrics found");
}
console.log('matches[0]', matches[0])
console.log('matches[1]', matches[1])
const metrics: Record<string, string> = JSON.parse(matches[1]);
// Convert metrics to payload format
for (const [metricName, valueStr] of Object.entries(metrics)) {
const value = parseInt(valueStr, 10);
if (isNaN(value)) {
throw new Error(`Metric "${metricName}" has invalid value format: "${valueStr}"`);
}
data.push({
name: metricName,
value: value,
interval: 60,
mtype: "gauge",
time: unixTimestamp,
});
}
const jsonPayload = JSON.stringify(data);
console.log(`Publishing metrics to https://graphite-us-central1.grafana.net/metrics, JSON: ${jsonPayload}`);
const url = 'https://graphite-us-central1.grafana.net/metrics';
const username = '6371';
const headers = new Headers();
headers.set("Content-Type", "application/json");
headers.set('Authorization', 'Basic ' + Buffer.from(username + ":" + key).toString('base64'));
try {
const response = await fetch(url, {
method: "POST",
headers,
body: jsonPayload,
});
if (!response.ok) {
throw new Error(`Metrics publishing failed with status code ${response.status}`);
}
console.log("Metrics successfully published");
} catch (error) {
throw new Error(`Metrics publishing failed: ${error instanceof Error ? error.message : String(error)}`);
}

View File

@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -z "${GIT_COMMIT:-}" ]]; then
echo "Error: Environment variable GIT_COMMIT is required"
exit 1
fi
if git merge-base --is-ancestor "$GIT_COMMIT" HEAD; then
echo "Commit $GIT_COMMIT is contained in HEAD"
else
echo "Error: Commit $GIT_COMMIT is not contained in HEAD"
exit 1
fi

View File

@ -24,7 +24,7 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Run Shellcheck

View File

@ -17,7 +17,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.frontend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@ -27,7 +27,7 @@ jobs:
with:
self: .github/workflows/storybook-a11y.yml
test-storybook-a11y:
test-storybook-a11y-light:
runs-on: ubuntu-latest-8-cores
permissions:
contents: read
@ -36,17 +36,48 @@ jobs:
if: needs.detect-changes.outputs.changed == 'true'
name: "Run Storybook a11y tests"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with:
node-version-file: '.nvmrc'
package-manager-cache: false # too large for GH's cache limits :-(
- run: yarn install --immutable --check-cache
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Start Storybook
run: yarn storybook &
run: STORYBOOK_THEME=light yarn storybook &
- name: Run tests
# the chromium browser used by Playwright sets its locale to "en_US@posix" by default
# this is not a valid language code, and causes some stories to fail to load!
# instead, we set the LANG environment variable to en_US to override this
# see https://github.com/microsoft/playwright/issues/34046
env:
LANG: en_US
run: npx wait-on --timeout 120000 http://localhost:9001 && yarn test:storybook
test-storybook-a11y-dark:
runs-on: ubuntu-latest-8-cores
permissions:
contents: read
id-token: write
needs: detect-changes
if: needs.detect-changes.outputs.changed == 'true'
name: "Run Storybook a11y tests"
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v5
with:
node-version-file: '.nvmrc'
package-manager-cache: false # too large for GH's cache limits :-(
- run: yarn install --immutable --check-cache
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Start Storybook
run: STORYBOOK_THEME=dark yarn storybook &
- name: Run tests
# the chromium browser used by Playwright sets its locale to "en_US@posix" by default
# this is not a valid language code, and causes some stories to fail to load!

View File

@ -24,7 +24,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@ -45,7 +45,7 @@ jobs:
steps:
# Set up repository clone
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go

View File

@ -16,7 +16,7 @@ jobs:
trivy-scan:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Install Trivy

View File

@ -26,7 +26,7 @@ jobs:
shell: bash
run: echo "fetch_depth=$(( ${{ github.event.pull_request.commits }} + 2 ))" >> "$GITHUB_OUTPUT"
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
fetch-depth: ${{ steps.fetch_depth.outputs.fetch_depth }}

View File

@ -8,7 +8,7 @@ jobs:
if: github.repository == 'grafana/grafana'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: grafana/writers-toolkit/update-make-docs@update-make-docs/v1 # zizmor: ignore[unpinned-uses]

View File

@ -0,0 +1,22 @@
name: Update Schema Types
on:
push:
branches:
- main
paths:
- docs/sources/developers/plugins/plugin.schema.json
workflow_dispatch:
# These permissions are needed to assume roles from Github's OIDC.
permissions:
contents: read
id-token: write
jobs:
bundle-schema-types:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: grafana/plugin-actions/bundle-schema-types@main

View File

@ -6,12 +6,16 @@ on:
paths:
- '**/*.cue'
permissions: {}
jobs:
main:
runs-on: "ubuntu-latest"
permissions:
contents: read # clone repository
steps:
- name: "Checkout Grafana repo"
uses: "actions/checkout@v4"
uses: "actions/checkout@v5"
with:
fetch-depth: 0
persist-credentials: false

13
.gitignore vendored
View File

@ -42,6 +42,7 @@ __debug_bin*
/devenv/docker/blocks/auth/saml-enterprise
/devenv/docker/blocks/auth/signer
/devenv/docker/blocks/mt-db
/devenv/mt-tilt
/tmp
tools/phantomjs/phantomjs
@ -94,6 +95,8 @@ example-apiserver/
/devenv/docker/blocks/auth/openldap/certs/
conf/custom.ini
conf/operator.ini
conf/storage.ini
/conf/provisioning/**/*.yaml
!/conf/provisioning/**/sample.yaml
@ -127,6 +130,9 @@ profile.cov
/public/app/extensions
!/public/app/extensions/.keep
# Enterprise operators
/pkg/operators/enterprise_*
/pkg/operators/**/enterprise_*
debug.test
/examples/*/dist
@ -189,6 +195,8 @@ compilation-stats.json
/e2e/build_results.zip
/e2e/extensions
!/e2e/extensions/.keep
/e2e-playwright/extensions
!/e2e-playwright/extensions/.keep
/e2e/extensions-suite
/test-results/
/playwright-report/
@ -218,7 +226,6 @@ public/api-spec.json
deployment_tools_config.json
.betterer.cache
.nx
# Temporary file for backporting PRs
@ -242,3 +249,7 @@ public/mockServiceWorker.js
/e2e-playwright/test-plugins/*/dist
/apps/provisioning/cmd/job-controller/bin/
# Ignore unified storage kv store files
/grafana-kv-data

View File

@ -101,6 +101,8 @@ linters:
- '**/pkg/tsdb/azuremonitor/**/*'
- '**/pkg/tsdb/cloud-monitoring/*'
- '**/pkg/tsdb/cloud-monitoring/**/*'
- '**/pkg/tsdb/graphite/*'
- '**/pkg/tsdb/graphite/**/*'
- '**/pkg/tsdb/mysql/*'
- '**/pkg/tsdb/mysql/**/*'
- '**/pkg/tsdb/parca/*'

View File

@ -11,7 +11,6 @@ node_modules
pkg
public/lib/monaco
public/sass/*.generated.scss
scripts/cli/bettererIssueTemplate.md
scripts/grafana-server/tmp
vendor
@ -43,3 +42,5 @@ public/mockServiceWorker.js
# Playwright results
test-results
playwright-report
eslint-suppressions.json

9
.vscode/launch.json vendored
View File

@ -120,6 +120,15 @@
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"name": "Debug ESLint with stats reporter",
"type": "node",
"request": "launch",
"runtimeExecutable": "yarn",
"runtimeArgs": ["run", "eslint", "${file}", "--format", "./scripts/cli/eslint-stats-reporter.mjs"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"name": "Debug Go test",
"type": "go",

File diff suppressed because one or more lines are too long

View File

@ -25,4 +25,6 @@ plugins:
path: .yarn/plugins/@yarnpkg/plugin-licenses.cjs
spec: "https://raw.githubusercontent.com/mhassan1/yarn-plugin-licenses/v0.15.0/bundles/@yarnpkg/plugin-licenses.js"
yarnPath: .yarn/releases/yarn-4.9.2.cjs
yarnPath: .yarn/releases/yarn-4.9.4.cjs
enableScripts: false

297
AGENTS.md Normal file
View File

@ -0,0 +1,297 @@
# AGENTS.md
<!-- docs-ai-begin -->
<!-- version: 1.1.0 -->
## Documentation
Instructions for documentation authoring in Markdown files.
DOCS.md contains all the Docs AI toolkit docs in one file.
## Role
Act as an experienced software engineer and technical writer for Grafana Labs.
Write for software developers and engineers who understand general programming concepts.
Focus on practical implementation and clear problem-solving guidance.
### Grafana
Use full product names on first mention, then short names:
- Grafana Alloy (full), Alloy (short)
- Grafana Beyla (full), Beyla (short)
Use "OpenTelemetry Collector" on first mention, then "Collector" for subsequent references.
Keep full name for distributions, headings, and links.
Always use "Grafana Cloud" in full.
Use complete terms:
- "OpenTelemetry" (not "OTel")
- "Kubernetes" (not "K8s")
Present observability signals in order: metrics, logs, traces, and profiles.
Focus content on Grafana solutions when discussing integrations or migrations.
## Style
### Structure
Structure articles into sections with headings.
Leave Markdown front matter content between two triple dashes `---`.
The front matter YAML `title` and the content h1 (#) heading should be the same.
Make sure there's an h1 heading in the content; this redundancy is required.
Always include copy after a heading or between headings, for example:
```markdown
## Heading
Immediately followed by copy and not another heading.
## Sub heading
```
The immediate copy after a heading should introduce and provide an overview of what's covered in the section.
Start articles with an introduction that covers the goal of the article. Example goals:
- Learn concepts
- Set up or install something
- Configure something
- Use a product to solve a business problem
- Troubleshoot a problem
- Integrate with other software or systems
- Migrate from one thing to another
- Refer to APIs or reference documentation
Follow the goal with a list of prerequisites, for example:
```markdown
Before you begin, ensure you have the following:
- <Prerequisite 1>
- <Prerequisite 2>
- ...
```
Suggest and link to next steps and related resources at the end of the article, for example:
- Learn more about A, B, C
- Configure X
- Use X to achieve Y
- Use X to achieve Z
- Project homepage or documentation
- Project repository (for example, GitHub, GitLab)
- Project package (for example, pip or NPM)
You don't need to use the "Refer to..." syntax for next steps; use the link text directly.
### Copy
Write simple, direct copy with short sentences and paragraphs.
Use contractions:
- it's, isn't, that's, you're, don't
Choose simple words:
- use (not utilize)
- help (not assist)
- show (not demonstrate)
Write with verbs and nouns. Use minimal adjectives except when describing Grafana Labs products.
## Tense
Write in present simple tense.
Avoid present continuous tense.
Only write in future tense to show future actions.
### Voice
Always write in an active voice.
Change passive voice to active voice.
### Perspective
Address users as "you".
Use second person perspective consistently.
### Wordlist
Use allowlist/blocklist instead of whitelist/blacklist.
Use primary/secondary instead of master/slave.
Use "refer to" instead of "see", "consult", "check out", and other phrases.
### Formatting
Use sentence case for titles and headings.
Use inline Markdown links: [Link text](https://example.com).
Link to other sections using descriptive phrases that include the section name:
"For setup details, refer to the [Lists](#lists) section."
Bold text with two asterisks: **bold**
Emphasize text with one underscore: _italics_
Format UI elements using sentence case as they appear:
- Click **Submit**.
- Navigate to **User settings**.
- Configure **Alerting rules**.
### Lists
Write complete sentences for lists:
- Works with all languages and frameworks (correct)
- All languages and frameworks (incorrect)
Use dashes for unordered lists.
Bold keywords at list start and follow with a colon.
### Images
Include descriptive alt text that conveys the essential information or purpose.
Write alt text without "Image of..." or "Picture of..." prefixes.
### Code
Use single code backticks for:
- user input
- placeholders in markdown, for example _`<PLACEHOLDER_NAME>`_
- files and directories, for example `/opt/file.md`
- source code keywords and identifiers,
for example variables, function and class names
- configuration options and values, for example `PORT` and `80`
- status codes, for example `404`
Use triple code backticks followed by the syntax for code blocks, for example:
```javascript
console.log('Hello World!');
```
Introduce each code block with a short description.
End the introduction with a colon if the code sample follows it, for example:
```markdown
The code sample outputs "Hello World!" to the browser console:
<CODE_BLOCK>
```
Use descriptive placeholder names in code samples.
Use uppercase letters with underscores to separate words in placeholders,
for example:
```sh
OTEL_RESOURCE_ATTRIBUTES="service.name=<SERVICE_NAME>
OTEL_EXPORTER_OTLP_ENDPOINT=<OTLP_ENDPOINT>
```
The placeholder includes the name and the less than and greater than symbols,
for example <PLACEHOLDER_NAME>.
If the placeholder is markdown emphasize it with underscores,
for example _`<PLACEHOLDER_NAME>`_.
In code blocks use the placeholder without additional backticks or emphasis,
for example <PLACEHOLDER_NAME>.
Provide an explanation for each placeholder,
typically in the text following the code block or in a configuration section.
Follow code samples with an explanation
and configuration options for placeholders, for example:
```markdown
<CODE_BLOCK>
This code sets required environment variables
to send OTLP data to an OTLP endpoint.
To configure the code refer to the configuration section.
<CONFIGURATION>
```
Put configuration for a code block after the code block.
## APIs
When documenting API endpoints specify the HTTP method,
for example `GET`, `POST`, `PUT`, `DELETE`.
Provide the full request path, using backticks.
Use backticks for parameter names and example values.
Use placeholders like `{userId}` for path parameters, for example:
- To retrieve user details, make a `GET` request to `/api/v1/users/{userId}`.
### CLI commands
When presenting CLI commands and their output,
introduce the command with a brief explanation of its purpose.
Clearly distinguish the command from its output.
For commands, use `sh` to specify the code block language.
For output, use a generic specifier like `text`, `console`,
or `json`/`yaml` if the output is structured.
For example:
```markdown
To list all running pods in the `default` namespace, use the following command:
<CODE_BLOCK>
```
The output will resemble the following:
```text
NAME READY STATUS RESTARTS AGE
my-app-deployment-7fdb6c5f65-abcde 1/1 Running 0 2d1h
another-service-pod-xyz123 2/2 Running 0 5h30m
```
### Shortcodes
Leave Hugo shortcodes in the content when editing.
Use our custom admonition Hugo shortcode for notes, cautions, or warnings,
with `<TYPE>` as "note", "caution", or "warning":
```markdown
{{< admonition type="<TYPE>" >}}
...
{{< /admonition >}}
```
Use admonitions sparingly.
Only include exceptional information in admonitions.
<!-- docs-ai-end -->

View File

@ -1,3 +1,224 @@
<!-- 12.2.0 START -->
# 12.2.0 (2025-09-23)
### Features and enhancements
- ** Alerting:** Add feedback buttons for the new AI helpers (Enterprise)
- **Access:** Remove plugin app access in plugin basic role seeder (Enterprise)
- **Actions:** Infinity authentication [#109493](https://github.com/grafana/grafana/pull/109493), [@adela-almasan](https://github.com/adela-almasan)
- **Alerting:** Add GMA export to the new list page [#109784](https://github.com/grafana/grafana/pull/109784), [@konrad147](https://github.com/konrad147)
- **Alerting:** Add alerting AI buttons for cloud (Enterprise)
- **Alerting:** Add contact point filter to Active Notifications page [#109775](https://github.com/grafana/grafana/pull/109775), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Add enrichment per rule extension component (Enterprise)
- **Alerting:** Add extension point link from alert rule to grafana-metricsdrilldown-app [#108566](https://github.com/grafana/grafana/pull/108566), [@bohandley](https://github.com/bohandley)
- **Alerting:** Add feature toggle and extension point [#110141](https://github.com/grafana/grafana/pull/110141), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
- **Alerting:** Add keepFiringFor and missing_series_evals_to_resolve to file provisioning [#109699](https://github.com/grafana/grafana/pull/109699), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Add observability to enrichment UI (Enterprise)
- **Alerting:** Add tooltips in enrichment list for enrichment type (Enterprise)
- **Alerting:** Alert enrichment list page (Enterprise)
- **Alerting:** Allow filter by rule source in Filter V2 [#110336](https://github.com/grafana/grafana/pull/110336), [@laurenashleigh](https://github.com/laurenashleigh)
- **Alerting:** Auto refresh contact points in the rule form [#109539](https://github.com/grafana/grafana/pull/109539), [@konrad147](https://github.com/konrad147)
- **Alerting:** Check if TimeInterval is used in ActiveTimings when deleting [#110691](https://github.com/grafana/grafana/pull/110691), [@fayzal-g](https://github.com/fayzal-g)
- **Alerting:** Disable group consistency check for GMA rules [#109599](https://github.com/grafana/grafana/pull/109599), [@konrad147](https://github.com/konrad147)
- **Alerting:** Display Error Message in Alert History View [#110123](https://github.com/grafana/grafana/pull/110123), [@laurenashleigh](https://github.com/laurenashleigh)
- **Alerting:** Enrichment Config Form (Enterprise)
- **Alerting:** Filter out private labels before writing recording rules [#109295](https://github.com/grafana/grafana/pull/109295), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** List V2 - Add a group link to the rule list item [#108960](https://github.com/grafana/grafana/pull/108960), [@konrad147](https://github.com/konrad147)
- **Alerting:** List V2 - datasource icons for rules [#109033](https://github.com/grafana/grafana/pull/109033), [@konrad147](https://github.com/konrad147)
- **Alerting:** Load labels in drop-downs without blocking the interaction with the form inputs [#110648](https://github.com/grafana/grafana/pull/110648), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
- **Alerting:** Mark Prometheus to Grafana conversion API as stable [#103499](https://github.com/grafana/grafana/pull/103499), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Move alerting file to an alerting folder [#110257](https://github.com/grafana/grafana/pull/110257), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
- **Alerting:** Support JSON responses in the Prometheus conversion API [#109070](https://github.com/grafana/grafana/pull/109070), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Support extra labels in the Prometheus conversion API [#109136](https://github.com/grafana/grafana/pull/109136), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Support retry with backoff in alert rule evaluation [#99710](https://github.com/grafana/grafana/pull/99710), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Triage alert history with Assistant if available (Enterprise)
- **Auditing:** Add settings to control recording of datasource query request and response body (Enterprise)
- **Auth:** Add setting to disable username based brute force login protection [#109152](https://github.com/grafana/grafana/pull/109152), [@TheoBrigitte](https://github.com/TheoBrigitte)
- **Auth:** Support JWT configs `tls_client_ca` and `jwk_set_bearer_token_file` [#109095](https://github.com/grafana/grafana/pull/109095), [@Baarsgaard](https://github.com/Baarsgaard)
- **Azure:** Resource picker improvements (#109458) [#109520](https://github.com/grafana/grafana/pull/109520), [@aangelisc](https://github.com/aangelisc)
- **Azure:** Show resource group in picker [#110442](https://github.com/grafana/grafana/pull/110442), [@aangelisc](https://github.com/aangelisc)
- **Canvas:** Add option to disable tooltips for one-click elements [#109937](https://github.com/grafana/grafana/pull/109937), [@adela-almasan](https://github.com/adela-almasan)
- **Canvas:** Dynamic connection direction [#108423](https://github.com/grafana/grafana/pull/108423), [@adela-almasan](https://github.com/adela-almasan)
- **Chore:** Remove prometheusCodeModeMetricNamesSearch feature toggle [#109024](https://github.com/grafana/grafana/pull/109024), [@itsmylife](https://github.com/itsmylife)
- **Chore:** Removes HideAngularDeprecation configuration [#110665](https://github.com/grafana/grafana/pull/110665), [@hugohaggmark](https://github.com/hugohaggmark)
- **CloudConfig:** Add config from defaults.ini to StackInfo (Enterprise)
- **CloudWatch:** Append query type to the request id [#109068](https://github.com/grafana/grafana/pull/109068), [@idastambuk](https://github.com/idastambuk)
- **CloudWatch:** Use default region when query region is unset [#109089](https://github.com/grafana/grafana/pull/109089), [@iwysiu](https://github.com/iwysiu)
- **CloudWatch:** Use the correct metric name for errors per function panel in the AWS Lambda sample dashboard [#110718](https://github.com/grafana/grafana/pull/110718), [@kevinwcyu](https://github.com/kevinwcyu)
- **CommandPalette:** Use fuzzySearch util from grafana/data [#108884](https://github.com/grafana/grafana/pull/108884), [@Clarity-89](https://github.com/Clarity-89)
- **Dashboard:** Inspect drawer can no longer be opened with url or linked to [#109617](https://github.com/grafana/grafana/pull/109617), [@torkelo](https://github.com/torkelo)
- **Dashboards:** Add support for full screen panel view and embedded (solo panel) route to repeated panels and new layouts (via new SoloPanelContex) [#107375](https://github.com/grafana/grafana/pull/107375), [@torkelo](https://github.com/torkelo)
- **Dashboards:** Conserve timestamp on time range copy-paste across timezones [#109769](https://github.com/grafana/grafana/pull/109769), [@alik-r](https://github.com/alik-r)
- **Dashboards:** Enable kubernetesDashboards by default [#107618](https://github.com/grafana/grafana/pull/107618), [@dprokop](https://github.com/dprokop)
- **Dashboards:** Make it possible to render variables under a drop-down [#109225](https://github.com/grafana/grafana/pull/109225), [@leventebalogh](https://github.com/leventebalogh)
- **Database:** Add primary key to Settings table (Enterprise)
- **Database:** Add primary key to settings table (Enterprise)
- **Dependencies:** Bump Go to v1.24.5 (Enterprise)
- **Docs:** Deprecate `grafana/grafana-oss` docker repo in favor of `grafana/grafana` [#110065](https://github.com/grafana/grafana/pull/110065), [@kminehart](https://github.com/kminehart)
- **Flame Graph:** Analyze with Grafana Assistant [#108684](https://github.com/grafana/grafana/pull/108684), [@ifrost](https://github.com/ifrost)
- **Folders:** Add team folders feature toggle [#109389](https://github.com/grafana/grafana/pull/109389), [@tomratcliffe](https://github.com/tomratcliffe)
- **Folders:** Update folder using app platform APIs [#110449](https://github.com/grafana/grafana/pull/110449), [@tomratcliffe](https://github.com/tomratcliffe)
- **Folders:** Use app platform search endpoint and update tests [#108814](https://github.com/grafana/grafana/pull/108814), [@tomratcliffe](https://github.com/tomratcliffe)
- **Go:** Update to 1.24.6 [#109313](https://github.com/grafana/grafana/pull/109313), [@Proximyst](https://github.com/Proximyst)
- **InfluxDB:** Ad hoc filters support for expressions [#109344](https://github.com/grafana/grafana/pull/109344), [@aangelisc](https://github.com/aangelisc)
- **Metrics:** Add http_response_size_bytes metric [#110428](https://github.com/grafana/grafana/pull/110428), [@joshhunt](https://github.com/joshhunt)
- **Nested folders:** Remove feature flag [#109212](https://github.com/grafana/grafana/pull/109212), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
- **NestedFolderPicker:** Add rootFolderUID prop [#109991](https://github.com/grafana/grafana/pull/109991), [@ywzheng1](https://github.com/ywzheng1)
- **P2P Filter:** Add adhoc filter option toggle [#110160](https://github.com/grafana/grafana/pull/110160), [@Develer](https://github.com/Develer)
- **PieChart:** Add panel options for ascending/descending sort, and no sorting [#109564](https://github.com/grafana/grafana/pull/109564), [@cglukas](https://github.com/cglukas)
- **Plugin Extensions:** DataSource Configuration Components [#108350](https://github.com/grafana/grafana/pull/108350), [@shelldandy](https://github.com/shelldandy)
- **Plugins:** Add Connections homepage [#108316](https://github.com/grafana/grafana/pull/108316), [@oshirohugo](https://github.com/oshirohugo)
- **Plugins:** Record plugin version in request metrics [#110210](https://github.com/grafana/grafana/pull/110210), [@njvrzm](https://github.com/njvrzm)
- **Preferences:** Move codegen to apps [#109178](https://github.com/grafana/grafana/pull/109178), [@ryantxu](https://github.com/ryantxu)
- **Prometheus data source:** Migration service [#107364](https://github.com/grafana/grafana/pull/107364), [@bossinc](https://github.com/bossinc)
- **Prometheus:** Refactor metrics modal to handle high cardinality metrics [#108437](https://github.com/grafana/grafana/pull/108437), [@itsmylife](https://github.com/itsmylife)
- **Pyroscope:** Process and display sampling annotations [#109707](https://github.com/grafana/grafana/pull/109707), [@aleks-p](https://github.com/aleks-p)
- **Reporting:** Permit valid but weird emails (Enterprise)
- **Reporting:** Show correct recipient count (Enterprise)
- **Revert:** DataSource: Support config CRUD from apiservers (#106996) [#110342](https://github.com/grafana/grafana/pull/110342), [@njvrzm](https://github.com/njvrzm)
- **Revert:** DataSource: Support config CRUD from apiservers (#8860) (Enterprise)
- **SCIM:** Add flag for rejecting non provisioned users from logging in (Enterprise)
- **SCIM:** Allow empty externalId on update operation (Enterprise)
- **SCIM:** Delete user instead of disabling it on SCIM DELETE user request (Enterprise)
- **SQL Expressions:** Switch feature toggle to public preview [#110473](https://github.com/grafana/grafana/pull/110473), [@kylebrandt](https://github.com/kylebrandt)
- **Table:** Frozen columns [#109276](https://github.com/grafana/grafana/pull/109276), [@fastfrwrd](https://github.com/fastfrwrd)
- **Table:** Max row height for variable height rows [#109639](https://github.com/grafana/grafana/pull/109639), [@fastfrwrd](https://github.com/fastfrwrd)
- **Table:** Tooltip from Field [#109428](https://github.com/grafana/grafana/pull/109428), [@fastfrwrd](https://github.com/fastfrwrd)
- **Table:** Update UX for uniform-reducer case in new footer and overflow [#110493](https://github.com/grafana/grafana/pull/110493), [@fastfrwrd](https://github.com/fastfrwrd)
- **TableNG:** Footer enhancements [#102948](https://github.com/grafana/grafana/pull/102948), [@alexjonspencer1](https://github.com/alexjonspencer1)
- **Text:** Add Inter italic font variants to Grafana UI [#110313](https://github.com/grafana/grafana/pull/110313), [@kapowaz](https://github.com/kapowaz)
- **TraceView:** Refine UI visual hierarchy inside details section [#108929](https://github.com/grafana/grafana/pull/108929), [@ifrost](https://github.com/ifrost)
- **Transformations:** Add empty values options to Transpose [#108421](https://github.com/grafana/grafana/pull/108421), [@gelicia](https://github.com/gelicia)
- **Trend/TimeSeries:** Add "Show values" option [#108090](https://github.com/grafana/grafana/pull/108090), [@HasithDeAlwis](https://github.com/HasithDeAlwis)
- **Trend:** Add support for a logarithmic x axis [#101433](https://github.com/grafana/grafana/pull/101433), [@gelicia](https://github.com/gelicia)
- **Variables:** shows warning when user tries to save erroneous variables [#110154](https://github.com/grafana/grafana/pull/110154), [@hugohaggmark](https://github.com/hugohaggmark)
- **VizTooltip:** Replace `ExemplarHoverView` with `VizTooltip` components [#109369](https://github.com/grafana/grafana/pull/109369), [@adela-almasan](https://github.com/adela-almasan)
### Bug fixes
- **Alerting:** Fix bug where rules with identical mute/active intervals produced conflicting routes [#110971](https://github.com/grafana/grafana/pull/110971), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Fix copying of recording rule fields [#110311](https://github.com/grafana/grafana/pull/110311), [@moustafab](https://github.com/moustafab)
- **Alerting:** Fix field names on webhook HMAC/TLS config HCL export [#110722](https://github.com/grafana/grafana/pull/110722), [@JacobsonMT](https://github.com/JacobsonMT)
- **Alerting:** Fix newly created alert rules not immediately showing up in folder view [#109584](https://github.com/grafana/grafana/pull/109584), [@tomratcliffe](https://github.com/tomratcliffe)
- **Alerting:** Fix permission checks for the Import to GMA [#109950](https://github.com/grafana/grafana/pull/109950), [@konrad147](https://github.com/konrad147)
- **Alerting:** Fix permissions for enrichment routes (Enterprise)
- **Alerting:** Fix subpath handling in the alerting package [#109448](https://github.com/grafana/grafana/pull/109448), [@konrad147](https://github.com/konrad147)
- **Alerting:** Fix wrong import (Enterprise)
- **Alerting:** Hide list view loader if we don't have anything yet [#110464](https://github.com/grafana/grafana/pull/110464), [@gillesdemey](https://github.com/gillesdemey)
- **Alerting:** Set dataSourceName to GRAFANA_RULES_SOURCE_NAME when switch… [#109900](https://github.com/grafana/grafana/pull/109900), [@laurenashleigh](https://github.com/laurenashleigh)
- **Alerting:** Update alerting module to 10915888e4f099586ad37bea5f4a70f45101d2f5 [#109989](https://github.com/grafana/grafana/pull/109989), [@yuri-tceretian](https://github.com/yuri-tceretian)
- **Azure:** Fix logs editor rendering [#109491](https://github.com/grafana/grafana/pull/109491), [@aangelisc](https://github.com/aangelisc)
- **Canvas:** Fix element selection being cleared on panel resize [#110010](https://github.com/grafana/grafana/pull/110010), [@adela-almasan](https://github.com/adela-almasan)
- **CloudConfig:** Fix panic in defaults.ini merge (Enterprise)
- **CloudWatch:** Fix handling region for legacy alerts [#109217](https://github.com/grafana/grafana/pull/109217), [@iwysiu](https://github.com/iwysiu)
- **CloudWatch:** Fix logs query requestId to prevent setting undefined-logs as a requestId [#109930](https://github.com/grafana/grafana/pull/109930), [@kevinwcyu](https://github.com/kevinwcyu)
- **CloudWatch:** Update grafana/aws-sdk-go with STS endpoint bugfix [#109120](https://github.com/grafana/grafana/pull/109120), [@idastambuk](https://github.com/idastambuk)
- **Config:** Fix date_formats options being moved to a different section [#109339](https://github.com/grafana/grafana/pull/109339), [@joshhunt](https://github.com/joshhunt)
- **Dashboard List:** Fix how link query part is created when variables are included [#109861](https://github.com/grafana/grafana/pull/109861), [@aocenas](https://github.com/aocenas)
- **Dashboard versions:** Fix list for large dashboards [#109433](https://github.com/grafana/grafana/pull/109433), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
- **Dashboard:** Fix AngularJS deprecation in grafana-overview dashboard [#106462](https://github.com/grafana/grafana/pull/106462), [@schoen2](https://github.com/schoen2)
- **Dashboard:** Fixes url links to embedded panels in scene based dashboards [#109837](https://github.com/grafana/grafana/pull/109837), [@torkelo](https://github.com/torkelo)
- **Dashboards:** Fix UTF-8 characters not working with excel downloads by replacing download for excel with excel compatibility mode. [#110099](https://github.com/grafana/grafana/pull/110099), [@oscarkilhed](https://github.com/oscarkilhed)
- **Dashboards:** Fix issue where the time range picker would seemingly be hidden behind the side menu if it was set to always open. [#108607](https://github.com/grafana/grafana/pull/108607), [@oscarkilhed](https://github.com/oscarkilhed)
- **Dashboards:** Fix kiosk mode not persisting through refresh [#110284](https://github.com/grafana/grafana/pull/110284), [@oscarkilhed](https://github.com/oscarkilhed)
- **Dashboards:** Fixing saving and viewing snapshots for repeated panels [#109856](https://github.com/grafana/grafana/pull/109856), [@torkelo](https://github.com/torkelo)
- **Explore:** Fix units overflow for trace durations [#108515](https://github.com/grafana/grafana/pull/108515), [@martincostello](https://github.com/martincostello)
- **Fix:** Install plugins when they have no plugin archive info(catalog en… [#109200](https://github.com/grafana/grafana/pull/109200), [@s4kh](https://github.com/s4kh)
- **InfluxDB:** Fix Unable to use self-signed CA for adding influxdb data source [#105586](https://github.com/grafana/grafana/pull/105586), [@geekeryy](https://github.com/geekeryy)
- **Prometheus:** Don't use incremental querying if one of the queries has $\_\_range variable [#108823](https://github.com/grafana/grafana/pull/108823), [@itsmylife](https://github.com/itsmylife)
- **Prometheus:** Fix eager auto completion [#109128](https://github.com/grafana/grafana/pull/109128), [@itsmylife](https://github.com/itsmylife)
- **Prometheus:** QueryEditor fix error when switching from code to builder for undefined aggregation operations [#110179](https://github.com/grafana/grafana/pull/110179), [@jcolladokuri](https://github.com/jcolladokuri)
- **Pyroscope:** Add start and end date to profiletypes call [#110277](https://github.com/grafana/grafana/pull/110277), [@zoltanbedi](https://github.com/zoltanbedi)
- **Pyroscope:** Fix incorrect rate calculation from flamegraph totals [#110470](https://github.com/grafana/grafana/pull/110470), [@marcsanmi](https://github.com/marcsanmi)
- **Service Accounts:** Fix typo on page indicating none are present [#109560](https://github.com/grafana/grafana/pull/109560), [@eamonryan](https://github.com/eamonryan)
- **Tempo:** Fix instant query streaming [#108924](https://github.com/grafana/grafana/pull/108924), [@adrapereira](https://github.com/adrapereira)
- **TimeSeries:** Use exported time shift and fix time comparison tooltip [#109947](https://github.com/grafana/grafana/pull/109947), [@drew08t](https://github.com/drew08t)
- **Transformations:** Account for group by / count when assessing if calculation is needed [#110546](https://github.com/grafana/grafana/pull/110546), [@gelicia](https://github.com/gelicia)
- **Transforms:** GroupToMatrix transform should retain keyRowField config [#109066](https://github.com/grafana/grafana/pull/109066), [@fastfrwrd](https://github.com/fastfrwrd)
### Breaking changes
- **Alerting:** Enable alertingSaveStateCompressed by default [#109390](https://github.com/grafana/grafana/pull/109390), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Dashboards:** Repeating with no clone keys [#109839](https://github.com/grafana/grafana/pull/109839), [@torkelo](https://github.com/torkelo)
- **Provisioning:** Use inline secrets for gitsync [#109908](https://github.com/grafana/grafana/pull/109908), [@ryantxu](https://github.com/ryantxu)
- **Stars:** Remove deprecated internal ID apis [#110499](https://github.com/grafana/grafana/pull/110499), [@ryantxu](https://github.com/ryantxu)
### Plugin development fixes & changes
- **Drawer:** Truncate Drawer title to just one line [#109540](https://github.com/grafana/grafana/pull/109540), [@joshhunt](https://github.com/joshhunt)
- **Modal:** Center modals at smaller screen heights [#109256](https://github.com/grafana/grafana/pull/109256), [@ashharrison90](https://github.com/ashharrison90)
- **MultiCombobox:** Fix async options to being able to be removed [#109473](https://github.com/grafana/grafana/pull/109473), [@joshhunt](https://github.com/joshhunt)
- **MultiCombobox:** Fix select all when only a single option is available [#109910](https://github.com/grafana/grafana/pull/109910), [@aangelisc](https://github.com/aangelisc)
<!-- 12.2.0 END -->
<!-- 12.1.2 START -->
# 12.1.2 (2025-09-23)
### Features and enhancements
- **Alerting:** Update alerting module [#109999](https://github.com/grafana/grafana/pull/109999), [@yuri-tceretian](https://github.com/yuri-tceretian)
- **Auditing:** Add settings to control recording of datasource query request and response body (Enterprise)
- **Auditing:** Document new options for recording datasource query request/response body [#109981](https://github.com/grafana/grafana/pull/109981), [@macabu](https://github.com/macabu)
- **Chore:** Don't show a "Not found" for public-dashboard fetches if the service is disabled via config [#110144](https://github.com/grafana/grafana/pull/110144), [@mmandrus](https://github.com/mmandrus)
- **CloudWatch:** Use default region when query region is unset [#111079](https://github.com/grafana/grafana/pull/111079), [@iwysiu](https://github.com/iwysiu)
### Bug fixes
- **Alerting:** Fix bug where rules with identical mute/active intervals produced conflicting routes [#110973](https://github.com/grafana/grafana/pull/110973), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Fix copying of recording rule fields [#110312](https://github.com/grafana/grafana/pull/110312), [@moustafab](https://github.com/moustafab)
- **Fix:** Fix redirection after login when Grafana is served from subpath [#111097](https://github.com/grafana/grafana/pull/111097), [@mgyongyosi](https://github.com/mgyongyosi)
### Plugin development fixes & changes
- **Fix:** Prevent Rollup from treeshaking NPM packages [#108570](https://github.com/grafana/grafana/pull/108570), [@jackw](https://github.com/jackw)
<!-- 12.1.2 END -->
<!-- 12.0.5 START -->
# 12.0.5 (2025-09-23)
### Features and enhancements
- **Alerting:** Update alerting module [#110000](https://github.com/grafana/grafana/pull/110000), [@yuri-tceretian](https://github.com/yuri-tceretian)
- **Auditing:** Add settings to control recording of datasource query request and response body (Enterprise)
- **Auditing:** Document new options for recording datasource query request/response body [#109980](https://github.com/grafana/grafana/pull/109980), [@macabu](https://github.com/macabu)
### Bug fixes
- **Alerting:** Fix copying of recording rule fields [#110346](https://github.com/grafana/grafana/pull/110346), [@moustafab](https://github.com/moustafab)
- **Azure:** Fix time management field [#108481](https://github.com/grafana/grafana/pull/108481), [@aangelisc](https://github.com/aangelisc)
- **Fix:** Fix redirection after login when Grafana is served from subpath [#111156](https://github.com/grafana/grafana/pull/111156), [@mgyongyosi](https://github.com/mgyongyosi)
### Plugin development fixes & changes
- **Fix:** Prevent Rollup from treeshaking NPM packages [#110523](https://github.com/grafana/grafana/pull/110523), [@jackw](https://github.com/jackw)
<!-- 12.0.5 END -->
<!-- 11.6.6 START -->
# 11.6.6 (2025-09-23)
### Features and enhancements
- **Auditing:** Add settings to control recording of datasource query request and response body (Enterprise)
<!-- 11.6.6 END -->
<!-- 11.5.9 START -->
# 11.5.9 (2025-09-23)
### Features and enhancements
- **Auditing:** Add settings to control recording of datasource query request and response body (Enterprise)
- **Auditing:** Document new options for recording datasource query request/response body [#109976](https://github.com/grafana/grafana/pull/109976), [@macabu](https://github.com/macabu)
### Bug fixes
- **Fix:** Fix redirection after login when Grafana is served from subpath [#111099](https://github.com/grafana/grafana/pull/111099), [@mgyongyosi](https://github.com/mgyongyosi)
<!-- 11.5.9 END -->
<!-- 12.1.1 START -->
# 12.1.1 (2025-08-13)
@ -13,6 +234,7 @@
- **Alerting:** Fix active time intervals when time interval is renamed [#108547](https://github.com/grafana/grafana/pull/108547), [@yuri-tceretian](https://github.com/yuri-tceretian)
- **Alerting:** Fix subpath handling in the alerting package [#109505](https://github.com/grafana/grafana/pull/109505), [@konrad147](https://github.com/konrad147)
- **Config:** Fix date_formats options being moved to a different section [#109366](https://github.com/grafana/grafana/pull/109366), [@joshhunt](https://github.com/joshhunt)
- **Pyroscope:** Fix flamegraph totals showing incorrect values after rate aggregation changes [#110470](https://github.com/grafana/grafana/pull/110470), [@marcsanmiquel](https://github.com/marcsanmiquel)
<!-- 12.1.1 END -->
<!-- 12.0.4 START -->

View File

@ -2,7 +2,7 @@
Thank you for your interest in contributing to Grafana! We welcome all people who want to contribute in a healthy and constructive manner within our community. To help us create a safe and positive community experience for all, we require all participants to adhere to the [Code of Conduct](CODE_OF_CONDUCT.md).
This document is a guide to help you through the process of making technical contributions to Grafana.
This document is a guide to help you through the process of contributing to Grafana. Be sure to check out the [Grafana Champions program](https://grafana.com/community/champions/?src=github&camp=community-cross-platform-engagement) as you start to contribute- its designed to recognize and empower individuals who are actively contributing to the growth and success of the Grafana ecosystem.
Whether you're a new contributer or a seasoned veteran we hope these resources help you connect with the community:

View File

@ -28,15 +28,17 @@ ENV NODE_OPTIONS=--max_old_space_size=8000
WORKDIR /tmp/grafana
RUN apk add --no-cache make build-base python3
COPY package.json project.json nx.json yarn.lock .yarnrc.yml ./
COPY .yarn .yarn
COPY packages packages
COPY e2e-playwright e2e-playwright
COPY public public
COPY LICENSE ./
COPY conf/defaults.ini ./conf/defaults.ini
COPY e2e e2e
RUN apk add --no-cache make build-base python3
#
# Set the node env according to defaults or argument passed
#
@ -93,16 +95,22 @@ COPY pkg/aggregator pkg/aggregator
COPY apps/playlist apps/playlist
COPY apps/plugins apps/plugins
COPY apps/shorturl apps/shorturl
COPY apps/correlations apps/correlations
COPY apps/preferences apps/preferences
COPY apps/provisioning apps/provisioning
COPY apps/secret apps/secret
COPY apps/scope apps/scope
COPY apps/investigations apps/investigations
COPY apps/advisor apps/advisor
COPY apps/dashboard apps/dashboard
COPY apps/folder apps/folder
COPY apps/preferences apps/preferences
COPY apps/iam apps/iam
COPY apps apps
COPY kindsv2 kindsv2
COPY apps/alerting/alertenrichment apps/alerting/alertenrichment
COPY apps/alerting/notifications apps/alerting/notifications
COPY apps/alerting/rules apps/alerting/rules
COPY pkg/codegen pkg/codegen
COPY pkg/plugins/codegen pkg/plugins/codegen

View File

@ -17,6 +17,7 @@ GO_RACE_FLAG := $(if $(GO_RACE),-race)
GO_BUILD_FLAGS += $(if $(GO_BUILD_DEV),-dev)
GO_BUILD_FLAGS += $(if $(GO_BUILD_TAGS),-build-tags=$(GO_BUILD_TAGS))
GO_BUILD_FLAGS += $(GO_RACE_FLAG)
GO_BUILD_FLAGS += $(if $(GO_BUILD_CGO),-cgo-enabled=$(GO_BUILD_CGO))
GO_TEST_FLAGS += $(if $(GO_BUILD_TAGS),-tags=$(GO_BUILD_TAGS))
GIT_BASE = remotes/origin/main
@ -70,6 +71,7 @@ swagger-oss-gen: ## Generate API Swagger specification
-x "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" \
-x "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" \
-x "github.com/prometheus/alertmanager" \
-x "github.com/docker/docker" \
-i pkg/api/swagger_tags.json \
--exclude-tag=alpha \
--exclude-tag=enterprise
@ -88,6 +90,7 @@ swagger-enterprise-gen: ## Generate API Swagger specification
-x "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" \
-x "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" \
-x "github.com/prometheus/alertmanager" \
-x "github.com/docker/docker" \
-i pkg/api/swagger_tags.json \
-t enterprise \
--exclude-tag=alpha \
@ -171,7 +174,19 @@ gen-cuev2: ## Do all CUE code generation
APPS_DIRS := ./apps/dashboard ./apps/folder ./apps/alerting/notifications
.PHONY: gen-apps
gen-apps: ## Generate code for Grafana App SDK apps
gen-apps: do-gen-apps gofmt ## Generate code for Grafana App SDK apps and run gofmt
@if [ -n "$$CODEGEN_VERIFY" ]; then \
echo "Verifying generated code is up to date..."; \
if ! git diff --quiet; then \
echo "Error: Generated apps code is not up to date. Please run 'make gen-apps' to regenerate."; \
git diff --name-only; \
exit 1; \
fi; \
echo "Generated apps code is up to date."; \
fi
.PHONY: do-gen-apps
do-gen-apps: ## Generate code for Grafana App SDK apps
for dir in $(APPS_DIRS); do \
$(MAKE) -C $$dir generate; \
done
@ -231,6 +246,10 @@ build-backend: ## Build Grafana backend.
@echo "build backend"
$(GO) run build.go $(GO_BUILD_FLAGS) build-backend
.PHONY: build-air
build-air: build-backend
@cp ./bin/grafana ./bin/grafana-air
.PHONY: build-server
build-server: ## Build Grafana server.
@echo "build server"
@ -384,6 +403,10 @@ lint-go-diff:
sed 's,^,./,' | \
$(XARGSR) $(golangci-lint) run --config .golangci.yml
.PHONY: gofmt
gofmt: ## Run gofmt for all Go files.
@go list -m -f '{{.Dir}}' | xargs -I{} sh -c 'test ! -f {}/.nolint && echo {}' | xargs gofmt -s -w 2>&1 | grep -v '/pkg/build/' || true
# with disabled SC1071 we are ignored some TCL,Expect `/usr/bin/env expect` scripts
.PHONY: shellcheck
shellcheck: $(SH_FILES) ## Run checks for shell scripts.

View File

@ -8,7 +8,7 @@ require (
github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43
github.com/grafana/grafana v0.0.0-00010101000000-000000000000
github.com/grafana/grafana-app-sdk v0.40.2
github.com/grafana/grafana-app-sdk/logging v0.40.1
github.com/grafana/grafana-app-sdk/logging v0.40.2
github.com/grafana/grafana-plugin-sdk-go v0.278.0
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2
github.com/stretchr/testify v1.10.0
@ -25,7 +25,7 @@ replace github.com/prometheus/alertmanager => github.com/grafana/prometheus-aler
require (
cloud.google.com/go/compute/metadata v0.7.0 // indirect
dario.cat/mergo v1.0.1 // indirect
dario.cat/mergo v1.0.2 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 // indirect
@ -244,14 +244,14 @@ require (
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 // indirect
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0 // indirect
go.opentelemetry.io/otel v1.37.0 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
@ -291,10 +291,9 @@ require (
modernc.org/libc v1.65.0 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.10.0 // indirect
modernc.org/sqlite v1.37.0 // indirect
modernc.org/sqlite v1.38.0 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
xorm.io/builder v0.3.6 // indirect
)

View File

@ -72,8 +72,8 @@ cloud.google.com/go/storage v1.55.0 h1:NESjdAToN9u1tmhVqhXCaCwYBuvEhZLLv0gBr+2zn
cloud.google.com/go/storage v1.55.0/go.mod h1:ztSmTTwzsdXe5syLVS0YsbFxXuvEmEyZj7v7zChEmuY=
cuelang.org/go v0.11.1 h1:pV+49MX1mmvDm8Qh3Za3M786cty8VKPWzQ1Ho4gZRP0=
cuelang.org/go v0.11.1/go.mod h1:PBY6XvPUswPPJ2inpvUozP9mebDVTXaeehQikhZPBz0=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
@ -665,8 +665,8 @@ github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg
github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E=
github.com/grafana/grafana-app-sdk v0.40.2 h1:j2ftFuqhX+exYUipfEjeWDs3i7oiJkweTF8gFLL7wWU=
github.com/grafana/grafana-app-sdk v0.40.2/go.mod h1:BbNXPNki3mtbkWxYqJsyA1Cj9AShSyaY33z8WkyfVv0=
github.com/grafana/grafana-app-sdk/logging v0.40.1 h1:ru+GqbaQk6jthA5l2Yo1WI/JbNXKNQmLiqNrxz7HGP4=
github.com/grafana/grafana-app-sdk/logging v0.40.1/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU=
github.com/grafana/grafana-app-sdk/logging v0.40.2 h1:HQ1+y9Od92iMbWWB54QxiYpNtCvYGUVpyxvxZ7ywB1k=
github.com/grafana/grafana-app-sdk/logging v0.40.2/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU=
github.com/grafana/grafana-aws-sdk v1.1.0 h1:G0fvwbQmHw14c5RXPd7Gnw9ZQcgzl139LtMDoe0KhmE=
github.com/grafana/grafana-aws-sdk v1.1.0/go.mod h1:7e+47EdHynteYWGoT5Ere9KeOXQObsk8F0vkOLQ1tz8=
github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0 h1:0TYrkzAc3u0HX+9GK86cGrLTUAcmQfl3/LEB3tL+SOA=
@ -1135,8 +1135,6 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
github.com/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4=
github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
github.com/thejerf/slogassert v0.3.4 h1:VoTsXixRbXMrRSSxDjYTiEDCM4VWbsYPW5rB/hX24kM=

View File

@ -7,6 +7,7 @@ import (
"github.com/grafana/grafana-app-sdk/app"
"github.com/grafana/grafana-app-sdk/k8s"
"github.com/grafana/grafana-app-sdk/logging"
"github.com/grafana/grafana-app-sdk/operator"
"github.com/grafana/grafana-app-sdk/resource"
"github.com/grafana/grafana-app-sdk/simple"
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
@ -48,10 +49,12 @@ func New(cfg app.Config) (app.App, error) {
Name: "advisor",
KubeConfig: cfg.KubeConfig,
InformerConfig: simple.AppInformerConfig{
InformerOptions: operator.InformerOptions{
ErrorHandler: func(ctx context.Context, err error) {
log.WithContext(ctx).Error("Informer processing error", "error", err)
},
},
},
ManagedKinds: []simple.AppManagedKind{
{
Kind: advisorv0alpha1.CheckKind(),
@ -78,7 +81,7 @@ func New(cfg app.Config) (app.App, error) {
}
}()
}
if req.Action == resource.AdmissionActionUpdate {
if req.Action == resource.AdmissionActionUpdate && retryAnnotationChanged(req.OldObject, req.Object) {
go func() {
logger := log.WithContext(ctx).With("check", check.ID())
logger.Debug("Updating check", "namespace", req.Object.GetNamespace(), "name", req.Object.GetName())

View File

@ -5,6 +5,8 @@ import (
"testing"
"github.com/grafana/grafana-app-sdk/logging"
"github.com/stretchr/testify/assert"
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/repo"
@ -12,7 +14,6 @@ import (
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
"github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins"
"github.com/stretchr/testify/assert"
)
func TestRun(t *testing.T) {
@ -22,7 +23,7 @@ func TestRun(t *testing.T) {
pluginInfo []repo.PluginInfo
pluginPreinstalled []string
pluginManaged []string
pluginProvisioned []string
pluginProvisioned []provisionedplugins.Plugin
pluginErrors []*plugins.Error
expectedFailures []advisor.CheckReportFailure
}{
@ -117,7 +118,7 @@ func TestRun(t *testing.T) {
pluginInfo: []repo.PluginInfo{
{Status: "deprecated", Slug: "plugin5", Version: "1.1.0"}, // This should be ignored
},
pluginProvisioned: []string{"plugin5"},
pluginProvisioned: []provisionedplugins.Plugin{{ID: "plugin5"}},
expectedFailures: []advisor.CheckReportFailure{},
},
{
@ -281,10 +282,10 @@ func (m *mockManagedPlugins) ManagedPlugins(ctx context.Context) []string {
type mockProvisionedPlugins struct {
provisionedplugins.Manager
provisioned []string
provisioned []provisionedplugins.Plugin
}
func (m *mockProvisionedPlugins) ProvisionedPlugins(ctx context.Context) ([]string, error) {
func (m *mockProvisionedPlugins) ProvisionedPlugins(_ context.Context) ([]provisionedplugins.Plugin, error) {
return m.provisioned, nil
}

View File

@ -139,7 +139,9 @@ func (r *Runner) Run(ctx context.Context) error {
}
func (r *Runner) listChecks(ctx context.Context, logger logging.Logger) ([]resource.Object, error) {
list, err := r.client.List(ctx, r.namespace, resource.ListOptions{})
list, err := r.client.List(ctx, r.namespace, resource.ListOptions{
Limit: 1000, // Avoid pagination for normal uses cases, which is a costly operation
})
if err != nil {
return nil, err
}
@ -147,7 +149,7 @@ func (r *Runner) listChecks(ctx context.Context, logger logging.Logger) ([]resou
checks := list.GetItems()
for list.GetContinue() != "" {
logger.Debug("List has continue token, listing next page", "continue", list.GetContinue())
list, err = r.client.List(ctx, r.namespace, resource.ListOptions{Continue: list.GetContinue()})
list, err = r.client.List(ctx, r.namespace, resource.ListOptions{Continue: list.GetContinue(), Limit: 1000})
if err != nil {
return nil, err
}

View File

@ -7,6 +7,8 @@ import (
"strings"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/grafana/grafana-app-sdk/app"
"github.com/grafana/grafana-app-sdk/k8s"
"github.com/grafana/grafana-app-sdk/logging"
@ -60,38 +62,6 @@ func New(cfg app.Config, log logging.Logger) (app.Runnable, error) {
}, nil
}
func (r *Runner) createOrUpdate(ctx context.Context, log logging.Logger, obj resource.Object) error {
id := obj.GetStaticMetadata().Identifier()
_, err := r.client.Create(ctx, id, obj, resource.CreateOptions{})
if err != nil {
if errors.IsAlreadyExists(err) {
// Already exists, update
log.Debug("Check type already exists, updating", "identifier", id)
// Retrieve current annotations to avoid overriding them
current, err := r.client.Get(ctx, obj.GetStaticMetadata().Identifier())
if err != nil {
return err
}
currentAnnotations := current.GetAnnotations()
if currentAnnotations == nil {
currentAnnotations = make(map[string]string)
}
annotations := obj.GetAnnotations()
maps.Copy(currentAnnotations, annotations)
obj.SetAnnotations(currentAnnotations) // This will update the annotations in the object
_, err = r.client.Update(ctx, id, obj, resource.UpdateOptions{})
if err != nil && !errors.IsAlreadyExists(err) {
// Ignore the error, it's probably due to a race condition
log.Info("Error updating check type, ignoring", "error", err)
}
return nil
}
return err
}
log.Debug("Check type registered successfully", "identifier", id)
return nil
}
func (r *Runner) Run(ctx context.Context) error {
logger := r.log.WithContext(ctx)
for _, t := range r.checkRegistry.Checks() {
@ -121,26 +91,139 @@ func (r *Runner) Run(ctx context.Context) error {
Steps: stepTypes,
},
}
for i := 0; i < r.retryAttempts; i++ {
err := r.createOrUpdate(context.WithoutCancel(ctx), logger, obj)
err := r.registerCheckType(ctx, logger, t.ID(), obj)
if err != nil {
if strings.Contains(err.Error(), "apiserver is shutting down") {
logger.Debug("Error creating check type, not retrying", "error", err)
return err
}
}
return nil
}
logger.Debug("Error creating check type, retrying", "error", err, "attempt", i+1)
if i == r.retryAttempts-1 {
logger.Error("Unable to register check type", "check_type", t.ID(), "error", err)
} else {
// Calculate exponential backoff delay: baseDelay * 2^attempt
delay := r.retryDelay * time.Duration(1<<i)
time.Sleep(delay)
func (r *Runner) registerCheckType(ctx context.Context, logger logging.Logger, checkType string, obj resource.Object) error {
for i := 0; i < r.retryAttempts; i++ {
current, err := r.client.Get(ctx, obj.GetStaticMetadata().Identifier())
if err != nil {
if errors.IsNotFound(err) {
// Check type does not exist, create it
err = r.create(context.WithoutCancel(ctx), logger, obj)
if err != nil {
if !r.shouldRetry(err, logger, i+1, checkType) {
return nil
}
// Retry
continue
}
logger.Debug("Check type registered successfully", "check_type", t.ID())
// Success
logger.Debug("Check type created successfully", "check_type", checkType)
break
}
if !r.shouldRetry(err, logger, i+1, checkType) {
return nil
}
// Retry
continue
}
// Check type already exists, check if it's the same and update if needed
logger.Debug("Check type already exists, checking if it's the same", "identifier", obj.GetStaticMetadata().Identifier())
if r.needsUpdate(current, obj, logger) {
err = r.update(context.WithoutCancel(ctx), logger, obj, current)
if err != nil {
if !r.shouldRetry(err, logger, i+1, checkType) {
return nil
}
// Retry
continue
}
// Success
logger.Debug("Check type updated successfully", "check_type", checkType)
break
}
// Check type is the same, no need to update
logger.Debug("Check type already registered", "check_type", checkType)
break
}
return nil
}
func (r *Runner) shouldRetry(err error, logger logging.Logger, attempt int, checkType string) bool {
logger.Debug("Error storing check type", "error", err, "attempt", attempt)
if isAPIServerShuttingDown(err, logger) {
return false
}
if attempt == r.retryAttempts-1 {
logger.Error("Unable to register check type", "check_type", checkType, "error", err)
return false
}
// Calculate exponential backoff delay: baseDelay * 2^attempt
delay := r.retryDelay * time.Duration(1<<attempt)
time.Sleep(delay)
return true
}
func (r *Runner) create(ctx context.Context, log logging.Logger, obj resource.Object) error {
id := obj.GetStaticMetadata().Identifier()
_, err := r.client.Create(ctx, id, obj, resource.CreateOptions{})
if err != nil {
return err
}
log.Debug("Check type created successfully", "identifier", id)
return nil
}
func (r *Runner) needsUpdate(current, newObj resource.Object, log logging.Logger) bool {
needsUpdate := false
// Check if the object annotations exist in the current object
currentAnnotations := current.GetAnnotations()
if currentAnnotations == nil {
currentAnnotations = make(map[string]string)
}
annotations := newObj.GetAnnotations()
for k, v := range annotations {
if currentAnnotations[k] != v {
needsUpdate = true
}
}
// Compare checktype spec steps with current steps
currentCheckType := current.(*advisorv0alpha1.CheckType)
newCheckType := newObj.(*advisorv0alpha1.CheckType)
newSteps := newCheckType.Spec.Steps
currentSteps := currentCheckType.Spec.Steps
if !cmp.Equal(newSteps, currentSteps, cmpopts.SortSlices(func(a, b advisorv0alpha1.CheckTypeStep) bool {
return a.StepID < b.StepID
})) {
log.Debug("Check type step mismatch, updating", "identifier", newObj.GetStaticMetadata().Identifier())
needsUpdate = true
}
return needsUpdate
}
func (r *Runner) update(ctx context.Context, log logging.Logger, obj resource.Object, current resource.Object) error {
id := obj.GetStaticMetadata().Identifier()
log.Debug("Updating check type", "identifier", id)
currentAnnotations := current.GetAnnotations()
if currentAnnotations == nil {
currentAnnotations = make(map[string]string)
}
annotations := obj.GetAnnotations()
maps.Copy(currentAnnotations, annotations)
obj.SetAnnotations(currentAnnotations) // This will update the annotations in the object
_, err := r.client.Update(ctx, id, obj, resource.UpdateOptions{})
if err != nil && !errors.IsAlreadyExists(err) {
// Ignore the error, it's probably due to a race condition
log.Info("Error updating check type, ignoring", "error", err)
}
log.Debug("Check type updated successfully", "identifier", id)
return nil
}
func isAPIServerShuttingDown(err error, logger logging.Logger) bool {
if strings.Contains(err.Error(), "apiserver is shutting down") {
logger.Debug("Error creating check type, not retrying", "error", err)
return true
}
return false
}

View File

@ -16,6 +16,54 @@ import (
)
func TestCheckTypesRegisterer_Run(t *testing.T) {
newMockCheck := &mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
}
existingObjectDifferentAnnotations := &advisorv0alpha1.CheckType{
ObjectMeta: metav1.ObjectMeta{
Name: "check1",
Annotations: map[string]string{
checks.NameAnnotation: "existing-name", // Different to trigger update
},
},
Spec: advisorv0alpha1.CheckTypeSpec{
Name: "check1",
Steps: []advisorv0alpha1.CheckTypeStep{
{StepID: "step1", Title: "Step 1", Description: "Description 1"},
},
},
}
existingObjectDifferentSteps := &advisorv0alpha1.CheckType{
ObjectMeta: metav1.ObjectMeta{
Name: "check1",
Annotations: map[string]string{
checks.NameAnnotation: "mock", // Same as check name
},
},
Spec: advisorv0alpha1.CheckTypeSpec{
Name: "check1",
Steps: []advisorv0alpha1.CheckTypeStep{
{StepID: "step2", Title: "Step 2", Description: "Description 2"}, // Different step
},
},
}
existingObjectSameContent := &advisorv0alpha1.CheckType{
ObjectMeta: metav1.ObjectMeta{
Name: "check1",
Annotations: map[string]string{
checks.NameAnnotation: "mock", // Same as check name
},
},
Spec: advisorv0alpha1.CheckTypeSpec{
Name: "check1",
Steps: []advisorv0alpha1.CheckTypeStep{
{StepID: "step1", Title: "Step 1", Description: "Description 1"},
},
},
}
tests := []struct {
name string
checks []checks.Check
@ -26,13 +74,9 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
}{
{
name: "successful create",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return nil, k8sErrs.NewNotFound(schema.GroupResource{}, id.Name)
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return obj, nil
@ -41,17 +85,10 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
expectedErr: nil,
},
{
name: "create already exists, successful update",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
name: "resource exists with different annotations, should update",
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return existingObjectDifferentAnnotations, nil
},
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
return obj, nil
@ -59,27 +96,32 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
expectedErr: nil,
},
{
name: "create already exists, with custom annotations",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
},
name: "resource exists with different steps, should update",
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return &advisorv0alpha1.CheckType{
ObjectMeta: metav1.ObjectMeta{
Name: "check1",
Annotations: map[string]string{
checks.IgnoreStepsAnnotationList: "step1",
return existingObjectDifferentSteps, nil
},
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
return obj, nil
},
}, nil
expectedErr: nil,
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
{
name: "resource exists with same annotations and steps, should not update",
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return existingObjectSameContent, nil
},
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
return nil, errors.New("updateFunc should not be called")
},
expectedErr: nil,
},
{
name: "resource exists, with custom annotations preserved",
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return existingObjectDifferentAnnotations, nil
},
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
if obj.GetAnnotations()[checks.IgnoreStepsAnnotationList] != "step1" {
@ -91,13 +133,9 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
},
{
name: "create error",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return nil, k8sErrs.NewNotFound(schema.GroupResource{}, id.Name)
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return nil, errors.New("create error")
@ -107,16 +145,9 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
},
{
name: "update error",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return existingObjectDifferentAnnotations, nil
},
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
return nil, errors.New("update error")
@ -125,16 +156,9 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
},
{
name: "shutting down error",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return existingObjectDifferentAnnotations, nil
},
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
return nil, errors.New("apiserver is shutting down")
@ -143,13 +167,9 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
},
{
name: "custom namespace",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return existingObjectDifferentAnnotations, nil
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
if obj.GetNamespace() != "custom-namespace" {
@ -262,13 +282,19 @@ func (m *mockClient) Get(ctx context.Context, id resource.Identifier) (resource.
if m.getFunc != nil {
return m.getFunc(ctx, id)
}
return advisorv0alpha1.CheckTypeKind().ZeroValue(), nil
return nil, errors.New("not implemented")
}
func (m *mockClient) Create(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
if m.createFunc != nil {
return m.createFunc(ctx, id, obj, opts)
}
return nil, errors.New("not implemented")
}
func (m *mockClient) Update(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
if m.updateFunc != nil {
return m.updateFunc(ctx, id, obj, opts)
}
return nil, errors.New("not implemented")
}

View File

@ -7,14 +7,18 @@ import (
"slices"
"strings"
"sync"
"time"
"github.com/grafana/grafana-app-sdk/logging"
"github.com/grafana/grafana-app-sdk/resource"
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
"github.com/grafana/grafana/pkg/services/contexthandler"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
)
var retryAnnotationPollingInterval = 1 * time.Second
func getCheck(obj resource.Object, checkMap map[string]checks.Check) (checks.Check, error) {
labels := obj.GetLabels()
objTypeLabel, ok := labels[checks.TypeLabel]
@ -78,7 +82,11 @@ func processCheck(ctx context.Context, log logging.Logger, client resource.Clien
}
return fmt.Errorf("error running steps: %w", err)
}
// Wait for the item to be persisted before patching the object
err = waitForItem(ctx, log, client, obj)
if err != nil {
return err
}
report := &advisorv0alpha1.CheckReport{
Failures: failures,
Count: int64(len(items)),
@ -100,13 +108,17 @@ func processCheckRetry(ctx context.Context, log logging.Logger, client resource.
status := checks.GetStatusAnnotation(obj)
if status == "" || status == checks.StatusAnnotationError {
// Check not processed yet or errored
log.Debug("Check not processed yet or errored, skipping retry", "check", obj.GetName(), "status", status)
return nil
}
// Get the item to retry from the annotation
itemToRetry := checks.GetRetryAnnotation(obj)
if itemToRetry == "" {
// No item to retry, nothing to do
log.Debug("No item to retry, skipping retry", "check", obj.GetName())
return nil
} else {
log.Debug("Item to retry found", "check", obj.GetName(), "item", itemToRetry)
}
c, ok := obj.(*advisorv0alpha1.Check)
if !ok {
@ -165,14 +177,23 @@ func processCheckRetry(ctx context.Context, log logging.Logger, client resource.
// Failure not in the list of items to retry, keep it
return false
})
// Wait for the retry annotation to be persisted before patching the object
err = waitForRetryAnnotation(ctx, log, client, obj, itemToRetry)
if err != nil {
return err
}
// Set the status
err = checks.SetStatus(ctx, client, obj, c.Status)
log.Debug("Status set", "check", obj.GetName(), "status.count", c.Status.Report.Count)
if err != nil {
return err
}
// Delete the retry annotation to mark the check as processed
annotations := checks.DeleteAnnotations(ctx, obj, []string{checks.RetryAnnotation})
err = checks.SetAnnotations(ctx, client, obj, annotations)
log.Debug("Annotations set", "check", obj.GetName(), "annotations", annotations)
return checks.SetAnnotations(ctx, client, obj, annotations)
return err
}
func runStepsInParallel(ctx context.Context, log logging.Logger, spec *advisorv0alpha1.CheckSpec, steps []checks.Step, items []any) ([]advisorv0alpha1.CheckReportFailure, error) {
@ -234,3 +255,65 @@ func filterSteps(checkType resource.Object, steps []checks.Step) ([]checks.Step,
}
return steps, nil
}
// retryAnnotationChanged compares the retry annotation between old and new objects
func retryAnnotationChanged(oldObj, newObj resource.Object) bool {
if oldObj == nil || newObj == nil {
return true // If either is nil, consider it changed
}
// Compare annotations
oldAnnotations := oldObj.GetAnnotations()
newAnnotations := newObj.GetAnnotations()
return newAnnotations[checks.RetryAnnotation] != "" &&
oldAnnotations[checks.RetryAnnotation] != newAnnotations[checks.RetryAnnotation]
}
func waitForItem(ctx context.Context, log logging.Logger, client resource.Client, obj resource.Object) error {
_, err := client.Get(ctx, resource.Identifier{
Namespace: obj.GetNamespace(),
Name: obj.GetName(),
})
retries := 0
for err != nil && k8serrors.IsNotFound(err) && retries < 5 {
log.Debug("Waiting for item to be persisted", "check", obj.GetName(), "retries", retries)
time.Sleep(retryAnnotationPollingInterval)
retries++
_, err = client.Get(ctx, resource.Identifier{
Namespace: obj.GetNamespace(),
Name: obj.GetName(),
})
}
return err
}
// waitForRetryAnnotation waits for the retry annotation to match the item to retry
func waitForRetryAnnotation(ctx context.Context, log logging.Logger, client resource.Client, obj resource.Object, itemToRetry string) error {
currentObj, err := client.Get(ctx, resource.Identifier{
Namespace: obj.GetNamespace(),
Name: obj.GetName(),
})
if err != nil {
return err
}
retries := 0
currentRetryAnnotation := checks.GetRetryAnnotation(currentObj)
for currentRetryAnnotation != itemToRetry {
log.Debug("Waiting for retry annotation to be persisted", "check", obj.GetName(), "item", itemToRetry, "currentRetryAnnotation", currentRetryAnnotation)
time.Sleep(retryAnnotationPollingInterval)
retries++
if retries > 5 {
return fmt.Errorf("timeout waiting for retry annotation to be persisted")
}
currentObj, err = client.Get(ctx, resource.Identifier{
Namespace: obj.GetNamespace(),
Name: obj.GetName(),
})
if err != nil {
return err
}
currentRetryAnnotation = checks.GetRetryAnnotation(currentObj)
}
log.Debug("Retry annotation persisted", "check", obj.GetName(), "item", itemToRetry)
return nil
}

View File

@ -6,6 +6,7 @@ import (
"fmt"
"net/http"
"testing"
"time"
"github.com/grafana/grafana-app-sdk/logging"
"github.com/grafana/grafana-app-sdk/resource"
@ -249,7 +250,9 @@ func TestProcessCheckRetry_SkipMissingItem(t *testing.T) {
t.Fatal(err)
}
meta.SetCreatedBy("user:1")
client := &mockClient{}
client := &mockClient{
res: obj,
}
typesClient := &mockTypesClient{}
ctx := context.TODO()
@ -281,7 +284,9 @@ func TestProcessCheckRetry_Success(t *testing.T) {
t.Fatal(err)
}
meta.SetCreatedBy("user:1")
client := &mockClient{}
client := &mockClient{
res: obj,
}
typesClient := &mockTypesClient{}
ctx := context.TODO()
@ -296,6 +301,51 @@ func TestProcessCheckRetry_Success(t *testing.T) {
assert.Empty(t, obj.Status.Report.Failures)
}
func TestProcessCheckRetry_Success_Polling(t *testing.T) {
retryAnnotationPollingInterval = 1 * time.Millisecond
obj := &advisorv0alpha1.Check{}
obj.SetAnnotations(map[string]string{
checks.RetryAnnotation: "item",
checks.StatusAnnotation: checks.StatusAnnotationProcessed,
})
obj.Status.Report.Failures = []advisorv0alpha1.CheckReportFailure{
{
ItemID: "item",
StepID: "step",
},
}
meta, err := utils.MetaAccessor(obj)
if err != nil {
t.Fatal(err)
}
meta.SetCreatedBy("user:1")
retryCount := 0
client := &mockClient{
get: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
if retryCount > 0 {
// obj contains the retry annotation
return obj, nil
}
retryCount++
oldObject := &advisorv0alpha1.Check{}
oldObject.SetAnnotations(map[string]string{
checks.RetryAnnotation: "",
})
return oldObject, nil
},
}
typesClient := &mockTypesClient{}
ctx := context.TODO()
check := &mockCheck{
items: []any{"item"},
}
err = processCheckRetry(ctx, logging.DefaultLogger, client, typesClient, obj, check)
assert.NoError(t, err)
assert.Equal(t, 1, retryCount)
}
func TestRunStepsInParallel_ConcurrentHeaderAccess(t *testing.T) {
// Create an HTTP request with headers to simulate the real scenario
req, err := http.NewRequest("GET", "/test", nil)
@ -360,6 +410,8 @@ func TestRunStepsInParallel_ConcurrentHeaderAccess(t *testing.T) {
type mockClient struct {
resource.Client
values []any
res resource.Object
get func(ctx context.Context, id resource.Identifier) (resource.Object, error)
}
func (m *mockClient) PatchInto(ctx context.Context, id resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions, obj resource.Object) error {
@ -368,6 +420,13 @@ func (m *mockClient) PatchInto(ctx context.Context, id resource.Identifier, req
return nil
}
func (m *mockClient) Get(ctx context.Context, id resource.Identifier) (resource.Object, error) {
if m.get != nil {
return m.get(ctx, id)
}
return m.res, nil
}
type mockTypesClient struct {
resource.Client
res resource.Object

View File

@ -0,0 +1,39 @@
module github.com/grafana/grafana/apps/alerting/alertenrichment
go 1.24.6
require (
github.com/grafana/grafana-app-sdk v0.46.0
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250901080157-a0280d701b28
k8s.io/apimachinery v0.34.1
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
)
require (
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/x448/float16 v0.8.4 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/net v0.44.0 // indirect
golang.org/x/text v0.29.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
)

View File

@ -0,0 +1,118 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250901080157-a0280d701b28 h1:PgMfX4OPENz/iXmtDDIW9+poZY4UD0hhmXm7flVclDo=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250901080157-a0280d701b28/go.mod h1:av5N0Naq+8VV9MLF7zAkihy/mVq5UbS2EvRSJukDHlY=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@ -0,0 +1,24 @@
package v1beta1
import (
"encoding/json"
"io"
"github.com/grafana/grafana-app-sdk/resource"
)
// AlertEnrichmentJSONCodec is a JSON codec for AlertEnrichment resources
type AlertEnrichmentJSONCodec struct{}
// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into`
func (*AlertEnrichmentJSONCodec) Read(reader io.Reader, into resource.Object) error {
return json.NewDecoder(reader).Decode(into)
}
// Write writes JSON-encoded bytes into `writer` marshaled from `from`
func (*AlertEnrichmentJSONCodec) Write(writer io.Writer, from resource.Object) error {
return json.NewEncoder(writer).Encode(from)
}
// Interface compliance checks
var _ resource.Codec = &AlertEnrichmentJSONCodec{}

View File

@ -0,0 +1,18 @@
package v1beta1
import "k8s.io/apimachinery/pkg/runtime/schema"
const (
// APIGroup is the API group used by all kinds in this package
APIGroup = "alertenrichment.grafana.app"
// APIVersion is the API version used by all kinds in this package
APIVersion = "v1beta1"
)
var (
// GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package
GroupVersion = schema.GroupVersion{
Group: APIGroup,
Version: APIVersion,
}
)

View File

@ -0,0 +1,6 @@
// +k8s:deepcopy-gen=package
// +k8s:openapi-gen=true
// +k8s:defaulter-gen=TypeMeta
// +groupName=alertenrichment.grafana.app
package v1beta1

View File

@ -0,0 +1,207 @@
package v1beta1
import (
"fmt"
"time"
"github.com/grafana/grafana-app-sdk/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
)
// App Platform resource.Object interface methods for AlertEnrichment
func (o *AlertEnrichment) GetSpec() any {
return o.Spec
}
func (o *AlertEnrichment) SetSpec(spec any) error {
cast, ok := spec.(AlertEnrichmentSpec)
if !ok {
return fmt.Errorf("cannot set spec type %#v, not of type AlertEnrichmentSpec", spec)
}
o.Spec = cast
return nil
}
func (o *AlertEnrichment) GetSubresources() map[string]any {
return map[string]any{}
}
func (o *AlertEnrichment) GetSubresource(name string) (any, bool) {
return nil, false
}
func (o *AlertEnrichment) SetSubresource(name string, value any) error {
return fmt.Errorf("subresource %s does not exist", name)
}
func (o *AlertEnrichment) Copy() resource.Object {
return resource.CopyObject(o)
}
func (o *AlertEnrichment) GetStaticMetadata() resource.StaticMetadata {
gvk := o.GroupVersionKind()
return resource.StaticMetadata{
Name: o.Name,
Namespace: o.Namespace,
Group: gvk.Group,
Version: gvk.Version,
Kind: gvk.Kind,
}
}
func (o *AlertEnrichment) SetStaticMetadata(metadata resource.StaticMetadata) {
o.Name = metadata.Name
o.Namespace = metadata.Namespace
o.SetGroupVersionKind(schema.GroupVersionKind{
Group: metadata.Group,
Version: metadata.Version,
Kind: metadata.Kind,
})
}
func (o *AlertEnrichment) GetCommonMetadata() resource.CommonMetadata {
dt := o.DeletionTimestamp
var deletionTimestamp *time.Time
if dt != nil {
deletionTimestamp = &dt.Time
}
// Legacy ExtraFields support
extraFields := make(map[string]any)
if o.Annotations != nil {
extraFields["annotations"] = o.Annotations
}
if o.ManagedFields != nil {
extraFields["managedFields"] = o.ManagedFields
}
if o.OwnerReferences != nil {
extraFields["ownerReferences"] = o.OwnerReferences
}
return resource.CommonMetadata{
UID: string(o.UID),
ResourceVersion: o.ResourceVersion,
Generation: o.Generation,
Labels: o.Labels,
CreationTimestamp: o.CreationTimestamp.Time,
DeletionTimestamp: deletionTimestamp,
Finalizers: o.Finalizers,
UpdateTimestamp: o.GetUpdateTimestamp(),
CreatedBy: o.GetCreatedBy(),
UpdatedBy: o.GetUpdatedBy(),
ExtraFields: extraFields,
}
}
func (o *AlertEnrichment) SetCommonMetadata(metadata resource.CommonMetadata) {
o.UID = types.UID(metadata.UID)
o.ResourceVersion = metadata.ResourceVersion
o.Generation = metadata.Generation
o.Labels = metadata.Labels
o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp)
if metadata.DeletionTimestamp != nil {
dt := metav1.NewTime(*metadata.DeletionTimestamp)
o.DeletionTimestamp = &dt
} else {
o.DeletionTimestamp = nil
}
o.Finalizers = metadata.Finalizers
if o.Annotations == nil {
o.Annotations = make(map[string]string)
}
if !metadata.UpdateTimestamp.IsZero() {
o.SetUpdateTimestamp(metadata.UpdateTimestamp)
}
if metadata.CreatedBy != "" {
o.SetCreatedBy(metadata.CreatedBy)
}
if metadata.UpdatedBy != "" {
o.SetUpdatedBy(metadata.UpdatedBy)
}
// Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields
if metadata.ExtraFields != nil {
if annotations, ok := metadata.ExtraFields["annotations"].(map[string]string); ok {
o.Annotations = annotations
}
if managedFields, ok := metadata.ExtraFields["managedFields"].([]metav1.ManagedFieldsEntry); ok {
o.ManagedFields = managedFields
}
if ownerReferences, ok := metadata.ExtraFields["ownerReferences"].([]metav1.OwnerReference); ok {
o.OwnerReferences = ownerReferences
}
}
}
func (o *AlertEnrichment) GetCreatedBy() string {
if o.Annotations == nil {
o.Annotations = make(map[string]string)
}
return o.Annotations["grafana.com/createdBy"]
}
func (o *AlertEnrichment) SetCreatedBy(createdBy string) {
if o.Annotations == nil {
o.Annotations = make(map[string]string)
}
o.Annotations["grafana.com/createdBy"] = createdBy
}
func (o *AlertEnrichment) GetUpdateTimestamp() time.Time {
if o.Annotations == nil {
o.Annotations = make(map[string]string)
}
parsed, _ := time.Parse(time.RFC3339, o.Annotations["grafana.com/updateTimestamp"])
return parsed
}
func (o *AlertEnrichment) SetUpdateTimestamp(updateTimestamp time.Time) {
if o.Annotations == nil {
o.Annotations = make(map[string]string)
}
o.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339)
}
func (o *AlertEnrichment) GetUpdatedBy() string {
if o.Annotations == nil {
o.Annotations = make(map[string]string)
}
return o.Annotations["grafana.com/updatedBy"]
}
func (o *AlertEnrichment) SetUpdatedBy(updatedBy string) {
if o.Annotations == nil {
o.Annotations = make(map[string]string)
}
o.Annotations["grafana.com/updatedBy"] = updatedBy
}
// AlertEnrichmentList also needs to implement resource.ListObject
func (o *AlertEnrichmentList) Copy() resource.ListObject {
cpy := &AlertEnrichmentList{
TypeMeta: o.TypeMeta,
Items: make([]AlertEnrichment, len(o.Items)),
}
o.ListMeta.DeepCopyInto(&cpy.ListMeta)
for i := 0; i < len(o.Items); i++ {
o.Items[i].DeepCopyInto(&cpy.Items[i])
}
return cpy
}
func (o *AlertEnrichmentList) GetItems() []resource.Object {
items := make([]resource.Object, len(o.Items))
for i, item := range o.Items {
items[i] = &item
}
return items
}
func (o *AlertEnrichmentList) SetItems(items []resource.Object) {
o.Items = make([]AlertEnrichment, len(items))
for i, item := range items {
if ae, ok := item.(*AlertEnrichment); ok {
o.Items[i] = *ae
}
}
}

Some files were not shown because too many files have changed in this diff Show More