|
|
@@ -1,28 +1,43 @@
|
|
|
import { Task, TaskRunner } from './task';
|
|
|
import { pluginBuildRunner } from './plugin.build';
|
|
|
import { restoreCwd } from '../utils/cwd';
|
|
|
+import { S3Client } from '../../plugins/aws';
|
|
|
import { getPluginJson } from '../../config/utils/pluginValidation';
|
|
|
+import { PluginMeta } from '@grafana/ui';
|
|
|
|
|
|
// @ts-ignore
|
|
|
import execa = require('execa');
|
|
|
import path = require('path');
|
|
|
-import fs = require('fs');
|
|
|
-import { getPackageDetails } from '../utils/fileHelper';
|
|
|
+import fs from 'fs';
|
|
|
+import { getPackageDetails, findImagesInFolder, appendPluginHistory } from '../../plugins/utils';
|
|
|
import {
|
|
|
job,
|
|
|
getJobFolder,
|
|
|
writeJobStats,
|
|
|
getCiFolder,
|
|
|
- agregateWorkflowInfo,
|
|
|
- agregateCoverageInfo,
|
|
|
- getPluginSourceInfo,
|
|
|
- TestResultInfo,
|
|
|
- agregateTestInfo,
|
|
|
-} from './plugin/ci';
|
|
|
+ getPluginBuildInfo,
|
|
|
+ getBuildNumber,
|
|
|
+ getPullRequestNumber,
|
|
|
+ getCircleDownloadBaseURL,
|
|
|
+} from '../../plugins/env';
|
|
|
+import { agregateWorkflowInfo, agregateCoverageInfo, agregateTestInfo } from '../../plugins/workflow';
|
|
|
+import {
|
|
|
+ PluginPackageDetails,
|
|
|
+ PluginBuildReport,
|
|
|
+ PluginHistory,
|
|
|
+ defaultPluginHistory,
|
|
|
+ TestResultsInfo,
|
|
|
+ PluginDevInfo,
|
|
|
+ PluginDevSummary,
|
|
|
+ DevSummary,
|
|
|
+} from '../../plugins/types';
|
|
|
+import { runEndToEndTests } from '../../plugins/e2e/launcher';
|
|
|
+import { getEndToEndSettings } from '../../plugins/index';
|
|
|
|
|
|
export interface PluginCIOptions {
|
|
|
backend?: string;
|
|
|
full?: boolean;
|
|
|
+ upload?: boolean;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -77,7 +92,8 @@ export const ciBuildPluginTask = new Task<PluginCIOptions>('Build Plugin', build
|
|
|
const buildPluginDocsRunner: TaskRunner<PluginCIOptions> = async () => {
|
|
|
const docsSrc = path.resolve(process.cwd(), 'docs');
|
|
|
if (!fs.existsSync(docsSrc)) {
|
|
|
- throw new Error('Docs folder does not exist!');
|
|
|
+ console.log('No docs src');
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
const start = Date.now();
|
|
|
@@ -146,7 +162,7 @@ const packagePluginRunner: TaskRunner<PluginCIOptions> = async () => {
|
|
|
console.log('Save the source info in plugin.json');
|
|
|
const pluginJsonFile = path.resolve(distDir, 'plugin.json');
|
|
|
const pluginInfo = getPluginJson(pluginJsonFile);
|
|
|
- (pluginInfo.info as any).source = await getPluginSourceInfo();
|
|
|
+ pluginInfo.info.build = await getPluginBuildInfo();
|
|
|
fs.writeFile(pluginJsonFile, JSON.stringify(pluginInfo, null, 2), err => {
|
|
|
if (err) {
|
|
|
throw new Error('Error writing: ' + pluginJsonFile);
|
|
|
@@ -165,7 +181,7 @@ const packagePluginRunner: TaskRunner<PluginCIOptions> = async () => {
|
|
|
throw new Error('Invalid zip file: ' + zipFile);
|
|
|
}
|
|
|
|
|
|
- const info: any = {
|
|
|
+ const info: PluginPackageDetails = {
|
|
|
plugin: await getPackageDetails(zipFile, distDir),
|
|
|
};
|
|
|
|
|
|
@@ -220,8 +236,7 @@ export const ciPackagePluginTask = new Task<PluginCIOptions>('Bundle Plugin', pa
|
|
|
const testPluginRunner: TaskRunner<PluginCIOptions> = async ({ full }) => {
|
|
|
const start = Date.now();
|
|
|
const workDir = getJobFolder();
|
|
|
- const pluginInfo = getPluginJson(`${process.cwd()}/ci/dist/plugin.json`);
|
|
|
- const results: TestResultInfo = { job };
|
|
|
+ const results: TestResultsInfo = { job, passed: 0, failed: 0, screenshots: [] };
|
|
|
const args = {
|
|
|
withCredentials: true,
|
|
|
baseURL: process.env.BASE_URL || 'http://localhost:3000/',
|
|
|
@@ -232,6 +247,14 @@ const testPluginRunner: TaskRunner<PluginCIOptions> = async ({ full }) => {
|
|
|
},
|
|
|
};
|
|
|
|
|
|
+ const settings = getEndToEndSettings();
|
|
|
+ await execa('rimraf', [settings.outputFolder]);
|
|
|
+ fs.mkdirSync(settings.outputFolder);
|
|
|
+
|
|
|
+ const tempDir = path.resolve(process.cwd(), 'e2e-temp');
|
|
|
+ await execa('rimraf', [tempDir]);
|
|
|
+ fs.mkdirSync(tempDir);
|
|
|
+
|
|
|
try {
|
|
|
const axios = require('axios');
|
|
|
const frontendSettings = await axios.get('api/frontend/settings', args);
|
|
|
@@ -239,17 +262,37 @@ const testPluginRunner: TaskRunner<PluginCIOptions> = async ({ full }) => {
|
|
|
|
|
|
console.log('Grafana: ' + JSON.stringify(results.grafana, null, 2));
|
|
|
|
|
|
- const pluginSettings = await axios.get(`api/plugins/${pluginInfo.id}/settings`, args);
|
|
|
- console.log('Plugin Info: ' + JSON.stringify(pluginSettings.data, null, 2));
|
|
|
+ const loadedMetaRsp = await axios.get(`api/plugins/${settings.plugin.id}/settings`, args);
|
|
|
+ const loadedMeta: PluginMeta = loadedMetaRsp.data;
|
|
|
+ console.log('Plugin Info: ' + JSON.stringify(loadedMeta, null, 2));
|
|
|
+ if (loadedMeta.info.build) {
|
|
|
+ const currentHash = settings.plugin.info.build!.hash;
|
|
|
+ console.log('Check version: ', settings.plugin.info.build);
|
|
|
+ if (loadedMeta.info.build.hash !== currentHash) {
|
|
|
+ console.warn(`Testing wrong plugin version. Expected: ${currentHash}, found: ${loadedMeta.info.build.hash}`);
|
|
|
+ throw new Error('Wrong plugin version');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!fs.existsSync('e2e-temp')) {
|
|
|
+ fs.mkdirSync(tempDir);
|
|
|
+ }
|
|
|
|
|
|
- console.log('TODO Puppeteer Tests', workDir);
|
|
|
+ await execa('cp', [
|
|
|
+ 'node_modules/@grafana/toolkit/src/plugins/e2e/commonPluginTests.ts',
|
|
|
+ path.resolve(tempDir, 'common.test.ts'),
|
|
|
+ ]);
|
|
|
|
|
|
- results.status = 'TODO... puppeteer';
|
|
|
+ await runEndToEndTests(settings.outputFolder, results);
|
|
|
} catch (err) {
|
|
|
results.error = err;
|
|
|
- results.status = 'EXCEPTION Thrown';
|
|
|
console.log('Test Error', err);
|
|
|
}
|
|
|
+ await execa('rimraf', [tempDir]);
|
|
|
+
|
|
|
+ // Now copy everything to work folder
|
|
|
+ await execa('cp', ['-rv', settings.outputFolder + '/.', workDir]);
|
|
|
+ results.screenshots = findImagesInFolder(workDir);
|
|
|
|
|
|
const f = path.resolve(workDir, 'results.json');
|
|
|
fs.writeFile(f, JSON.stringify(results, null, 2), err => {
|
|
|
@@ -267,31 +310,117 @@ export const ciTestPluginTask = new Task<PluginCIOptions>('Test Plugin (e2e)', t
|
|
|
* 4. Report
|
|
|
*
|
|
|
* Create a report from all the previous steps
|
|
|
- *
|
|
|
*/
|
|
|
-const pluginReportRunner: TaskRunner<PluginCIOptions> = async () => {
|
|
|
+const pluginReportRunner: TaskRunner<PluginCIOptions> = async ({ upload }) => {
|
|
|
const ciDir = path.resolve(process.cwd(), 'ci');
|
|
|
- const packageInfo = require(path.resolve(ciDir, 'packages', 'info.json'));
|
|
|
+ const packageDir = path.resolve(ciDir, 'packages');
|
|
|
+ const packageInfo = require(path.resolve(packageDir, 'info.json')) as PluginPackageDetails;
|
|
|
|
|
|
- console.log('Save the source info in plugin.json');
|
|
|
const pluginJsonFile = path.resolve(ciDir, 'dist', 'plugin.json');
|
|
|
- const report = {
|
|
|
- plugin: getPluginJson(pluginJsonFile),
|
|
|
+ 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(),
|
|
|
};
|
|
|
+ const pr = getPullRequestNumber();
|
|
|
+ if (pr) {
|
|
|
+ report.pullRequest = pr;
|
|
|
+ }
|
|
|
|
|
|
- console.log('REPORT', report);
|
|
|
-
|
|
|
+ // Save the report to disk
|
|
|
const file = path.resolve(ciDir, 'report.json');
|
|
|
fs.writeFile(file, JSON.stringify(report, null, 2), err => {
|
|
|
if (err) {
|
|
|
throw new Error('Unable to write: ' + file);
|
|
|
}
|
|
|
});
|
|
|
- console.log('TODO... notify some service');
|
|
|
+
|
|
|
+ console.log('Initalizing S3 Client');
|
|
|
+ const s3 = new S3Client();
|
|
|
+
|
|
|
+ const build = pluginMeta.info.build;
|
|
|
+ if (!build) {
|
|
|
+ throw new Error('Metadata missing build info');
|
|
|
+ }
|
|
|
+
|
|
|
+ const version = pluginMeta.info.version || 'unknown';
|
|
|
+ const branch = build.branch || 'unknown';
|
|
|
+ const buildNumber = getBuildNumber();
|
|
|
+ const root = `dev/${pluginMeta.id}`;
|
|
|
+ const dirKey = pr ? `${root}/pr/${pr}` : `${root}/branch/${branch}/${buildNumber}`;
|
|
|
+
|
|
|
+ const jobKey = `${dirKey}/index.json`;
|
|
|
+ if (await s3.exits(jobKey)) {
|
|
|
+ throw new Error('Job already registered: ' + jobKey);
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('Write Job', jobKey);
|
|
|
+ await s3.writeJSON(jobKey, report, {
|
|
|
+ Tagging: `version=${version}&type=${pluginMeta.type}`,
|
|
|
+ });
|
|
|
+
|
|
|
+ // Upload logo
|
|
|
+ const logo = await s3.uploadLogo(report.plugin.info, {
|
|
|
+ local: path.resolve(ciDir, 'dist'),
|
|
|
+ remote: root,
|
|
|
+ });
|
|
|
+
|
|
|
+ const latest: PluginDevInfo = {
|
|
|
+ pluginId: pluginMeta.id,
|
|
|
+ name: pluginMeta.name,
|
|
|
+ logo,
|
|
|
+ build: pluginMeta.info.build!,
|
|
|
+ version,
|
|
|
+ };
|
|
|
+
|
|
|
+ if (pr) {
|
|
|
+ latest.build.pr = pr;
|
|
|
+ } else {
|
|
|
+ latest.build.number = buildNumber;
|
|
|
+ const base = `${root}/branch/${branch}/`;
|
|
|
+ const historyKey = base + `history.json`;
|
|
|
+ console.log('Read', historyKey);
|
|
|
+ const history: PluginHistory = await s3.readJSON(historyKey, defaultPluginHistory);
|
|
|
+ appendPluginHistory(report, latest, history);
|
|
|
+
|
|
|
+ await s3.writeJSON(historyKey, history);
|
|
|
+ console.log('wrote history');
|
|
|
+ }
|
|
|
+
|
|
|
+ // Private things may want to upload
|
|
|
+ if (upload) {
|
|
|
+ s3.uploadPackages(packageInfo, {
|
|
|
+ local: packageDir,
|
|
|
+ remote: dirKey + '/packages',
|
|
|
+ });
|
|
|
+
|
|
|
+ s3.uploadTestFiles(report.tests, {
|
|
|
+ local: ciDir,
|
|
|
+ remote: dirKey,
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('Update Directory Indexes');
|
|
|
+
|
|
|
+ let indexKey = `${root}/index.json`;
|
|
|
+ const index: PluginDevSummary = await s3.readJSON(indexKey, { branch: {}, pr: {} });
|
|
|
+ if (pr) {
|
|
|
+ index.pr[pr] = latest;
|
|
|
+ } else {
|
|
|
+ index.branch[branch] = latest;
|
|
|
+ }
|
|
|
+ await s3.writeJSON(indexKey, index);
|
|
|
+
|
|
|
+ indexKey = `dev/index.json`;
|
|
|
+ const pluginIndex: DevSummary = await s3.readJSON(indexKey, {});
|
|
|
+ pluginIndex[pluginMeta.id] = latest;
|
|
|
+ await s3.writeJSON(indexKey, pluginIndex);
|
|
|
+ console.log('wrote index');
|
|
|
};
|
|
|
|
|
|
export const ciPluginReportTask = new Task<PluginCIOptions>('Generate Plugin Report', pluginReportRunner);
|