Commit 227b233a by Ryan McKinley Committed by GitHub

Toolkit: save a json manifest file for signing (#23446)

parent 692a7387
import { getFilePaths } from './manifest'; import { getFilesForManifest, convertSha1SumsToManifest } from './manifest';
describe('Manifest', () => { describe('Manifest', () => {
it('should collect file paths', () => { it('should collect file paths', () => {
const info = getFilePaths(__dirname); const info = getFilesForManifest(__dirname);
expect(info).toMatchInlineSnapshot(` expect(info).toMatchInlineSnapshot(`
Array [ Array [
"changelog.ts", "changelog.ts",
...@@ -31,4 +31,44 @@ describe('Manifest', () => { ...@@ -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": "<?>",
}
`);
});
}); });
...@@ -2,12 +2,13 @@ import { Task, TaskRunner } from './task'; ...@@ -2,12 +2,13 @@ import { Task, TaskRunner } from './task';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import execa from 'execa'; import execa from 'execa';
import { ManifestInfo } from '../../plugins/types';
interface ManifestOptions { interface ManifestOptions {
folder: string; folder: string;
} }
export function getFilePaths(root: string, work?: string, acc?: string[]): string[] { export function getFilesForManifest(root: string, work?: string, acc?: string[]): string[] {
if (!acc) { if (!acc) {
acc = []; acc = [];
} }
...@@ -17,37 +18,78 @@ export function getFilePaths(root: string, work?: string, acc?: string[]): strin ...@@ -17,37 +18,78 @@ export function getFilePaths(root: string, work?: string, acc?: string[]): strin
const f = path.join(abs, file); const f = path.join(abs, file);
const stat = fs.statSync(f); const stat = fs.statSync(f);
if (stat.isDirectory()) { if (stat.isDirectory()) {
acc = getFilePaths(root, f, acc); acc = getFilesForManifest(root, f, acc);
} else { } 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('\\', '/')); acc!.push(f.substring(root.length + 1).replace('\\', '/'));
} }
}); });
return acc; return acc;
} }
export function convertSha1SumsToManifest(sums: string): ManifestInfo {
const files: Record<string, string> = {};
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<ManifestOptions> = async ({ folder }) => { const manifestRunner: TaskRunner<ManifestOptions> = 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 filename = 'MANIFEST.txt';
const files = getFilePaths(folder).filter(f => f !== filename); const files = getFilesForManifest(folder).filter(f => f !== filename);
// Run sha1sum
const originalDir = __dirname; const originalDir = __dirname;
process.chdir(folder); process.chdir(folder);
const { stdout } = await execa('sha1sum', files); const { stdout } = await execa('sha1sum', files);
process.chdir(originalDir);
// Write the process output // Send the manifest to grafana API
fs.writeFileSync(path.join(folder, filename), stdout); const manifest = convertSha1SumsToManifest(stdout);
const outputPath = path.join(folder, filename);
// Call a signing service const pluginPath = path.join(folder, 'plugin.json');
const GRAFANA_API_KEY = process.env.GRAFANA_API_KEY; const plugin = require(pluginPath);
if (GRAFANA_API_KEY) { const url = `https://grafana.com/api/plugins/${plugin.id}/sign`;
const pluginPath = path.join(folder, 'plugin.json'); manifest.plugin = plugin.id;
const plugin = require(pluginPath); manifest.version = plugin.version;
const url = `https://grafana.com/api/plugins/${plugin.id}/sign`;
console.log(`TODO: sign and save: ${url}`);
}
// Go back to where you were console.log('Request Signature:', url);
process.chdir(originalDir); const axios = require('axios');
console.log('Wrote manifest: ', filename); 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<ManifestOptions>('Build Manifest', manifestRunner); export const manifestTask = new Task<ManifestOptions>('Build Manifest', manifestRunner);
NOTE: not a real image, but should be excluded from hashed files
\ No newline at end of file
...@@ -87,3 +87,11 @@ export interface GitLogInfo { ...@@ -87,3 +87,11 @@ export interface GitLogInfo {
author: UserInfo; author: UserInfo;
commiter: 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<string, string>;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment