diff --git a/pkg/build/daggerbuild/e2e/validate_package.go b/pkg/build/daggerbuild/e2e/validate_package.go index c786b4a991b..c4da4aaf305 100644 --- a/pkg/build/daggerbuild/e2e/validate_package.go +++ b/pkg/build/daggerbuild/e2e/validate_package.go @@ -6,12 +6,14 @@ import ( ) func CypressImage(version string) string { - return "cypress/included:13.1.0" + return "cypress/included:13.10.0" } // CypressContainer returns a docker container with everything set up that is needed to build or run e2e tests. func CypressContainer(d *dagger.Client, base string) *dagger.Container { - container := d.Container().From(base).WithEntrypoint([]string{}) + container := d.Container().From(base).WithEntrypoint([]string{}). + WithExec([]string{"apt-get", "update", "-yq"}). + WithExec([]string{"apt-get", "install", "-yq", "make", "gcc", "g++"}) return container } diff --git a/pkg/build/daggerbuild/frontend/builder.go b/pkg/build/daggerbuild/frontend/builder.go index f2873770f3f..71d88ae10e2 100644 --- a/pkg/build/daggerbuild/frontend/builder.go +++ b/pkg/build/daggerbuild/frontend/builder.go @@ -30,6 +30,8 @@ func Builder(d *dagger.Client, platform dagger.Platform, src *dagger.Directory, }, }, ). + WithEnvVariable("NX_ISOLATE_PLUGINS", "false"). + WithEnvVariable("NX_DAEMON", "false"). WithWorkdir("/src") // TODO: Should figure out exactly what we can include without all the extras so we can take advantage of caching better. diff --git a/pkg/build/daggerbuild/frontend/npm.go b/pkg/build/daggerbuild/frontend/npm.go index 3a728f1a726..2c7241fb164 100644 --- a/pkg/build/daggerbuild/frontend/npm.go +++ b/pkg/build/daggerbuild/frontend/npm.go @@ -12,22 +12,14 @@ import ( // NPMPackages versions and packs the npm packages into tarballs into `npm-packages` directory. // It then returns the npm-packages directory as a dagger.Directory. func NPMPackages(builder *dagger.Container, d *dagger.Client, log *slog.Logger, src *dagger.Directory, ersion string) (*dagger.Directory, error) { - // Check if the version of Grafana uses lerna or nx to manage package versioning. var ( out = fmt.Sprintf("/src/npm-packages/%%s-%v.tgz", "v"+ersion) - - lernaBuild = fmt.Sprintf("yarn run packages:build && yarn lerna version %s --exact --no-git-tag-version --no-push --force-publish -y", ersion) - lernaPack = fmt.Sprintf("yarn lerna exec --no-private -- yarn pack --out %s", out) - - nxBuild = fmt.Sprintf("yarn run packages:build && yarn nx release version %s --no-git-commit --no-git-tag --no-stage-changes --group grafanaPackages", ersion) - nxPack = fmt.Sprintf("yarn nx exec --projects=$(cat nx.json | jq -r '.release.groups.grafanaPackages.projects | join(\",\")') -- yarn pack --out %s", out) ) return builder.WithExec([]string{"mkdir", "npm-packages"}). WithEnvVariable("SHELL", "/bin/bash"). WithExec([]string{"yarn", "install", "--immutable"}). - WithExec([]string{"/bin/bash", "-c", fmt.Sprintf("if [ -f lerna.json ]; then %s; else %s; fi", lernaBuild, nxBuild)}). - WithExec([]string{"/bin/bash", "-c", fmt.Sprintf("if [ -f lerna.json ]; then %s; else %s; fi", lernaPack, nxPack)}). + WithExec([]string{"/bin/bash", "-c", fmt.Sprintf("yarn run packages:build && yarn lerna version %s --exact --no-git-tag-version --no-push --force-publish -y && yarn lerna exec --no-private -- yarn pack --out %s", ersion, out)}). Directory("./npm-packages"), nil } diff --git a/scripts/generate-rtk-apis.ts b/scripts/generate-rtk-apis.ts index 792594a91e1..850d32b7477 100644 --- a/scripts/generate-rtk-apis.ts +++ b/scripts/generate-rtk-apis.ts @@ -30,8 +30,6 @@ const config: ConfigFile = { 'getDashboardByUid', 'getLibraryElementByUid', - - 'getResourceDependencies', ], }, '../public/app/features/preferences/api/user/endpoints.gen.ts': { @@ -41,45 +39,25 @@ const config: ConfigFile = { apiImport: 'baseAPI', filterEndpoints: ['getUserPreferences', 'updateUserPreferences', 'patchUserPreferences'], }, - '../public/app/api/clients/iam/v0alpha1/endpoints.gen.ts': { + '../public/app/features/iam/api/endpoints.gen.ts': { schemaFile: '../data/openapi/iam.grafana.app-v0alpha1.json', - apiFile: '../public/app/api/clients/iam/v0alpha1/baseAPI.ts', + apiFile: '../public/app/features/iam/api/api.ts', + apiImport: 'iamApi', filterEndpoints: ['getDisplayMapping'], + exportName: 'generatedIamApi', + flattenArg: false, tag: true, }, - '../public/app/api/clients/provisioning/v0alpha1/endpoints.gen.ts': { - apiFile: '../public/app/api/clients/provisioning/v0alpha1/baseAPI.ts', + '../public/app/features/provisioning/api/endpoints.gen.ts': { + apiFile: '../public/app/features/provisioning/api/baseAPI.ts', schemaFile: '../data/openapi/provisioning.grafana.app-v0alpha1.json', + apiImport: 'baseAPI', filterEndpoints, + argSuffix: 'Arg', + responseSuffix: 'Response', tag: true, hooks: true, }, - '../public/app/api/clients/folder/v1beta1/endpoints.gen.ts': { - apiFile: '../public/app/api/clients/folder/v1beta1/baseAPI.ts', - schemaFile: '../data/openapi/folder.grafana.app-v1beta1.json', - tag: true, - }, - '../public/app/api/clients/advisor/v0alpha1/endpoints.gen.ts': { - apiFile: '../public/app/api/clients/advisor/v0alpha1/baseAPI.ts', - schemaFile: '../data/openapi/advisor.grafana.app-v0alpha1.json', - filterEndpoints: [ - 'createCheck', - 'getCheck', - 'listCheck', - 'deleteCheck', - 'updateCheck', - 'listCheckType', - 'updateCheckType', - ], - tag: true, - }, - '../public/app/api/clients/playlist/v0alpha1/endpoints.gen.ts': { - apiFile: '../public/app/api/clients/playlist/v0alpha1/baseAPI.ts', - schemaFile: '../data/openapi/playlist.grafana.app-v0alpha1.json', - filterEndpoints: ['listPlaylist', 'getPlaylist', 'createPlaylist', 'deletePlaylist', 'replacePlaylist'], - tag: true, - }, - // PLOP_INJECT_API_CLIENT - Used by the API client generator }, }; diff --git a/scripts/grafana-server/custom.ini b/scripts/grafana-server/custom.ini index 68968c502fa..98a518778ec 100644 --- a/scripts/grafana-server/custom.ini +++ b/scripts/grafana-server/custom.ini @@ -5,13 +5,7 @@ content_security_policy_template = """require-trusted-types-for 'script'; script enable_frontend_sandbox_for_plugins = sandbox-app-test,sandbox-test-datasource,sandbox-test-panel [feature_toggles] -publicDashboards=true -grafanaAPIServer=true -queryLibrary=true -queryService=true - -[environment] -stack_id = 12345 +enable = publicDashboards [plugins] allow_loading_unsigned_plugins=grafana-extensionstest-app,grafana-extensionexample1-app,grafana-extensionexample2-app,grafana-extensionexample3-app,grafana-e2etest-datasource @@ -25,6 +19,3 @@ max_open_conn = 2 [smtp] enabled = true host = localhost:7777 - -[cloud_migration] -developer_mode = true ; Enable developer mode to use in-memory implementations of 3rdparty services needed. diff --git a/scripts/grafana-server/kill-server b/scripts/grafana-server/kill-server index 702626ecb3c..7b9e38cda78 100755 --- a/scripts/grafana-server/kill-server +++ b/scripts/grafana-server/kill-server @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/bash . scripts/grafana-server/variables diff --git a/scripts/grafana-server/start-server b/scripts/grafana-server/start-server index 6162439a299..fdb4499697e 100755 --- a/scripts/grafana-server/start-server +++ b/scripts/grafana-server/start-server @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/bash set -eo pipefail . scripts/grafana-server/variables diff --git a/scripts/grafana-server/variables b/scripts/grafana-server/variables index b61d316b41c..541c4f21f69 100644 --- a/scripts/grafana-server/variables +++ b/scripts/grafana-server/variables @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/bash DEFAULT_RUNDIR=scripts/grafana-server/tmp RUNDIR=${RUNDIR:-$DEFAULT_RUNDIR} diff --git a/scripts/grafana-server/wait-for-grafana b/scripts/grafana-server/wait-for-grafana index 54ff393b7a8..a77972fb167 100755 --- a/scripts/grafana-server/wait-for-grafana +++ b/scripts/grafana-server/wait-for-grafana @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/bash set -eo pipefail . scripts/grafana-server/variables diff --git a/scripts/prepare-npm-package.js b/scripts/prepare-npm-package.js index 714e4691981..212b4f89487 100644 --- a/scripts/prepare-npm-package.js +++ b/scripts/prepare-npm-package.js @@ -1,5 +1,6 @@ import PackageJson from '@npmcli/package-json'; import { mkdir } from 'node:fs/promises'; +import { join, dirname } from 'node:path'; const cwd = process.cwd(); @@ -7,17 +8,18 @@ try { const pkgJson = await PackageJson.load(cwd); const cjsIndex = pkgJson.content.publishConfig?.main ?? pkgJson.content.main; const esmIndex = pkgJson.content.publishConfig?.module ?? pkgJson.content.module; - const typesIndex = pkgJson.content.publishConfig?.types ?? pkgJson.content.types; + const cjsTypes = pkgJson.content.publishConfig?.types ?? pkgJson.content.types; + const esmTypes = `./${join(dirname(esmIndex), 'index.d.mts')}`; const exports = { './package.json': './package.json', '.': { import: { - types: typesIndex, + types: esmTypes, default: esmIndex, }, require: { - types: typesIndex, + types: cjsTypes, default: cjsIndex, }, }, @@ -31,17 +33,9 @@ try { }; } - // Fix for @grafana/i18n so eslint-plugin can be imported by consumers - if (pkgJson.content.name === '@grafana/i18n') { - exports['./eslint-plugin'] = { - import: './dist/eslint/index.cjs', - require: './dist/eslint/index.cjs', - }; - } - pkgJson.update({ main: cjsIndex, - types: typesIndex, + types: cjsTypes, module: esmIndex, exports, }); @@ -58,12 +52,12 @@ try { ...pkgJson.content.exports, [`./${aliasName}`]: { import: { - types: typesIndex.replace('index', aliasName), + types: esmTypes.replace('index', aliasName), default: esmIndex.replace('index', aliasName), }, require: { - types: typesIndex.replace('index', aliasName), - default: cjsIndex.replace('index', aliasName), + types: cjsTypes.replace('index', aliasName), + default: cjsTypes.replace('index', aliasName), }, }, }, @@ -86,7 +80,7 @@ async function createAliasPackageJsonFiles(packageJsonContent, aliasName) { const pkgJson = await PackageJson.create(pkgJsonPath, { data: { name: pkgName, - types: `../dist/types/${aliasName}.d.ts`, + types: `../dist/cjs/${aliasName}.d.cts`, main: `../dist/cjs/${aliasName}.cjs`, module: `../dist/esm/${aliasName}.mjs`, }, diff --git a/scripts/webpack/env-util.js b/scripts/webpack/env-util.js index c963f476fee..5d49fd4c8dc 100644 --- a/scripts/webpack/env-util.js +++ b/scripts/webpack/env-util.js @@ -1,4 +1,3 @@ -// @ts-check const { parse } = require('ini'); const { readFileSync, existsSync } = require('node:fs'); const path = require('path'); @@ -19,17 +18,14 @@ const getEnvConfig = () => { const custom = parse(customSettings); const merged = { ...defaults.frontend_dev, ...custom.frontend_dev }; - // Take all frontend keys from the ini file and prefix with `frontend_dev_`, // so they can be added to `process.env` elsewhere - /** @type {Record} */ - const env = {}; - - for (const [key, value] of Object.entries(merged)) { - env[`frontend_dev_${key}`] = value; - } - - return env; + return Object.entries(merged).reduce((acc, [key, value]) => { + return { + ...acc, + [`frontend_dev_${key}`]: value, + }; + }, {}); }; module.exports = getEnvConfig; diff --git a/scripts/webpack/sass.rule.js b/scripts/webpack/sass.rule.js index 9ca29cde4af..36290bb3adc 100644 --- a/scripts/webpack/sass.rule.js +++ b/scripts/webpack/sass.rule.js @@ -5,14 +5,10 @@ const path = require('path'); module.exports = function (options) { return { - test: /\.(sa|sc|c)ss$/, + test: /\.scss$/, + exclude: /node_modules/, use: [ - { - loader: MiniCssExtractPlugin.loader, - options: { - publicPath: './', - }, - }, + MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { @@ -34,10 +30,6 @@ module.exports = function (options) { loader: 'sass-loader', options: { sourceMap: options.sourceMap, - sassOptions: { - // silencing these warnings since we're planning to remove sass when angular is gone - silenceDeprecations: ['import', 'global-builtin'], - }, }, }, ], diff --git a/scripts/webpack/webpack.common.js b/scripts/webpack/webpack.common.js index 8d847f40813..0692e4355ac 100644 --- a/scripts/webpack/webpack.common.js +++ b/scripts/webpack/webpack.common.js @@ -1,4 +1,3 @@ -const CopyWebpackPlugin = require('copy-webpack-plugin'); const path = require('path'); const webpack = require('webpack'); @@ -10,10 +9,6 @@ module.exports = { app: './public/app/index.ts', swagger: './public/swagger/index.tsx', }, - experiments: { - // Required to load WASM modules. - asyncWebAssembly: true, - }, output: { clean: true, path: path.resolve(__dirname, '../../public/build'), @@ -27,6 +22,9 @@ module.exports = { // some of data source plugins use global Prism object to add the language definition // we want to have same Prism object in core and in grafana/ui prismjs: require.resolve('prismjs'), + // some sub-dependencies use a different version of @emotion/react and generate warnings + // in the browser about @emotion/react loaded twice. We want to only load it once + '@emotion/react': require.resolve('@emotion/react'), // due to our webpack configuration not understanding package.json `exports` // correctly we must alias this package to the correct file // the alternative to this alias is to copy-paste the file into our @@ -69,14 +67,6 @@ module.exports = { new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'], }), - new CopyWebpackPlugin({ - patterns: [ - { - from: 'public/img', - to: 'img', - }, - ], - }), ], module: { rules: [ @@ -106,11 +96,20 @@ module.exports = { }, ], }, + { + test: /\.css$/, + use: ['style-loader', 'css-loader'], + }, { test: /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/, type: 'asset/resource', generator: { filename: 'static/img/[name].[hash:8][ext]' }, }, + // for pre-caching SVGs as part of the JS bundles + { + test: /(unicons|mono|custom|solid)[\\/].*\.svg$/, + type: 'asset/source', + }, { // Required for msagl library (used in Nodegraph panel) to work test: /\.m?js$/, @@ -127,6 +126,12 @@ module.exports = { chunks: 'all', minChunks: 1, cacheGroups: { + unicons: { + test: /[\\/]node_modules[\\/]@iconscout[\\/]react-unicons[\\/].*[jt]sx?$/, + chunks: 'initial', + priority: 20, + enforce: true, + }, moment: { test: /[\\/]node_modules[\\/]moment[\\/].*[jt]sx?$/, chunks: 'initial', diff --git a/scripts/webpack/webpack.dev.js b/scripts/webpack/webpack.dev.js index 20c8c38c46c..82accfe50f0 100644 --- a/scripts/webpack/webpack.dev.js +++ b/scripts/webpack/webpack.dev.js @@ -4,12 +4,10 @@ const browserslist = require('browserslist'); const { resolveToEsbuildTarget } = require('esbuild-plugin-browserslist'); const ESLintPlugin = require('eslint-webpack-plugin'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); -const fs = require('fs'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const path = require('path'); const { DefinePlugin, EnvironmentPlugin } = require('webpack'); const WebpackAssetsManifest = require('webpack-assets-manifest'); -const LiveReloadPlugin = require('webpack-livereload-plugin'); const { merge } = require('webpack-merge'); const WebpackBar = require('webpackbar'); @@ -30,21 +28,6 @@ function getDecoupledPlugins() { return packages.filter((pkg) => pkg.dir.includes('plugins/datasource')).map((pkg) => `${pkg.dir}/**`); } -// When linking scenes for development, resolve the path to the src directory for sourcemaps -function scenesModule() { - const scenesPath = path.resolve('./node_modules/@grafana/scenes'); - try { - const status = fs.lstatSync(scenesPath); - if (status.isSymbolicLink()) { - console.log(`scenes is linked to local scenes repo`); - return path.resolve(scenesPath + '/src'); - } - } catch (error) { - console.error(`Error checking scenes path: ${error.message}`); - } - return scenesPath; -} - const envConfig = getEnvConfig(); module.exports = (env = {}) => { @@ -68,10 +51,14 @@ module.exports = (env = {}) => { // Packages linked for development need react to be resolved from the same location react: path.resolve('./node_modules/react'), + // Also Grafana packages need to be resolved from the same location so they share + // the same singletons + '@grafana/runtime': path.resolve(__dirname, '../../packages/grafana-runtime'), + '@grafana/data': path.resolve(__dirname, '../../packages/grafana-data'), + // This is required to correctly resolve react-router-dom when linking with // local version of @grafana/scenes 'react-router-dom': path.resolve('./node_modules/react-router-dom'), - '@grafana/scenes': scenesModule(), }, }, @@ -87,7 +74,7 @@ module.exports = (env = {}) => { }, require('./sass.rule.js')({ sourceMap: false, - preserveUrl: true, + preserveUrl: false, }), ], }, @@ -118,24 +105,13 @@ module.exports = (env = {}) => { }, plugins: [ - ...(parseInt(env.liveReload, 10) - ? [ - new LiveReloadPlugin({ - appendScriptTag: true, - useSourceHash: true, - hostname: 'localhost', - protocol: 'http', - port: 35750, - }), - ] - : []), parseInt(env.noTsCheck, 10) ? new DefinePlugin({}) // bogus plugin to satisfy webpack API : new ForkTsCheckerWebpackPlugin({ async: true, // don't block webpack emit typescript: { mode: 'write-references', - memoryLimit: 5096, + memoryLimit: 4096, diagnosticOptions: { semantic: true, syntactic: true, @@ -148,7 +124,6 @@ module.exports = (env = {}) => { cache: true, lintDirtyModulesOnly: true, // don't lint on start, only lint changed files extensions: ['.ts', '.tsx'], - configType: 'flat', }), new MiniCssExtractPlugin({ filename: 'grafana.[name].[contenthash].css', @@ -161,7 +136,6 @@ module.exports = (env = {}) => { new WebpackAssetsManifest({ entrypoints: true, integrity: true, - integrityHashes: ['sha384', 'sha512'], publicPath: true, }), new WebpackBar({ diff --git a/scripts/webpack/webpack.prod.js b/scripts/webpack/webpack.prod.js index c014f8b54d9..98768225963 100644 --- a/scripts/webpack/webpack.prod.js +++ b/scripts/webpack/webpack.prod.js @@ -10,10 +10,8 @@ const { EnvironmentPlugin } = require('webpack'); const WebpackAssetsManifest = require('webpack-assets-manifest'); const { WebpackManifestPlugin } = require('webpack-manifest-plugin'); const { merge } = require('webpack-merge'); -const { SubresourceIntegrityPlugin } = require('webpack-subresource-integrity'); const getEnvConfig = require('./env-util.js'); -const FeatureFlaggedSRIPlugin = require('./plugins/FeatureFlaggedSriPlugin'); const common = require('./webpack.common.js'); const esbuildTargets = resolveToEsbuildTarget(browserslist(), { printUnknownTargets: false }); @@ -49,13 +47,10 @@ module.exports = (env = {}) => }, require('./sass.rule.js')({ sourceMap: false, - preserveUrl: true, + preserveUrl: false, }), ], }, - output: { - crossOriginLoading: 'anonymous', - }, optimization: { nodeEnv: 'production', minimize: parseInt(env.noMinify, 10) !== 1, @@ -75,8 +70,6 @@ module.exports = (env = {}) => new MiniCssExtractPlugin({ filename: 'grafana.[name].[contenthash].css', }), - new SubresourceIntegrityPlugin(), - new FeatureFlaggedSRIPlugin(), /** * I know we have two manifest plugins here. * WebpackManifestPlugin was only used in prod before and does not support integrity hashes @@ -84,7 +77,6 @@ module.exports = (env = {}) => new WebpackAssetsManifest({ entrypoints: true, integrity: true, - integrityHashes: ['sha384', 'sha512'], publicPath: true, }), new WebpackManifestPlugin({