From e7d1f1df142a365ee61c727ac017a4fffa698430 Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 8 Jul 2019 16:00:06 -0700 Subject: [PATCH 1/9] add stubs for each ci task --- packages/grafana-toolkit/package.json | 1 + packages/grafana-toolkit/src/cli/index.ts | 35 +++- .../src/cli/tasks/plugin.ci.ts | 165 +++++++++++++++--- yarn.lock | 12 ++ 4 files changed, 187 insertions(+), 26 deletions(-) diff --git a/packages/grafana-toolkit/package.json b/packages/grafana-toolkit/package.json index dbc667de0db..ba583537c20 100644 --- a/packages/grafana-toolkit/package.json +++ b/packages/grafana-toolkit/package.json @@ -28,6 +28,7 @@ "@types/node": "^12.0.4", "@types/react-dev-utils": "^9.0.1", "@types/semver": "^6.0.0", + "@types/tmp": "^0.1.0", "@types/webpack": "4.4.34", "axios": "0.19.0", "babel-loader": "8.0.6", diff --git a/packages/grafana-toolkit/src/cli/index.ts b/packages/grafana-toolkit/src/cli/index.ts index 423a14b4a58..a136ca804cd 100644 --- a/packages/grafana-toolkit/src/cli/index.ts +++ b/packages/grafana-toolkit/src/cli/index.ts @@ -15,7 +15,7 @@ import { pluginTestTask } from './tasks/plugin.tests'; import { searchTestDataSetupTask } from './tasks/searchTestDataSetup'; import { closeMilestoneTask } from './tasks/closeMilestone'; import { pluginDevTask } from './tasks/plugin.dev'; -import { pluginCITask } from './tasks/plugin.ci'; +import { ciBuildPluginTask, ciBundlePluginTask, ciTestPluginTask, ciDeployPluginTask } from './tasks/plugin.ci'; export const run = (includeInternalScripts = false) => { if (includeInternalScripts) { @@ -154,15 +154,38 @@ export const run = (includeInternalScripts = false) => { }); program - .command('plugin:ci') - .option('--dryRun', "Dry run (don't post results)") - .description('Run Plugin CI task') + .command('plugin:ci-build') + .option('--platform', 'For backend task, which backend to run') + .description('Build the plugin, leaving artifacts in /dist') .action(async cmd => { - await execTask(pluginCITask)({ - dryRun: cmd.dryRun, + await execTask(ciBuildPluginTask)({ + platform: cmd.platform, }); }); + program + .command('plugin:ci-bundle') + .description('Create a zip artifact for the plugin') + .action(async cmd => { + await execTask(ciBundlePluginTask)({}); + }); + + program + .command('plugin:ci-test') + .description('end-to-end test using bundle in /artifacts') + .action(async cmd => { + await execTask(ciTestPluginTask)({ + platform: cmd.platform, + }); + }); + + program + .command('plugin:ci-deploy') + .description('Publish plugin CI results') + .action(async cmd => { + await execTask(ciDeployPluginTask)({}); + }); + 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 index 9a5834de9dc..bf5b67d1267 100644 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts +++ b/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts @@ -8,9 +8,10 @@ import { getPluginJson } from '../../config/utils/pluginValidation'; import execa = require('execa'); import path = require('path'); import fs = require('fs'); +import tmp = require('tmp'); export interface PluginCIOptions { - dryRun?: boolean; + platform?: string; } const calcJavascriptSize = (base: string, files?: string[]): number => { @@ -33,22 +34,74 @@ const calcJavascriptSize = (base: string, files?: string[]): number => { return size; }; -const pluginCIRunner: TaskRunner = async ({ dryRun }) => { +/** + * 1. BUILD + * + * when platform exists it is building backend, otherwise frontend + * + * Everything in /build folder + * + */ +const buildPluginRunner: TaskRunner = async ({ platform }) => { const start = Date.now(); const distDir = `${process.cwd()}/dist`; - const artifactsDir = `${process.cwd()}/artifacts`; - await execa('rimraf', [`${process.cwd()}/coverage`]); - await execa('rimraf', [artifactsDir]); + const buildDir = `${process.cwd()}/build`; + const coverageDir = `${process.cwd()}/coverage`; + if (!fs.existsSync(buildDir)) { + fs.mkdirSync(buildDir); + } + + if (platform) { + console.log('TODO, backend support?'); + const stub = distDir + `/bin_${platform}`; + if (!fs.existsSync(stub)) { + fs.mkdirSync(stub); + } + fs.writeFile(stub + '/README.txt', 'TODO... build it!', err => { + if (err) { + throw new Error('Unable to write: ' + stub); + } + }); + } else { + // Do regular build process + await pluginBuildRunner({ coverage: true }); + } + + // Move dist & coverage into 'build' + fs.renameSync(distDir, path.resolve(buildDir, 'dist')); + fs.renameSync(coverageDir, path.resolve(buildDir, 'coverage')); - // Do regular build process - await pluginBuildRunner({ coverage: true }); const elapsed = Date.now() - start; + const stats = { + job: `${process.env.CIRCLE_JOB}`, + sha1: `${process.env.CIRCLE_SHA1}`, + startTime: start, + buildTime: elapsed, + endTime: Date.now(), + }; + console.log('BUILD Info', stats); +}; +export const ciBuildPluginTask = new Task('Build Plugin', buildPluginRunner); + +/** + * 2. BUNDLE + * + * Take everything from /build/dist and zip it up + * + */ +const bundlePluginRunner: TaskRunner = async () => { + const start = Date.now(); + const distDir = `${process.cwd()}/build/dist`; + if (!fs.existsSync(distDir)) { + throw new Error('Dist folder does not exist: ' + distDir); + } + + const artifactsDir = `${process.cwd()}/build/artifacts`; if (!fs.existsSync(artifactsDir)) { fs.mkdirSync(artifactsDir); } - // TODO? can this typed from @grafana/ui? const pluginInfo = getPluginJson(`${distDir}/plugin.json`); const zipName = pluginInfo.id + '-' + pluginInfo.info.version + '.zip'; const zipFile = path.resolve(artifactsDir, zipName); @@ -56,23 +109,95 @@ const pluginCIRunner: TaskRunner = async ({ dryRun }) => { await execa('zip', ['-r', zipFile, '.']); restoreCwd(); + const zipStats = fs.statSync(zipFile); + if (zipStats.size < 100) { + throw new Error('Invalid zip file: ' + zipFile); + } + const stats = { - startTime: start, - buildTime: elapsed, - jsSize: calcJavascriptSize(distDir), - zipSize: fs.statSync(zipFile).size, - endTime: Date.now(), + name: zipName, + size: zipStats.size, }; - fs.writeFile(artifactsDir + '/stats.json', JSON.stringify(stats, null, 2), err => { + + fs.writeFile(artifactsDir + '/info.json', JSON.stringify(stats, null, 2), err => { if (err) { throw new Error('Unable to write stats'); } - console.log('Stats', stats); + console.log('Created', stats); }); - - if (!dryRun) { - console.log('TODO send info to github?'); - } }; -export const pluginCITask = new Task('Plugin CI', pluginCIRunner); +export const ciBundlePluginTask = new Task('Bundle Plugin', bundlePluginRunner); + +/** + * 3. Test (end-to-end) + * + * deploy the zip to a running grafana instance + * + */ +const testPluginRunner: TaskRunner = async ({ platform }) => { + const start = Date.now(); + + const artifactsDir = `${process.cwd()}/build/artifacts`; + const infoFile = path.resolve(artifactsDir, 'info.json'); + const zipInfo = require(infoFile); + const zipPath = path.resolve(artifactsDir, zipInfo.name); + + const tmpobj = tmp.dirSync(); + const pluginFolder = tmpobj.name; + console.log('Temp Folder', pluginFolder); + + await execa('unzip', [zipPath, '-d', pluginFolder]); + + const { stdout } = await execa('ls', ['-Rl', pluginFolder]); + console.log(stdout); + + // Manual cleanup + tmpobj.removeCallback(); + + fs.mkdirSync(pluginFolder, { recursive: true }); + + const elapsed = Date.now() - start; + const stats = { + job: `${process.env.CIRCLE_JOB}`, + sha1: `${process.env.CIRCLE_SHA1}`, + startTime: start, + buildTime: elapsed, + endTime: Date.now(), + }; + console.log('TODO Test', stats); +}; + +export const ciTestPluginTask = new Task('Test Plugin (e2e)', testPluginRunner); + +/** + * 4. Deploy + * + * deploy the zip to a running grafana instance + * + */ +const deployPluginRunner: TaskRunner = async () => { + const start = Date.now(); + + // TASK Time + if (process.env.CIRCLE_INTERNAL_TASK_DATA) { + const timingInfo = fs.readdirSync(`${process.env.CIRCLE_INTERNAL_TASK_DATA}`); + if (timingInfo) { + timingInfo.forEach(file => { + console.log('TIMING INFO: ', file); + }); + } + } + + const elapsed = Date.now() - start; + const stats = { + job: `${process.env.CIRCLE_JOB}`, + sha1: `${process.env.CIRCLE_SHA1}`, + startTime: start, + buildTime: elapsed, + endTime: Date.now(), + }; + console.log('TODO DEPLOY', stats); +}; + +export const ciDeployPluginTask = new Task('Deploy plugin', deployPluginRunner); diff --git a/yarn.lock b/yarn.lock index 113e9a0e954..72a85838247 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2568,6 +2568,11 @@ resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.2.tgz#721ca5c5d1a2988b4a886e35c2ffc5735b6afbdf" integrity sha512-PeHg/AtdW6aaIO2a+98Xj7rWY4KC1E6yOy7AFknJQ7VXUGNrMlyxDFxJo7HqLtjQms/ZhhQX52mLVW/EX3JGOw== +"@types/tmp@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.1.0.tgz#19cf73a7bcf641965485119726397a096f0049bd" + integrity sha512-6IwZ9HzWbCq6XoQWhxLpDjuADodH/MKXRUIDFudvgjcVdjFknvmR+DNsoUeer4XPrEnrZs04Jj+kfV9pFsrhmA== + "@types/uglify-js@*": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.4.tgz#96beae23df6f561862a830b4288a49e86baac082" @@ -15752,6 +15757,13 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +tmp@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" + integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== + dependencies: + rimraf "^2.6.3" + tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" From a13b96521d5b762b714e2586940db70b537f6d76 Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 8 Jul 2019 16:56:29 -0700 Subject: [PATCH 2/9] use ci-work folder rather than build --- packages/grafana-toolkit/src/cli/index.ts | 2 +- .../src/cli/tasks/plugin.ci.ts | 41 ++++++++++--------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/packages/grafana-toolkit/src/cli/index.ts b/packages/grafana-toolkit/src/cli/index.ts index a136ca804cd..c6b8357e7a8 100644 --- a/packages/grafana-toolkit/src/cli/index.ts +++ b/packages/grafana-toolkit/src/cli/index.ts @@ -155,7 +155,7 @@ export const run = (includeInternalScripts = false) => { program .command('plugin:ci-build') - .option('--platform', 'For backend task, which backend to run') + .option('--platform ', 'For backend task, which backend to run') .description('Build the plugin, leaving artifacts in /dist') .action(async cmd => { await execTask(ciBuildPluginTask)({ diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts index bf5b67d1267..a69bbc2b43f 100644 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts +++ b/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts @@ -39,23 +39,23 @@ const calcJavascriptSize = (base: string, files?: string[]): number => { * * when platform exists it is building backend, otherwise frontend * - * Everything in /build folder + * Everything in /ci-work folder * */ const buildPluginRunner: TaskRunner = async ({ platform }) => { const start = Date.now(); const distDir = `${process.cwd()}/dist`; - const buildDir = `${process.cwd()}/build`; + const buildDir = `${process.cwd()}/ci-work`; const coverageDir = `${process.cwd()}/coverage`; - if (!fs.existsSync(buildDir)) { - fs.mkdirSync(buildDir); - } + + await execa('rimraf', [buildDir]); + fs.mkdirSync(buildDir); if (platform) { console.log('TODO, backend support?'); - const stub = distDir + `/bin_${platform}`; + const stub = buildDir + `/bin_${platform}`; if (!fs.existsSync(stub)) { - fs.mkdirSync(stub); + fs.mkdirSync(stub, { recursive: true }); } fs.writeFile(stub + '/README.txt', 'TODO... build it!', err => { if (err) { @@ -65,11 +65,11 @@ const buildPluginRunner: TaskRunner = async ({ platform }) => { } else { // Do regular build process await pluginBuildRunner({ coverage: true }); - } - // Move dist & coverage into 'build' - fs.renameSync(distDir, path.resolve(buildDir, 'dist')); - fs.renameSync(coverageDir, path.resolve(buildDir, 'coverage')); + // Move dist & coverage into workspace + fs.renameSync(distDir, path.resolve(buildDir, 'dist')); + fs.renameSync(coverageDir, path.resolve(buildDir, 'coverage')); + } const elapsed = Date.now() - start; const stats = { @@ -87,17 +87,17 @@ export const ciBuildPluginTask = new Task('Build Plugin', build /** * 2. BUNDLE * - * Take everything from /build/dist and zip it up + * Take everything from /ci-work/dist and zip it up * */ const bundlePluginRunner: TaskRunner = async () => { const start = Date.now(); - const distDir = `${process.cwd()}/build/dist`; + const distDir = `${process.cwd()}/ci-work/dist`; if (!fs.existsSync(distDir)) { throw new Error('Dist folder does not exist: ' + distDir); } - const artifactsDir = `${process.cwd()}/build/artifacts`; + const artifactsDir = `${process.cwd()}/ci-work/artifacts`; if (!fs.existsSync(artifactsDir)) { fs.mkdirSync(artifactsDir); } @@ -138,7 +138,7 @@ export const ciBundlePluginTask = new Task('Bundle Plugin', bun const testPluginRunner: TaskRunner = async ({ platform }) => { const start = Date.now(); - const artifactsDir = `${process.cwd()}/build/artifacts`; + const artifactsDir = `${process.cwd()}/ci-work/artifacts`; const infoFile = path.resolve(artifactsDir, 'info.json'); const zipInfo = require(infoFile); const zipPath = path.resolve(artifactsDir, zipInfo.name); @@ -149,13 +149,14 @@ const testPluginRunner: TaskRunner = async ({ platform }) => { await execa('unzip', [zipPath, '-d', pluginFolder]); - const { stdout } = await execa('ls', ['-Rl', pluginFolder]); - console.log(stdout); + let ex = await execa('ls', ['-Rl', pluginFolder]); + console.log(ex.stdout); - // Manual cleanup - tmpobj.removeCallback(); + ex = await execa('grafana-cli', ['--version']); + console.log('Grafana Version: ' + ex.stdout); - fs.mkdirSync(pluginFolder, { recursive: true }); + console.log('TODO, install: ' + pluginFolder); + console.log('TODO, puppeteer...'); const elapsed = Date.now() - start; const stats = { From 26d5db2b634aee11e874da213fa476ae76546b4a Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 8 Jul 2019 20:53:09 -0700 Subject: [PATCH 3/9] use axios for basic testing --- .../src/cli/tasks/plugin.ci.ts | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts index a69bbc2b43f..516714c1d05 100644 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts +++ b/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts @@ -92,14 +92,18 @@ export const ciBuildPluginTask = new Task('Build Plugin', build */ const bundlePluginRunner: TaskRunner = async () => { const start = Date.now(); - const distDir = `${process.cwd()}/ci-work/dist`; + let distDir = `${process.cwd()}/ci-work/dist`; if (!fs.existsSync(distDir)) { - throw new Error('Dist folder does not exist: ' + distDir); + distDir = `${process.cwd()}/dist`; + if (!fs.existsSync(distDir)) { + throw new Error('Dist folder does not exist: ' + distDir); + } } + // Create an artifact const artifactsDir = `${process.cwd()}/ci-work/artifacts`; if (!fs.existsSync(artifactsDir)) { - fs.mkdirSync(artifactsDir); + fs.mkdirSync(artifactsDir, { recursive: true }); } const pluginInfo = getPluginJson(`${distDir}/plugin.json`); @@ -114,17 +118,14 @@ const bundlePluginRunner: TaskRunner = async () => { throw new Error('Invalid zip file: ' + zipFile); } - const stats = { - name: zipName, - size: zipStats.size, - }; + // Set up the docker folder structure + const dockerDir = `${process.cwd()}/ci-work/docker`; + const pluginFolder = path.resolve(dockerDir, 'plugin'); + fs.mkdirSync(pluginFolder, { recursive: true }); + await execa('unzip', [zipFile, '-d', pluginFolder]); - fs.writeFile(artifactsDir + '/info.json', JSON.stringify(stats, null, 2), err => { - if (err) { - throw new Error('Unable to write stats'); - } - console.log('Created', stats); - }); + let ex = await execa('ls', ['-Rl', pluginFolder]); + console.log('Now load docker from:', ex.stdout); }; export const ciBundlePluginTask = new Task('Bundle Plugin', bundlePluginRunner); @@ -138,25 +139,27 @@ export const ciBundlePluginTask = new Task('Bundle Plugin', bun const testPluginRunner: TaskRunner = async ({ platform }) => { const start = Date.now(); - const artifactsDir = `${process.cwd()}/ci-work/artifacts`; - const infoFile = path.resolve(artifactsDir, 'info.json'); - const zipInfo = require(infoFile); - const zipPath = path.resolve(artifactsDir, zipInfo.name); + const args = { + withCredentials: true, + baseURL: 'http://localhost:3000/', + responseType: 'json', + auth: { + username: 'admin', + password: 'admin', + }, + }; - const tmpobj = tmp.dirSync(); - const pluginFolder = tmpobj.name; - console.log('Temp Folder', pluginFolder); + const axios = require('axios'); + const frontendSettings = await axios.get('api/frontend/settings', args); - await execa('unzip', [zipPath, '-d', pluginFolder]); + console.log('Grafana Version: ' + JSON.stringify(frontendSettings.data.buildInfo, null, 2)); - let ex = await execa('ls', ['-Rl', pluginFolder]); - console.log(ex.stdout); + const pluginInfo = getPluginJson(`${process.cwd()}/src/plugin.json`); + const pluginSettings = await axios.get(`api/plugins/${pluginInfo.id}/settings`, args); - ex = await execa('grafana-cli', ['--version']); - console.log('Grafana Version: ' + ex.stdout); + console.log('Plugin Info: ' + JSON.stringify(pluginSettings.data, null, 2)); - console.log('TODO, install: ' + pluginFolder); - console.log('TODO, puppeteer...'); + console.log('TODO puppeteer'); const elapsed = Date.now() - start; const stats = { From 82996d6f0af1c889bc30a24abf696596cb602d99 Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 9 Jul 2019 10:45:55 -0700 Subject: [PATCH 4/9] Packages: publish packages@6.3.0-alpha.39 --- lerna.json | 2 +- packages/grafana-data/package.json | 2 +- packages/grafana-runtime/package.json | 2 +- packages/grafana-toolkit/package.json | 2 +- packages/grafana-ui/package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lerna.json b/lerna.json index 83a941b5539..b076b4b9c63 100644 --- a/lerna.json +++ b/lerna.json @@ -2,5 +2,5 @@ "npmClient": "yarn", "useWorkspaces": true, "packages": ["packages/*"], - "version": "6.3.0-alpha.36" + "version": "6.3.0-alpha.39" } diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index 34d6509bfde..6f0e312c120 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/data", - "version": "6.3.0-alpha.36", + "version": "6.3.0-alpha.39", "description": "Grafana Data Library", "keywords": [ "typescript" diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json index 7798b549dac..3f2fb98bf04 100644 --- a/packages/grafana-runtime/package.json +++ b/packages/grafana-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/runtime", - "version": "6.3.0-alpha.36", + "version": "6.3.0-alpha.39", "description": "Grafana Runtime Library", "keywords": [ "grafana" diff --git a/packages/grafana-toolkit/package.json b/packages/grafana-toolkit/package.json index 304e3c6b56b..fa322196826 100644 --- a/packages/grafana-toolkit/package.json +++ b/packages/grafana-toolkit/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/toolkit", - "version": "6.3.0-alpha.36", + "version": "6.3.0-alpha.39", "description": "Grafana Toolkit", "keywords": [ "grafana", diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index de0cb7f3eb1..4584e1d0d5e 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/ui", - "version": "6.3.0-alpha.36", + "version": "6.3.0-alpha.39", "description": "Grafana Components Library", "keywords": [ "grafana", From 38c288bb9a14798ba40323a83a5e4a2f3c291d5e Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 9 Jul 2019 10:56:08 -0700 Subject: [PATCH 5/9] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 569bbfd6096..81e53750c40 100644 --- a/package.json +++ b/package.json @@ -148,7 +148,7 @@ "themes:generate": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/generateSassVariableFiles.ts", "packages:prepare": "lerna run clean && npm run test && lerna version --tag-version-prefix=\"packages@\" -m \"Packages: publish %s\" --no-push", "packages:build": "lerna run clean && lerna run build", - "packages:publish": "lerna publish from-package --contents dist --tag-version-prefix=\"packages@\" --dist-tag next" + "packages:publish": "lerna publish from-package --contents dist --dist-tag next --tag-version-prefix=\"packages@\"" }, "husky": { "hooks": { From 807594fd653f312f6b39cd21d62f1af62b784c3a Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 9 Jul 2019 23:16:17 -0700 Subject: [PATCH 6/9] add download task --- packages/grafana-toolkit/src/cli/index.ts | 17 +- .../src/cli/tasks/plugin.ci.ts | 165 +++++++++++++----- 2 files changed, 142 insertions(+), 40 deletions(-) diff --git a/packages/grafana-toolkit/src/cli/index.ts b/packages/grafana-toolkit/src/cli/index.ts index d4da9485d34..ab9efadae40 100644 --- a/packages/grafana-toolkit/src/cli/index.ts +++ b/packages/grafana-toolkit/src/cli/index.ts @@ -13,7 +13,13 @@ import { pluginTestTask } from './tasks/plugin.tests'; import { searchTestDataSetupTask } from './tasks/searchTestDataSetup'; import { closeMilestoneTask } from './tasks/closeMilestone'; import { pluginDevTask } from './tasks/plugin.dev'; -import { ciBuildPluginTask, ciBundlePluginTask, ciTestPluginTask, ciDeployPluginTask } from './tasks/plugin.ci'; +import { + ciBuildPluginTask, + ciBundlePluginTask, + ciTestPluginTask, + ciDeployPluginTask, + ciSetupPluginTask, +} from './tasks/plugin.ci'; import { buildPackageTask } from './tasks/package.build'; export const run = (includeInternalScripts = false) => { @@ -157,6 +163,15 @@ export const run = (includeInternalScripts = false) => { await execTask(ciBundlePluginTask)({}); }); + program + .command('plugin:ci-setup') + .option('--installer ', 'Name of installer to download and run') + .description('Install and configure grafana') + .action(async cmd => { + await execTask(ciSetupPluginTask)({ + installer: cmd.installer, + }); + }); program .command('plugin:ci-test') .description('end-to-end test using bundle in /artifacts') diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts index d57cec19c4a..5466c8b70b9 100644 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts +++ b/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts @@ -1,6 +1,5 @@ import { Task, TaskRunner } from './task'; import { pluginBuildRunner } from './plugin.build'; -import { useSpinner } from '../utils/useSpinner'; import { restoreCwd } from '../utils/cwd'; import { getPluginJson } from '../../config/utils/pluginValidation'; @@ -11,6 +10,7 @@ import fs = require('fs'); export interface PluginCIOptions { platform?: string; + installer?: string; } const calcJavascriptSize = (base: string, files?: string[]): number => { @@ -33,6 +33,33 @@ const calcJavascriptSize = (base: string, files?: string[]): number => { return size; }; +const getWorkFolder = () => { + let dir = `${process.cwd()}/work`; + if (process.env.CIRCLE_JOB) { + dir = path.resolve(dir, process.env.CIRCLE_JOB); + } + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + return dir; +}; + +const writeWorkStats = (startTime: number, workDir: string) => { + const elapsed = Date.now() - startTime; + const stats = { + job: `${process.env.CIRCLE_JOB}`, + startTime, + buildTime: elapsed, + endTime: Date.now(), + }; + const f = path.resolve(workDir, 'stats.json'); + fs.writeFile(f, JSON.stringify(stats, null, 2), err => { + if (err) { + throw new Error('Unable to stats: ' + f); + } + }); +}; + /** * 1. BUILD * @@ -43,42 +70,31 @@ const calcJavascriptSize = (base: string, files?: string[]): number => { */ const buildPluginRunner: TaskRunner = async ({ platform }) => { const start = Date.now(); - const distDir = `${process.cwd()}/dist`; - const buildDir = `${process.cwd()}/ci-work`; - const coverageDir = `${process.cwd()}/coverage`; - - await execa('rimraf', [buildDir]); - fs.mkdirSync(buildDir); + const workDir = getWorkFolder(); + await execa('rimraf', [workDir]); + fs.mkdirSync(workDir); if (platform) { console.log('TODO, backend support?'); - const stub = buildDir + `/bin_${platform}`; - if (!fs.existsSync(stub)) { - fs.mkdirSync(stub, { recursive: true }); - } - fs.writeFile(stub + '/README.txt', 'TODO... build it!', err => { + const file = path.resolve(workDir, 'README.txt'); + fs.writeFile(workDir + '/README.txt', 'TODO... build it!', err => { if (err) { - throw new Error('Unable to write: ' + stub); + throw new Error('Unable to write: ' + file); } }); } else { - // Do regular build process + // Do regular build process with coverage await pluginBuildRunner({ coverage: true }); + const distDir = `${process.cwd()}/dist`; + const coverageDir = `${process.cwd()}/coverage`; + // Move dist & coverage into workspace - fs.renameSync(distDir, path.resolve(buildDir, 'dist')); - fs.renameSync(coverageDir, path.resolve(buildDir, 'coverage')); + fs.renameSync(distDir, path.resolve(workDir, 'dist')); + fs.renameSync(coverageDir, path.resolve(workDir, 'coverage')); } - const elapsed = Date.now() - start; - const stats = { - job: `${process.env.CIRCLE_JOB}`, - sha1: `${process.env.CIRCLE_SHA1}`, - startTime: start, - buildTime: elapsed, - endTime: Date.now(), - }; - console.log('BUILD Info', stats); + writeWorkStats(start, workDir); }; export const ciBuildPluginTask = new Task('Build Plugin', buildPluginRunner); @@ -91,16 +107,18 @@ export const ciBuildPluginTask = new Task('Build Plugin', build */ const bundlePluginRunner: TaskRunner = async () => { const start = Date.now(); - let distDir = `${process.cwd()}/ci-work/dist`; + const workDir = getWorkFolder(); + let distDir = path.resolve(workDir, 'build', 'dist'); if (!fs.existsSync(distDir)) { distDir = `${process.cwd()}/dist`; if (!fs.existsSync(distDir)) { throw new Error('Dist folder does not exist: ' + distDir); } } + // TODO -- merge all the build/xxx/dist folders // Create an artifact - const artifactsDir = `${process.cwd()}/ci-work/artifacts`; + const artifactsDir = path.resolve(workDir, 'artifacts'); if (!fs.existsSync(artifactsDir)) { fs.mkdirSync(artifactsDir, { recursive: true }); } @@ -116,31 +134,96 @@ const bundlePluginRunner: TaskRunner = async () => { if (zipStats.size < 100) { throw new Error('Invalid zip file: ' + zipFile); } + await execa('sha1sum', [zipFile, '>', zipFile + '.sha1']); + const info = { + name: zipName, + size: zipStats.size, + }; + const f = path.resolve(artifactsDir, 'info.json'); + fs.writeFile(f, JSON.stringify(info, null, 2), err => { + if (err) { + throw new Error('Error writing artifact info: ' + f); + } + }); - // Set up the docker folder structure - const dockerDir = `${process.cwd()}/ci-work/docker`; - const pluginFolder = path.resolve(dockerDir, 'plugin'); - fs.mkdirSync(pluginFolder, { recursive: true }); - await execa('unzip', [zipFile, '-d', pluginFolder]); - - const exe = await execa('ls', ['-Rl', pluginFolder]); - console.log('Now load docker from:', exe.stdout); + writeWorkStats(start, workDir); }; export const ciBundlePluginTask = new Task('Bundle Plugin', bundlePluginRunner); /** - * 3. Test (end-to-end) + * 3. Setup (install grafana and setup provisioning) + * + * deploy the zip to a running grafana instance + * + */ +const setupPluginRunner: TaskRunner = async ({ installer }) => { + const start = Date.now(); + + if (!installer) { + throw new Error('Missing installer path'); + } + + // Download the grafana installer + const workDir = getWorkFolder(); + const installFile = path.resolve(workDir, installer); + if (!fs.existsSync(installFile)) { + console.log('download', installer); + const exe = await execa('wget', ['-O', installFile, 'https://dl.grafana.com/oss/release/' + installer]); + console.log(exe.stdout); + } + + // Find the plugin zip file + const artifactsInfo = require(path.resolve(workDir, 'artifacts', 'info.json')); + const pluginZip = path.resolve(workDir, 'artifacts', artifactsInfo.name); + if (!fs.existsSync(pluginZip)) { + throw new Error('Missing zip file:' + pluginZip); + } + + // Create a grafana runtime folder + const grafanaPluginsDir = path.resolve(require('os').homedir(), 'grafana', 'plugins'); + await execa('rimraf', [grafanaPluginsDir]); + fs.mkdirSync(grafanaPluginsDir, { recursive: true }); + + // unzip package.zip -d /opt + let exe = await execa('unzip', [pluginZip, '-d', grafanaPluginsDir]); + console.log(exe.stdout); + + // Write the custom settings + const customIniPath = '/usr/share/grafana/conf/custom.ini'; + const customIniBody = `[paths] \n` + `plugins = ${grafanaPluginsDir}\n` + ''; + fs.writeFile(customIniPath, customIniBody, err => { + if (err) { + throw new Error('Unable to write: ' + customIniPath); + } + }); + + console.log('Install Grafana'); + exe = await execa('sudo', ['dpkg', 'i', installFile]); + console.log(exe.stdout); + + exe = await execa('sudo', ['grafana-server', 'start']); + console.log(exe.stdout); + exe = await execa('grafana-cli', ['--version']); + + writeWorkStats(start, workDir + '_setup'); +}; + +export const ciSetupPluginTask = new Task('Setup Grafana', setupPluginRunner); + +/** + * 4. Test (end-to-end) * * deploy the zip to a running grafana instance * */ const testPluginRunner: TaskRunner = async ({ platform }) => { const start = Date.now(); + const workDir = getWorkFolder(); const args = { withCredentials: true, - baseURL: 'http://localhost:3000/', + baseURL: process.env.GRAFANA_URL || 'http://localhost:3000/', responseType: 'json', auth: { username: 'admin', @@ -168,7 +251,9 @@ const testPluginRunner: TaskRunner = async ({ platform }) => { buildTime: elapsed, endTime: Date.now(), }; - console.log('TODO Test', stats); + + console.log('TODO Puppeteer Tests', stats); + writeWorkStats(start, workDir); }; export const ciTestPluginTask = new Task('Test Plugin (e2e)', testPluginRunner); @@ -200,7 +285,9 @@ const deployPluginRunner: TaskRunner = async () => { buildTime: elapsed, endTime: Date.now(), }; - console.log('TODO DEPLOY', stats); + console.log('TODO DEPLOY??', stats); + console.log(' if PR => write a comment to github with difference '); + console.log(' if master | vXYZ ==> upload artifacts to some repo '); }; export const ciDeployPluginTask = new Task('Deploy plugin', deployPluginRunner); From 905f2c3e163e83baa0eff4c21f41a3f2e84cbb4d Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 9 Jul 2019 23:26:20 -0700 Subject: [PATCH 7/9] Packages: publish packages@6.3.0-alpha.40 --- lerna.json | 2 +- packages/grafana-toolkit/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lerna.json b/lerna.json index b076b4b9c63..109951a7fef 100644 --- a/lerna.json +++ b/lerna.json @@ -2,5 +2,5 @@ "npmClient": "yarn", "useWorkspaces": true, "packages": ["packages/*"], - "version": "6.3.0-alpha.39" + "version": "6.3.0-alpha.40" } diff --git a/packages/grafana-toolkit/package.json b/packages/grafana-toolkit/package.json index fa322196826..5aeea66dc16 100644 --- a/packages/grafana-toolkit/package.json +++ b/packages/grafana-toolkit/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/toolkit", - "version": "6.3.0-alpha.39", + "version": "6.3.0-alpha.40", "description": "Grafana Toolkit", "keywords": [ "grafana", From 724731fddc8a2e31298ff68f9da70e89f734b865 Mon Sep 17 00:00:00 2001 From: ryan Date: Wed, 10 Jul 2019 00:14:18 -0700 Subject: [PATCH 8/9] merge all dist folders into one --- .../grafana-toolkit/src/cli/tasks/plugin.ci.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts index 5466c8b70b9..99628288ff1 100644 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts +++ b/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts @@ -108,14 +108,19 @@ export const ciBuildPluginTask = new Task('Build Plugin', build const bundlePluginRunner: TaskRunner = async () => { const start = Date.now(); const workDir = getWorkFolder(); - let distDir = path.resolve(workDir, 'build', 'dist'); - if (!fs.existsSync(distDir)) { - distDir = `${process.cwd()}/dist`; - if (!fs.existsSync(distDir)) { - throw new Error('Dist folder does not exist: ' + distDir); + + // Copy all `dist` folders to a single dist folder + const distDir = path.resolve(workDir, 'dist'); + fs.mkdirSync(distDir, { recursive: true }); + const dirs = fs.readdirSync(workDir); + for (const dir of dirs) { + if (dir.startsWith('build_')) { + const contents = path.resolve(dir, 'dist'); + if (fs.existsSync(contents)) { + await execa('cp', ['-rp', contents, distDir]); + } } } - // TODO -- merge all the build/xxx/dist folders // Create an artifact const artifactsDir = path.resolve(workDir, 'artifacts'); From 461b97ee80f86533aa7bbe002b00386b774d6da1 Mon Sep 17 00:00:00 2001 From: ryan Date: Wed, 10 Jul 2019 00:33:50 -0700 Subject: [PATCH 9/9] fix folder paths --- .../src/cli/tasks/plugin.ci.ts | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts index 99628288ff1..a30199a70cf 100644 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts +++ b/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts @@ -65,8 +65,11 @@ const writeWorkStats = (startTime: number, workDir: string) => { * * when platform exists it is building backend, otherwise frontend * - * Everything in /ci-work folder + * Each build writes data: + * ~/work/build_xxx/ * + * Anything that should be put into the final zip file should be put in: + * ~/work/build_xxx/dist */ const buildPluginRunner: TaskRunner = async ({ platform }) => { const start = Date.now(); @@ -85,15 +88,13 @@ const buildPluginRunner: TaskRunner = async ({ platform }) => { } else { // Do regular build process with coverage await pluginBuildRunner({ coverage: true }); - - const distDir = `${process.cwd()}/dist`; - const coverageDir = `${process.cwd()}/coverage`; - - // Move dist & coverage into workspace - fs.renameSync(distDir, path.resolve(workDir, 'dist')); - fs.renameSync(coverageDir, path.resolve(workDir, 'coverage')); } + // Move dist to the scoped work folder + const distDir = path.resolve(process.cwd(), 'dist'); + if (fs.existsSync(distDir)) { + fs.renameSync(distDir, path.resolve(workDir, 'dist')); + } writeWorkStats(start, workDir); }; @@ -102,15 +103,19 @@ export const ciBuildPluginTask = new Task('Build Plugin', build /** * 2. BUNDLE * - * Take everything from /ci-work/dist and zip it up + * Take everything from `~/work/build_XXX/dist` and zip it into + * artifacts * */ const bundlePluginRunner: TaskRunner = async () => { const start = Date.now(); const workDir = getWorkFolder(); - // Copy all `dist` folders to a single dist folder - const distDir = path.resolve(workDir, 'dist'); + // Copy all `dist` folders to the root dist folder + const distDir = path.resolve(process.cwd(), 'dist'); + if (!fs.existsSync(distDir)) { + fs.mkdirSync(distDir); + } fs.mkdirSync(distDir, { recursive: true }); const dirs = fs.readdirSync(workDir); for (const dir of dirs) { @@ -123,7 +128,7 @@ const bundlePluginRunner: TaskRunner = async () => { } // Create an artifact - const artifactsDir = path.resolve(workDir, 'artifacts'); + const artifactsDir = path.resolve(process.cwd(), 'artifacts'); if (!fs.existsSync(artifactsDir)) { fs.mkdirSync(artifactsDir, { recursive: true }); } @@ -179,7 +184,8 @@ const setupPluginRunner: TaskRunner = async ({ installer }) => } // Find the plugin zip file - const artifactsInfo = require(path.resolve(workDir, 'artifacts', 'info.json')); + const artifactsDir = path.resolve(process.cwd(), 'artifacts'); + const artifactsInfo = require(path.resolve(artifactsDir, 'info.json')); const pluginZip = path.resolve(workDir, 'artifacts', artifactsInfo.name); if (!fs.existsSync(pluginZip)) { throw new Error('Missing zip file:' + pluginZip);