diff --git a/packages/grafana-toolkit/src/cli/tasks/manifest.test.ts b/packages/grafana-toolkit/src/cli/tasks/manifest.test.ts index 5183f037041..63d0b701313 100644 --- a/packages/grafana-toolkit/src/cli/tasks/manifest.test.ts +++ b/packages/grafana-toolkit/src/cli/tasks/manifest.test.ts @@ -1,8 +1,8 @@ -import { getFilePaths } from './manifest'; +import { getFilesForManifest, convertSha1SumsToManifest } from './manifest'; describe('Manifest', () => { it('should collect file paths', () => { - const info = getFilePaths(__dirname); + const info = getFilesForManifest(__dirname); expect(info).toMatchInlineSnapshot(` Array [ "changelog.ts", @@ -31,4 +31,44 @@ describe('Manifest', () => { ] `); }); + + it('should convert a sha1 sum to manifest structure', () => { + const sha1output = `7df059597099bb7dcf25d2a9aedfaf4465f72d8d LICENSE +4ebed28a02dc029719296aa847bffcea8eb5b9ff README.md +4493f107eb175b085f020c1afea04614232dc0fd gfx_sheets_darwin_amd64 +d8b05884e3829d1389a9c0e4b79b0aba8c19ca4a gfx_sheets_linux_amd64 +88f33db20182e17c72c2823fe3bed87d8c45b0fd gfx_sheets_windows_amd64.exe +e6d8f6704dbe85d5f032d4e8ba44ebc5d4a68c43 img/config-page.png +63d79d0e0f9db21ea168324bd4e180d6892b9d2b img/dashboard.png +7ea6295954b24be55b27320af2074852fb088fa1 img/graph.png +262f2bfddb004c7ce567042e8096f9e033c9b1bd img/query-editor.png +f134ab85caff88b59ea903c5491c6a08c221622f img/sheets.svg +40b8c38cea260caed3cdc01d6e3c1eca483ab5c1 module.js +3c04068eb581f73a262a2081f4adca2edbb14edf module.js.map +bfcae42976f0feca58eed3636655bce51702d3ed plugin.json`; + + const manifest = convertSha1SumsToManifest(sha1output); + + expect(manifest).toMatchInlineSnapshot(` + Object { + "files": Object { + "LICENSE": "7df059597099bb7dcf25d2a9aedfaf4465f72d8d", + "README.md": "4ebed28a02dc029719296aa847bffcea8eb5b9ff", + "gfx_sheets_darwin_amd64": "4493f107eb175b085f020c1afea04614232dc0fd", + "gfx_sheets_linux_amd64": "d8b05884e3829d1389a9c0e4b79b0aba8c19ca4a", + "gfx_sheets_windows_amd64.exe": "88f33db20182e17c72c2823fe3bed87d8c45b0fd", + "img/config-page.png": "e6d8f6704dbe85d5f032d4e8ba44ebc5d4a68c43", + "img/dashboard.png": "63d79d0e0f9db21ea168324bd4e180d6892b9d2b", + "img/graph.png": "7ea6295954b24be55b27320af2074852fb088fa1", + "img/query-editor.png": "262f2bfddb004c7ce567042e8096f9e033c9b1bd", + "img/sheets.svg": "f134ab85caff88b59ea903c5491c6a08c221622f", + "module.js": "40b8c38cea260caed3cdc01d6e3c1eca483ab5c1", + "module.js.map": "3c04068eb581f73a262a2081f4adca2edbb14edf", + "plugin.json": "bfcae42976f0feca58eed3636655bce51702d3ed", + }, + "plugin": "", + "version": "", + } + `); + }); }); diff --git a/packages/grafana-toolkit/src/cli/tasks/manifest.ts b/packages/grafana-toolkit/src/cli/tasks/manifest.ts index f938254d44b..39d1eed8ce9 100644 --- a/packages/grafana-toolkit/src/cli/tasks/manifest.ts +++ b/packages/grafana-toolkit/src/cli/tasks/manifest.ts @@ -2,12 +2,13 @@ import { Task, TaskRunner } from './task'; import fs from 'fs'; import path from 'path'; import execa from 'execa'; +import { ManifestInfo } from '../../plugins/types'; interface ManifestOptions { folder: string; } -export function getFilePaths(root: string, work?: string, acc?: string[]): string[] { +export function getFilesForManifest(root: string, work?: string, acc?: string[]): string[] { if (!acc) { acc = []; } @@ -17,37 +18,78 @@ export function getFilePaths(root: string, work?: string, acc?: string[]): strin const f = path.join(abs, file); const stat = fs.statSync(f); if (stat.isDirectory()) { - acc = getFilePaths(root, f, acc); + acc = getFilesForManifest(root, f, acc); } else { + const idx = f.lastIndexOf('.'); + if (idx > 0) { + // Don't hash images + const suffix = f.substring(idx + 1).toLowerCase(); + if (suffix === 'png' || suffix == 'gif' || suffix === 'svg') { + return; + } + } acc!.push(f.substring(root.length + 1).replace('\\', '/')); } }); return acc; } -const manifestRunner: TaskRunner = async ({ folder }) => { - const filename = 'MANIFEST.txt'; - const files = getFilePaths(folder).filter(f => f !== filename); +export function convertSha1SumsToManifest(sums: string): ManifestInfo { + const files: Record = {}; + for (const line of sums.split(/\r?\n/)) { + const idx = line.indexOf(' '); + if (idx > 0) { + const hash = line.substring(0, idx).trim(); + const path = line.substring(idx + 1).trim(); + files[path] = hash; + } + } + return { + plugin: '', + version: '', + files, + }; +} +const manifestRunner: TaskRunner = async ({ folder }) => { + const GRAFANA_API_KEY = process.env.GRAFANA_API_KEY; + if (!GRAFANA_API_KEY) { + console.log('Plugin signing requires a grafana API key'); + } + const filename = 'MANIFEST.txt'; + const files = getFilesForManifest(folder).filter(f => f !== filename); + + // Run sha1sum const originalDir = __dirname; process.chdir(folder); const { stdout } = await execa('sha1sum', files); - - // Write the process output - fs.writeFileSync(path.join(folder, filename), stdout); - - // Call a signing service - const GRAFANA_API_KEY = process.env.GRAFANA_API_KEY; - if (GRAFANA_API_KEY) { - const pluginPath = path.join(folder, 'plugin.json'); - const plugin = require(pluginPath); - const url = `https://grafana.com/api/plugins/${plugin.id}/sign`; - console.log(`TODO: sign and save: ${url}`); - } - - // Go back to where you were process.chdir(originalDir); - console.log('Wrote manifest: ', filename); + + // Send the manifest to grafana API + const manifest = convertSha1SumsToManifest(stdout); + const outputPath = path.join(folder, filename); + + const pluginPath = path.join(folder, 'plugin.json'); + const plugin = require(pluginPath); + const url = `https://grafana.com/api/plugins/${plugin.id}/sign`; + manifest.plugin = plugin.id; + manifest.version = plugin.version; + + console.log('Request Signature:', url); + const axios = require('axios'); + const info = await axios.post(url, manifest, { + headers: { Authorization: 'Bearer ' + GRAFANA_API_KEY }, + responseType: 'arraybuffer', + }); + if (info.status === 200) { + console.log('OK: ', info.data); + const buffer = new Buffer(info.data, 'binary'); + fs.writeFileSync(outputPath, buffer); + } else { + console.warn('Error: ', info); + console.log('Saving the unsigned manifest'); + fs.writeFileSync(outputPath, JSON.stringify(manifest, null, 2)); + } }; export const manifestTask = new Task('Build Manifest', manifestRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin/test_exclude_image_suffix_in_manifest.png b/packages/grafana-toolkit/src/cli/tasks/plugin/test_exclude_image_suffix_in_manifest.png new file mode 100644 index 00000000000..bb07b075925 --- /dev/null +++ b/packages/grafana-toolkit/src/cli/tasks/plugin/test_exclude_image_suffix_in_manifest.png @@ -0,0 +1 @@ +NOTE: not a real image, but should be excluded from hashed files \ No newline at end of file diff --git a/packages/grafana-toolkit/src/plugins/types.ts b/packages/grafana-toolkit/src/plugins/types.ts index 28c810781a5..bf8d5020120 100644 --- a/packages/grafana-toolkit/src/plugins/types.ts +++ b/packages/grafana-toolkit/src/plugins/types.ts @@ -87,3 +87,11 @@ export interface GitLogInfo { author: UserInfo; commiter: UserInfo; } + +export interface ManifestInfo { + // time: number; << filled in by the server + // keyId: string; << filled in by the server + plugin: string; + version: string; + files: Record; +}