From 22713186cb776b23c724ee55f445053131c2e90d Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Wed, 26 Apr 2023 15:01:32 +0200 Subject: [PATCH] Toolkit: Remove `plugin:ci-build` `plugin:ci-package` `plugin:ci-report` and related files (#67212) --- .betterer.results | 28 -- packages/grafana-toolkit/package.json | 1 - packages/grafana-toolkit/src/cli/index.ts | 62 ----- .../src/cli/tasks/plugin.ci.ts | 258 ------------------ .../src/cli/tasks/plugin.update.ts | 24 -- .../src/cli/tasks/plugin/bundle.managed.ts | 34 --- packages/grafana-toolkit/src/plugins/env.ts | 137 ---------- packages/grafana-toolkit/src/plugins/index.ts | 3 - .../grafana-toolkit/src/plugins/manifest.ts | 91 ------ packages/grafana-toolkit/src/plugins/types.ts | 104 ------- packages/grafana-toolkit/src/plugins/utils.ts | 132 --------- .../grafana-toolkit/src/plugins/workflow.ts | 100 ------- yarn.lock | 10 - 13 files changed, 984 deletions(-) delete mode 100644 packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts delete mode 100644 packages/grafana-toolkit/src/cli/tasks/plugin.update.ts delete mode 100644 packages/grafana-toolkit/src/cli/tasks/plugin/bundle.managed.ts delete mode 100644 packages/grafana-toolkit/src/plugins/manifest.ts delete mode 100644 packages/grafana-toolkit/src/plugins/types.ts delete mode 100644 packages/grafana-toolkit/src/plugins/utils.ts delete mode 100644 packages/grafana-toolkit/src/plugins/workflow.ts diff --git a/.betterer.results b/.betterer.results index ae38d94c5e2..449693d7e46 100644 --- a/.betterer.results +++ b/.betterer.results @@ -885,17 +885,11 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], - "packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], "packages/grafana-toolkit/src/cli/tasks/plugin.utils.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], - "packages/grafana-toolkit/src/cli/tasks/plugin/bundle.managed.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], "packages/grafana-toolkit/src/cli/tasks/task.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -925,28 +919,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], - "packages/grafana-toolkit/src/plugins/manifest.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Do not use any type assertions.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"], - [0, 0, 0, "Do not use any type assertions.", "6"], - [0, 0, 0, "Unexpected any. Specify a different type.", "7"], - [0, 0, 0, "Do not use any type assertions.", "8"], - [0, 0, 0, "Unexpected any. Specify a different type.", "9"] - ], - "packages/grafana-toolkit/src/plugins/types.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], - "packages/grafana-toolkit/src/plugins/utils.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], - "packages/grafana-toolkit/src/plugins/workflow.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"] - ], "packages/grafana-ui/src/components/Card/Card.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] diff --git a/packages/grafana-toolkit/package.json b/packages/grafana-toolkit/package.json index c4ab0b6a77d..6d36ce4b905 100644 --- a/packages/grafana-toolkit/package.json +++ b/packages/grafana-toolkit/package.json @@ -94,7 +94,6 @@ "less": "^4.1.2", "less-loader": "^10.2.0", "lodash": "^4.17.21", - "md5-file": "^5.0.0", "mini-css-extract-plugin": "^2.6.0", "ora": "^5.4.1", "postcss": "^8.4.12", diff --git a/packages/grafana-toolkit/src/cli/index.ts b/packages/grafana-toolkit/src/cli/index.ts index dbf7d8e7ab0..0507f026aba 100644 --- a/packages/grafana-toolkit/src/cli/index.ts +++ b/packages/grafana-toolkit/src/cli/index.ts @@ -4,10 +4,7 @@ import { program } from 'commander'; import { nodeVersionCheckerTask } from './tasks/nodeVersionChecker'; import { buildPackageTask } from './tasks/package.build'; import { pluginBuildTask } from './tasks/plugin.build'; -import { ciBuildPluginTask, ciPackagePluginTask, ciPluginReportTask } from './tasks/plugin.ci'; -import { pluginUpdateTask } from './tasks/plugin.update'; import { getToolkitVersion, githubPublishTask } from './tasks/plugin.utils'; -import { bundleManagedTask } from './tasks/plugin/bundle.managed'; import { templateTask } from './tasks/template'; import { toolkitBuildTask } from './tasks/toolkit.build'; import { execTask } from './utils/execTask'; @@ -130,53 +127,6 @@ export const run = (includeInternalScripts = false) => { process.exit(1); }); - program - .command('plugin:ci-build') - .option('--finish', 'move all results to the jobs folder', false) - .option('--maxJestWorkers |', 'Limit number of Jest workers spawned') - .description('[deprecated] Build the plugin, leaving results in /dist and /coverage') - .action(async (cmd) => { - await execTask(ciBuildPluginTask)({ - finish: cmd.finish, - maxJestWorkers: cmd.maxJestWorkers, - }); - }); - - program - .command('plugin:ci-package') - .option('--signatureType ', 'Signature Type') - .option('--rootUrls ', 'Root URLs') - .option('--signing-admin', 'Use the admin API endpoint for signing the manifest. (deprecated)', false) - .description('[deprecated] Create a zip packages for the plugin') - .action(async (cmd) => { - await execTask(ciPackagePluginTask)({ - signatureType: cmd.signatureType, - rootUrls: cmd.rootUrls, - }); - }); - - program - .command('plugin:ci-report') - .description('[deprecated] Build a report for this whole process') - .option('--upload', 'upload packages also') - .action(async (cmd) => { - await execTask(ciPluginReportTask)({ - upload: cmd.upload, - }); - }); - - program - .command('plugin:bundle-managed') - .description('[Deprecated] Builds managed plugins') - .action(async (cmd) => { - console.log( - chalk.yellow.bold( - `⚠️ This command is deprecated and will be removed in v10. No further support will be provided. ⚠️` - ) - ); - await execTask(bundleManagedTask)({}); - }); - program .command('plugin:github-publish') .option('--dryrun', 'Do a dry run only', false) @@ -197,18 +147,6 @@ export const run = (includeInternalScripts = false) => { }); }); - program - .command('plugin:update-circleci') - .description('[Deprecated] Update plugin') - .action(async (cmd) => { - console.log( - chalk.yellow.bold( - `⚠️ This command is deprecated and will be removed in v10. No further support will be provided. ⚠️` - ) - ); - await execTask(pluginUpdateTask)({}); - }); - program.on('command:*', () => { console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args.join(' ')); process.exit(1); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts deleted file mode 100644 index 53078e4185e..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts +++ /dev/null @@ -1,258 +0,0 @@ -import execa = require('execa'); -import fs from 'fs-extra'; -import path = require('path'); -import rimrafCallback from 'rimraf'; -import { promisify } from 'util'; - -import { getPluginId } from '../../config/utils/getPluginId'; -import { assertRootUrlIsValid, getPluginJson } from '../../config/utils/pluginValidation'; -import { - getJobFolder, - writeJobStats, - getCiFolder, - getPluginBuildInfo, - getPullRequestNumber, - getCircleDownloadBaseURL, -} from '../../plugins/env'; -import { buildManifest, signManifest, saveManifest } from '../../plugins/manifest'; -import { PluginPackageDetails, PluginBuildReport } from '../../plugins/types'; -import { getPackageDetails, getGrafanaVersions, readGitLog } from '../../plugins/utils'; -import { agregateWorkflowInfo, agregateCoverageInfo, agregateTestInfo } from '../../plugins/workflow'; - -import { pluginBuildRunner } from './plugin.build'; -import { Task, TaskRunner } from './task'; -const rimraf = promisify(rimrafCallback); - -export interface PluginCIOptions { - finish?: boolean; - upload?: boolean; - signatureType?: string; - rootUrls?: string[]; - maxJestWorkers?: string; -} - -/** - * 1. BUILD - * - * when platform exists it is building backend, otherwise frontend - * - * Each build writes data: - * ~/ci/jobs/build_xxx/ - * - * Anything that should be put into the final zip file should be put in: - * ~/ci/jobs/build_xxx/dist - * - * @deprecated -- this task was written with a specific circle-ci build in mind. That system - * has been replaced with Drone, and this is no longer the best practice. Any new work - * should be defined in the grafana build pipeline tool or drone configs directly. - */ -const buildPluginRunner: TaskRunner = async ({ finish, maxJestWorkers }) => { - const start = Date.now(); - - if (finish) { - const workDir = getJobFolder(); - await rimraf(workDir); - fs.mkdirSync(workDir); - - // Move local folders to the scoped job folder - for (const name of ['dist', 'coverage']) { - const dir = path.resolve(process.cwd(), name); - if (fs.existsSync(dir)) { - fs.moveSync(dir, path.resolve(workDir, name)); - } - } - writeJobStats(start, workDir); - } else { - // Do regular build process with coverage - await pluginBuildRunner({ coverage: true, maxJestWorkers }); - } -}; - -export const ciBuildPluginTask = new Task('Build Plugin', buildPluginRunner); - -/** - * 2. Package - * - * Take everything from `~/ci/job/{any}/dist` and - * 1. merge it into: `~/ci/dist` - * 2. zip it into packages in `~/ci/packages` - * 3. prepare grafana environment in: `~/ci/grafana-test-env` - * - * - * @deprecated -- this task was written with a specific circle-ci build in mind. That system - * has been replaced with Drone, and this is no longer the best practice. Any new work - * should be defined in the grafana build pipeline tool or drone configs directly. - */ -const packagePluginRunner: TaskRunner = async ({ signatureType, rootUrls }) => { - const start = Date.now(); - const ciDir = getCiFolder(); - const packagesDir = path.resolve(ciDir, 'packages'); - const distDir = path.resolve(ciDir, 'dist'); - const docsDir = path.resolve(ciDir, 'docs'); - const jobsDir = path.resolve(ciDir, 'jobs'); - - fs.exists(jobsDir, (jobsDirExists) => { - if (!jobsDirExists) { - throw new Error('You must run plugin:ci-build prior to running plugin:ci-package'); - } - }); - - const grafanaEnvDir = path.resolve(ciDir, 'grafana-test-env'); - await execa('rimraf', [packagesDir, distDir, grafanaEnvDir]); - fs.mkdirSync(packagesDir); - fs.mkdirSync(distDir); - - // Updating the dist dir to have a pluginId named directory in it - // The zip needs to contain the plugin code wrapped in directory with a pluginId name - const distContentDir = path.resolve(distDir, getPluginId()); - fs.mkdirSync(grafanaEnvDir); - - console.log('Build Dist Folder'); - - // 1. Check for a local 'dist' folder - const d = path.resolve(process.cwd(), 'dist'); - if (fs.existsSync(d)) { - await execa('cp', ['-rn', d + '/.', distContentDir]); - } - - // 2. Look for any 'dist' folders under ci/job/XXX/dist - const dirs = fs.readdirSync(path.resolve(ciDir, 'jobs')); - for (const j of dirs) { - const contents = path.resolve(ciDir, 'jobs', j, 'dist'); - if (fs.existsSync(contents)) { - try { - await execa('cp', ['-rn', contents + '/.', distContentDir]); - } catch (er) { - throw new Error('Duplicate files found in dist folders'); - } - } - } - - console.log('Save the source info in plugin.json'); - const pluginJsonFile = path.resolve(distContentDir, 'plugin.json'); - const pluginInfo = getPluginJson(pluginJsonFile); - pluginInfo.info.build = await getPluginBuildInfo(); - fs.writeFileSync(pluginJsonFile, JSON.stringify(pluginInfo, null, 2), { encoding: 'utf-8' }); - - // Write a MANIFEST.txt file in the dist folder - try { - const manifest = await buildManifest(distContentDir); - if (signatureType) { - manifest.signatureType = signatureType; - } - if (rootUrls && rootUrls.length > 0) { - rootUrls.forEach(assertRootUrlIsValid); - manifest.rootUrls = rootUrls; - } - const signedManifest = await signManifest(manifest); - await saveManifest(distContentDir, signedManifest); - } catch (err) { - console.warn(`Error signing manifest: ${distContentDir}`, err); - } - - console.log('Building ZIP'); - let zipName = pluginInfo.id + '-' + pluginInfo.info.version + '.zip'; - let zipFile = path.resolve(packagesDir, zipName); - await execa('zip', ['-r', zipFile, '.'], { cwd: distDir }); - - const zipStats = fs.statSync(zipFile); - if (zipStats.size < 100) { - throw new Error('Invalid zip file: ' + zipFile); - } - - // Make a copy so it is easy for report to read - await execa('cp', [pluginJsonFile, distDir]); - - const info: PluginPackageDetails = { - plugin: await getPackageDetails(zipFile, distDir), - }; - - console.log('Setup Grafana Environment'); - let p = path.resolve(grafanaEnvDir, 'plugins', pluginInfo.id); - fs.mkdirSync(p, { recursive: true }); - await execa('unzip', [zipFile, '-d', p]); - - // If docs exist, zip them into packages - if (fs.existsSync(docsDir)) { - console.log('Creating documentation zip'); - zipName = pluginInfo.id + '-' + pluginInfo.info.version + '-docs.zip'; - zipFile = path.resolve(packagesDir, zipName); - await execa('zip', ['-r', zipFile, '.'], { cwd: docsDir }); - - info.docs = await getPackageDetails(zipFile, docsDir); - } - - p = path.resolve(packagesDir, 'info.json'); - fs.writeFileSync(p, JSON.stringify(info, null, 2), { encoding: 'utf-8' }); - - // Write the custom settings - p = path.resolve(grafanaEnvDir, 'custom.ini'); - const customIniBody = - `# Autogenerated by @grafana/toolkit \n` + - `[paths] \n` + - `plugins = ${path.resolve(grafanaEnvDir, 'plugins')}\n` + - `\n`; // empty line - fs.writeFileSync(p, customIniBody, { encoding: 'utf-8' }); - - writeJobStats(start, getJobFolder()); -}; - -export const ciPackagePluginTask = new Task('Bundle Plugin', packagePluginRunner); - -/** - * 4. Report - * - * Create a report from all the previous steps - * - * @deprecated -- this task was written with a specific circle-ci build in mind. That system - * has been replaced with Drone, and this is no longer the best practice. Any new work - * should be defined in the grafana build pipeline tool or drone configs directly. - */ -const pluginReportRunner: TaskRunner = async ({ upload }) => { - const ciDir = path.resolve(process.cwd(), 'ci'); - const packageDir = path.resolve(ciDir, 'packages'); - const packageInfo = require(path.resolve(packageDir, 'info.json')) as PluginPackageDetails; - - const pluginJsonFile = path.resolve(ciDir, 'dist', 'plugin.json'); - console.log('Load info from: ' + pluginJsonFile); - - const pluginMeta = getPluginJson(pluginJsonFile); - const report: PluginBuildReport = { - plugin: pluginMeta, - packages: packageInfo, - workflow: agregateWorkflowInfo(), - coverage: agregateCoverageInfo(), - tests: agregateTestInfo(), - artifactsBaseURL: await getCircleDownloadBaseURL(), - grafanaVersion: getGrafanaVersions(), - git: await readGitLog(), - }; - const pr = getPullRequestNumber(); - if (pr) { - report.pullRequest = pr; - } - - // Save the report to disk - const file = path.resolve(ciDir, 'report.json'); - fs.writeFileSync(file, JSON.stringify(report, null, 2), { encoding: 'utf-8' }); - - const GRAFANA_API_KEY = process.env.GRAFANA_API_KEY; - if (!GRAFANA_API_KEY) { - console.log('Enter a GRAFANA_API_KEY to upload the plugin report'); - return; - } - const url = `https://grafana.com/api/plugins/${report.plugin.id}/ci`; - - console.log('Sending report to:', url); - const axios = require('axios'); - const info = await axios.post(url, report, { - headers: { Authorization: 'Bearer ' + GRAFANA_API_KEY }, - }); - if (info.status === 200) { - console.log('OK: ', info.data); - } else { - console.warn('Error: ', info); - } -}; - -export const ciPluginReportTask = new Task('Generate Plugin Report', pluginReportRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.update.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.update.ts deleted file mode 100644 index fee3286b395..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.update.ts +++ /dev/null @@ -1,24 +0,0 @@ -import fs = require('fs'); -import path = require('path'); - -import { useSpinner } from '../utils/useSpinner'; - -import { Task, TaskRunner } from './task'; - -interface UpdatePluginTask {} - -const updateCiConfig = () => - useSpinner('Updating CircleCI config', async () => { - const ciConfigPath = path.join(process.cwd(), '.circleci'); - if (!fs.existsSync(ciConfigPath)) { - fs.mkdirSync(ciConfigPath); - } - - const sourceFile = require.resolve('@grafana/toolkit/config/circleci/config.yml'); - const destFile = path.join(ciConfigPath, 'config.yml'); - fs.copyFileSync(sourceFile, destFile); - }); - -const pluginUpdateRunner: TaskRunner = () => updateCiConfig(); - -export const pluginUpdateTask = new Task('Update Plugin', pluginUpdateRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin/bundle.managed.ts b/packages/grafana-toolkit/src/cli/tasks/plugin/bundle.managed.ts deleted file mode 100644 index 7ffd6c32d6d..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin/bundle.managed.ts +++ /dev/null @@ -1,34 +0,0 @@ -import execa = require('execa'); -import { promises as fs } from 'fs'; - -import { Task, TaskRunner } from '../task'; - -interface BundeManagedOptions {} - -const MANAGED_PLUGINS_PATH = `${process.cwd()}/plugins-bundled`; -const MANAGED_PLUGINS_SCOPES = ['internal', 'external']; - -const bundleManagedPluginsRunner: TaskRunner = async () => { - await Promise.all( - MANAGED_PLUGINS_SCOPES.map(async (scope) => { - try { - const plugins = await fs.readdir(`${MANAGED_PLUGINS_PATH}/${scope}`); - if (plugins.length > 0) { - for (const plugin of plugins) { - try { - console.log(`[${scope}]: ${plugin} building...`); - await execa('yarn', ['build'], { cwd: `${MANAGED_PLUGINS_PATH}/${scope}/${plugin}` }); - console.log(`[${scope}]: ${plugin} bundled`); - } catch (e: any) { - console.log(e.stdout); - } - } - } - } catch (e) { - console.log(e); - } - }) - ); -}; - -export const bundleManagedTask = new Task('Bundle managed plugins', bundleManagedPluginsRunner); diff --git a/packages/grafana-toolkit/src/plugins/env.ts b/packages/grafana-toolkit/src/plugins/env.ts index f79b68d449d..56b9e126351 100644 --- a/packages/grafana-toolkit/src/plugins/env.ts +++ b/packages/grafana-toolkit/src/plugins/env.ts @@ -1,106 +1,6 @@ -import execa from 'execa'; import fs from 'fs'; import path from 'path'; -import { PluginBuildInfo } from '@grafana/data'; - -import { JobInfo } from './types'; - -const getJobFromProcessArgv = () => { - const arg = process.argv[2]; - if (arg && arg.startsWith('plugin:ci-')) { - const task = arg.substring('plugin:ci-'.length); - if ('build' === task) { - if ('--backend' === process.argv[3] && process.argv[4]) { - return task + '_' + process.argv[4]; - } - return 'build_plugin'; - } - return task; - } - return 'unknown_job'; -}; - -export const job = - (process.env.DRONE_STEP_NAME ? process.env.DRONE_STEP_NAME : process.env.CIRCLE_JOB) || getJobFromProcessArgv(); - -export const getPluginBuildInfo = async (): Promise => { - if (process.env.CI === 'true') { - let repo: string | undefined; - let branch: string | undefined; - let hash: string | undefined; - let build: number | undefined; - let pr: number | undefined; - if (process.env.DRONE === 'true') { - repo = process.env.DRONE_REPO_LINK; - branch = process.env.DRONE_BRANCH; - hash = process.env.DRONE_COMMIT_SHA; - build = parseInt(process.env.DRONE_BUILD_NUMBER || '', 10); - pr = parseInt(process.env.DRONE_PULL_REQUEST || '', 10); - } else if (process.env.CIRCLECI === 'true') { - repo = process.env.CIRCLE_REPOSITORY_URL; - branch = process.env.CIRCLE_BRANCH; - hash = process.env.CIRCLE_SHA1; - build = parseInt(process.env.CIRCLE_BUILD_NUM || '', 10); - const url = process.env.CIRCLE_PULL_REQUEST || ''; - const idx = url.lastIndexOf('/') + 1; - pr = parseInt(url.substring(idx), 10); - } - - const info: PluginBuildInfo = { - time: Date.now(), - repo, - branch, - hash, - }; - if (pr) { - info.pr = pr; - } - if (build) { - info.number = build; - } - return info; - } - - const branch = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD']); - const hash = await execa('git', ['rev-parse', 'HEAD']); - return { - time: Date.now(), - branch: branch.stdout, - hash: hash.stdout, - }; -}; - -export const getBuildNumber = (): number | undefined => { - if (process.env.DRONE === 'true') { - return parseInt(process.env.DRONE_BUILD_NUMBER || '', 10); - } else if (process.env.CIRCLECI === 'true') { - return parseInt(process.env.CIRCLE_BUILD_NUM || '', 10); - } - - return undefined; -}; - -export const getPullRequestNumber = (): number | undefined => { - if (process.env.DRONE === 'true') { - return parseInt(process.env.DRONE_PULL_REQUEST || '', 10); - } else if (process.env.CIRCLECI === 'true') { - const url = process.env.CIRCLE_PULL_REQUEST || ''; - const idx = url.lastIndexOf('/') + 1; - return parseInt(url.substring(idx), 10); - } - - return undefined; -}; - -export const getJobFolder = () => { - const dir = path.resolve(process.cwd(), 'ci', 'jobs', job); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - return dir; -}; - export const getCiFolder = () => { const dir = path.resolve(process.cwd(), 'ci'); if (!fs.existsSync(dir)) { @@ -108,40 +8,3 @@ export const getCiFolder = () => { } return dir; }; - -export const writeJobStats = (startTime: number, workDir: string) => { - const endTime = Date.now(); - const stats: JobInfo = { - job, - startTime, - endTime, - elapsed: endTime - startTime, - buildNumber: getBuildNumber(), - }; - const f = path.resolve(workDir, 'job.json'); - fs.writeFile(f, JSON.stringify(stats, null, 2), (err) => { - if (err) { - throw new Error('Unable to stats: ' + f); - } - }); -}; - -// https://circleci.com/api/v1.1/project/github/NatelEnergy/grafana-discrete-panel/latest/artifacts -export async function getCircleDownloadBaseURL(): Promise { - try { - const axios = require('axios'); - const repo = process.env.CIRCLE_PROJECT_REPONAME; - const user = process.env.CIRCLE_PROJECT_USERNAME; - let url = `https://circleci.com/api/v1.1/project/github/${user}/${repo}/latest/artifacts`; - const rsp = await axios.get(url); - for (const s of rsp.data) { - const { path, url } = s; - if (url && path && path.endsWith('report.json')) { - return url.substring(url.length - 'report.json'.length); - } - } - } catch (e) { - console.log('Error reading CircleCI artifact URL', e); - } - return undefined; -} diff --git a/packages/grafana-toolkit/src/plugins/index.ts b/packages/grafana-toolkit/src/plugins/index.ts index c302e909020..c1532d6d2e4 100644 --- a/packages/grafana-toolkit/src/plugins/index.ts +++ b/packages/grafana-toolkit/src/plugins/index.ts @@ -1,4 +1 @@ export * from './env'; -export * from './utils'; -export * from './workflow'; -export * from './types'; diff --git a/packages/grafana-toolkit/src/plugins/manifest.ts b/packages/grafana-toolkit/src/plugins/manifest.ts deleted file mode 100644 index 17c1f052170..00000000000 --- a/packages/grafana-toolkit/src/plugins/manifest.ts +++ /dev/null @@ -1,91 +0,0 @@ -import crypto from 'crypto'; -import fs from 'fs'; -import path from 'path'; - -import { ManifestInfo } from './types'; - -const MANIFEST_FILE = 'MANIFEST.txt'; - -async function* walk(dir: string, baseDir: string): AsyncGenerator { - for await (const d of await (fs.promises as any).opendir(dir)) { - const entry = path.posix.join(dir, d.name); - if (d.isDirectory()) { - yield* await walk(entry, baseDir); - } else if (d.isFile()) { - yield path.posix.relative(baseDir, entry); - } else if (d.isSymbolicLink()) { - const realPath = await (fs.promises as any).realpath(entry); - if (!realPath.startsWith(baseDir)) { - throw new Error( - `symbolic link ${path.posix.relative( - baseDir, - entry - )} targets a file outside of the base directory: ${baseDir}` - ); - } - // if resolved symlink target is a file include it in the manifest - const stats = await (fs.promises as any).stat(realPath); - if (stats.isFile()) { - yield path.posix.relative(baseDir, entry); - } - } - } -} - -export async function buildManifest(dir: string): Promise { - const pluginJson = JSON.parse(fs.readFileSync(path.join(dir, 'plugin.json'), { encoding: 'utf8' })); - - const manifest = { - plugin: pluginJson.id, - version: pluginJson.info.version, - files: {}, - } as ManifestInfo; - - for await (const p of await walk(dir, dir)) { - if (p === MANIFEST_FILE) { - continue; - } - - manifest.files[p] = crypto - .createHash('sha256') - .update(fs.readFileSync(path.join(dir, p))) - .digest('hex'); - } - - return manifest; -} - -export async function signManifest(manifest: ManifestInfo): Promise { - const GRAFANA_API_KEY = process.env.GRAFANA_API_KEY; - if (!GRAFANA_API_KEY) { - throw new Error('You must enter a GRAFANA_API_KEY to sign the plugin manifest'); - } - - const GRAFANA_COM_URL = process.env.GRAFANA_COM_URL || 'https://grafana.com/api'; - const url = GRAFANA_COM_URL + '/plugins/ci/sign'; - - const axios = require('axios'); - - try { - const info = await axios.post(url, manifest, { - headers: { Authorization: 'Bearer ' + GRAFANA_API_KEY }, - }); - if (info.status !== 200) { - console.warn('Error: ', info); - throw new Error('Error signing manifest'); - } - - return info.data; - } catch (err: any) { - if (err.response?.data?.message) { - throw new Error('Error signing manifest: ' + err.response.data.message); - } - - throw new Error('Error signing manifest: ' + err.message); - } -} - -export async function saveManifest(dir: string, signedManifest: string): Promise { - fs.writeFileSync(path.join(dir, MANIFEST_FILE), signedManifest); - return true; -} diff --git a/packages/grafana-toolkit/src/plugins/types.ts b/packages/grafana-toolkit/src/plugins/types.ts deleted file mode 100644 index 296cbc8252a..00000000000 --- a/packages/grafana-toolkit/src/plugins/types.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { PluginMeta, KeyValue } from '@grafana/data'; - -export interface PluginPackageDetails { - plugin: ZipFileInfo; - docs?: ZipFileInfo; -} - -export interface PluginBuildReport { - plugin: PluginMeta; - packages: PluginPackageDetails; - workflow: WorkflowInfo; - coverage: CoverageInfo[]; - tests: TestResultsInfo[]; - git?: GitLogInfo; - pullRequest?: number; - artifactsBaseURL?: string; - grafanaVersion?: KeyValue; -} - -export interface JobInfo { - job?: string; - startTime: number; - endTime: number; - elapsed: number; - status?: string; - buildNumber?: number; -} - -export interface WorkflowInfo extends JobInfo { - workflowId?: string; - jobs: JobInfo[]; - user?: string; - repo?: string; -} - -export interface CoverageDetails { - total: number; - covered: number; - skipped: number; - pct: number; -} - -export interface CoverageInfo { - job: string; - summary: { [key: string]: CoverageDetails }; - report?: string; // path to report -} - -export interface TestResultsInfo { - job: string; - grafana?: any; - error?: string; - passed: number; - failed: number; - screenshots: string[]; -} - -export interface CountAndSize { - count: number; - bytes: number; -} - -export interface ExtensionSize { - [key: string]: CountAndSize; -} - -export interface ZipFileInfo { - name: string; - size: number; - contents: ExtensionSize; - sha1?: string; - md5?: string; -} - -interface UserInfo { - name: string; - email: string; - time?: number; -} - -export interface GitLogInfo { - commit: string; - tree: string; - subject: string; - body?: string; - notes?: string; - author: UserInfo; - commiter: UserInfo; -} - -export interface ManifestInfo { - // time: number; << filled in by the server - // keyId: string; << filled in by the server - // signedByOrg: string; << filled in by the server - // signedByOrgName: string; << filled in by the server - signatureType?: string; // filled in by the server if not specified - rootUrls?: string[]; // for private signatures - plugin: string; - version: string; - files: Record; - toolkit?: { - version: string; - }; -} diff --git a/packages/grafana-toolkit/src/plugins/utils.ts b/packages/grafana-toolkit/src/plugins/utils.ts deleted file mode 100644 index e968fc31fd0..00000000000 --- a/packages/grafana-toolkit/src/plugins/utils.ts +++ /dev/null @@ -1,132 +0,0 @@ -import execa from 'execa'; -import fs from 'fs'; -import path from 'path'; - -import { KeyValue } from '@grafana/data'; - -import { ExtensionSize, ZipFileInfo, GitLogInfo } from './types'; - -const md5File = require('md5-file'); - -export function getGrafanaVersions(): KeyValue { - const dir = path.resolve(process.cwd(), 'node_modules', '@grafana'); - const versions: KeyValue = {}; - try { - fs.readdirSync(dir).forEach((file) => { - const json = require(path.resolve(dir, file, 'package.json')); - versions[file] = json.version; - }); - } catch (err) { - console.warn('Error reading toolkit versions', err); - } - return versions; -} - -export function getFileSizeReportInFolder(dir: string, info?: ExtensionSize): ExtensionSize { - const acc: ExtensionSize = info ? info : {}; - - const files = fs.readdirSync(dir); - if (files) { - files.forEach((file) => { - const newbase = path.join(dir, file); - const stat = fs.statSync(newbase); - if (stat.isDirectory()) { - getFileSizeReportInFolder(newbase, info); - } else { - let ext = '_none_'; - const idx = file.lastIndexOf('.'); - if (idx > 0) { - ext = file.substring(idx + 1).toLowerCase(); - } - const current = acc[ext]; - if (current) { - current.count += 1; - current.bytes += stat.size; - } else { - acc[ext] = { bytes: stat.size, count: 1 }; - } - } - }); - } - return acc; -} - -export async function getPackageDetails(zipFile: string, zipSrc: string, writeChecksum = true): Promise { - const zipStats = fs.statSync(zipFile); - if (zipStats.size < 100) { - throw new Error('Invalid zip file: ' + zipFile); - } - const info: ZipFileInfo = { - name: path.basename(zipFile), - size: zipStats.size, - contents: getFileSizeReportInFolder(zipSrc), - }; - try { - const exe = await execa('shasum', [zipFile]); - const idx = exe.stdout.indexOf(' '); - const sha1 = exe.stdout.substring(0, idx); - if (writeChecksum) { - fs.writeFile(zipFile + '.sha1', sha1, (err) => {}); - } - info.sha1 = sha1; - } catch { - console.warn('Unable to read SHA1 Checksum'); - } - try { - info.md5 = md5File.sync(zipFile); - } catch { - console.warn('Unable to read MD5 Checksum'); - } - return info; -} - -export function findImagesInFolder(dir: string, prefix = '', append?: string[]): string[] { - const imgs = append || []; - - const files = fs.readdirSync(dir); - if (files) { - files.forEach((file) => { - if (file.endsWith('.png')) { - imgs.push(file); - } - }); - } - - return imgs; -} - -export async function readGitLog(): Promise { - try { - let exe = await execa('git', [ - 'log', - '-1', // last line - '--pretty=format:{%n "commit": "%H",%n "tree": "%T",%n "subject": "%s",%n "author": {%n "name": "%aN",%n "email": "%aE",%n "time":"%at" },%n "commiter": {%n "name": "%cN",%n "email": "%cE",%n "time":"%ct" }%n}', - ]); - const info = JSON.parse(exe.stdout) as GitLogInfo; - - // Read the body - exe = await execa('git', [ - 'log', - '-1', // last line - '--pretty=format:%b', // Just the body (with newlines!) - ]); - if (exe.stdout && exe.stdout.length) { - info.body = exe.stdout.trim(); - } - - // Read any commit notes - exe = await execa('git', [ - 'log', - '-1', // last line - '--pretty=format:%N', // commit notes (with newlines!) - ]); - if (exe.stdout && exe.stdout.length) { - info.notes = exe.stdout.trim(); - } - - return info; - } catch (err) { - console.warn('Error REading Git log info', err); - } - return undefined; -} diff --git a/packages/grafana-toolkit/src/plugins/workflow.ts b/packages/grafana-toolkit/src/plugins/workflow.ts deleted file mode 100644 index ea653836841..00000000000 --- a/packages/grafana-toolkit/src/plugins/workflow.ts +++ /dev/null @@ -1,100 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -import { getBuildNumber, getCiFolder } from './env'; -import { JobInfo, WorkflowInfo, CoverageInfo, TestResultsInfo } from './types'; - -export const agregateWorkflowInfo = (): WorkflowInfo => { - const now = Date.now(); - const workflow: WorkflowInfo = { - jobs: [], - startTime: now, - endTime: now, - workflowId: process.env.CIRCLE_WORKFLOW_ID, - repo: process.env.CIRCLE_PROJECT_REPONAME, - user: process.env.CIRCLE_PROJECT_USERNAME, - buildNumber: getBuildNumber(), - elapsed: 0, - }; - - const jobsFolder = path.resolve(getCiFolder(), 'jobs'); - if (fs.existsSync(jobsFolder)) { - const files = fs.readdirSync(jobsFolder); - if (files && files.length) { - files.forEach((file) => { - const p = path.resolve(jobsFolder, file, 'job.json'); - if (fs.existsSync(p)) { - const job = require(p) as JobInfo; - workflow.jobs.push(job); - if (job.startTime < workflow.startTime) { - workflow.startTime = job.startTime; - } - if (job.endTime > workflow.endTime) { - workflow.endTime = job.endTime; - } - } else { - console.log('Missing Job info: ', p); - } - }); - } else { - console.log('NO JOBS IN: ', jobsFolder); - } - } - - workflow.elapsed = workflow.endTime - workflow.startTime; - return workflow; -}; - -export const agregateCoverageInfo = (): CoverageInfo[] => { - const coverage: CoverageInfo[] = []; - const ciDir = getCiFolder(); - const jobsFolder = path.resolve(ciDir, 'jobs'); - if (fs.existsSync(jobsFolder)) { - const files = fs.readdirSync(jobsFolder); - if (files && files.length) { - files.forEach((file) => { - const dir = path.resolve(jobsFolder, file, 'coverage'); - if (fs.existsSync(dir)) { - const s = path.resolve(dir, 'coverage-summary.json'); - const r = path.resolve(dir, 'lcov-report', 'index.html'); - if (fs.existsSync(s)) { - const raw = require(s); - const info: CoverageInfo = { - job: file, - summary: raw.total, - }; - if (fs.existsSync(r)) { - info.report = r.substring(ciDir.length); - } - coverage.push(info); - } - } - }); - } else { - console.log('NO JOBS IN: ', jobsFolder); - } - } - return coverage; -}; - -export const agregateTestInfo = (): TestResultsInfo[] => { - const tests: TestResultsInfo[] = []; - const ciDir = getCiFolder(); - const jobsFolder = path.resolve(ciDir, 'jobs'); - if (fs.existsSync(jobsFolder)) { - const files = fs.readdirSync(jobsFolder); - if (files && files.length) { - files.forEach((file) => { - if (file.startsWith('test')) { - const summary = path.resolve(jobsFolder, file, 'results.json'); - if (fs.existsSync(summary)) { - tests.push(require(summary) as TestResultsInfo); - } - } - }); - } else { - console.log('NO Jobs IN: ', jobsFolder); - } - } - return tests; -}; diff --git a/yarn.lock b/yarn.lock index a24b50d212f..c49e4cbe802 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3436,7 +3436,6 @@ __metadata: less: ^4.1.2 less-loader: ^10.2.0 lodash: ^4.17.21 - md5-file: ^5.0.0 mini-css-extract-plugin: ^2.6.0 ora: ^5.4.1 postcss: ^8.4.12 @@ -25384,15 +25383,6 @@ __metadata: languageName: node linkType: hard -"md5-file@npm:^5.0.0": - version: 5.0.0 - resolution: "md5-file@npm:5.0.0" - bin: - md5-file: cli.js - checksum: c606a00ff58adf5428e8e2f36d86e5d3c7029f9688126faca302cd83b5e92cac183a62e1d1f05fae7c2614e80f993326fd0a8d6a3a913c41ec7ea0eefc25aa76 - languageName: node - linkType: hard - "mdast-squeeze-paragraphs@npm:^4.0.0": version: 4.0.0 resolution: "mdast-squeeze-paragraphs@npm:4.0.0"