diff --git a/src/common/utils/tar.ts b/src/common/utils/tar.ts index f9876e2b27..659210e682 100644 --- a/src/common/utils/tar.ts +++ b/src/common/utils/tar.ts @@ -3,37 +3,46 @@ import tar, { ExtractOptions, FileStat } from "tar"; import path from "path"; -export interface ReadFileFromTarOpts { +export interface ReadFileFromTarOpts { tarPath: string; filePath: string; - parseJson?: boolean; + parse: (buf: Buffer) => R; } -export function readFileFromTar({ tarPath, filePath, parseJson }: ReadFileFromTarOpts): Promise { - return new Promise(async (resolve, reject) => { +/** + * This function is useful for when you want the raw buffer from `readFileFromTar` + * @param buf the input buffer from reading a file from a tar ball + */ +export function passBuffer(buf: Buffer): Buffer { + return buf; +} + +export function readFileFromTar({ tarPath, filePath, parse }: ReadFileFromTarOpts): Promise { + return new Promise((resolve, reject) => { const fileChunks: Buffer[] = []; - await tar.list({ + tar.list({ file: tarPath, filter: entryPath => path.normalize(entryPath) === filePath, onentry(entry: FileStat) { entry.on("data", chunk => { - fileChunks.push(chunk); + if (chunk instanceof Buffer) { + fileChunks.push(chunk); + } else if (typeof chunk === "string") { + fileChunks.push(Buffer.from(chunk)); + } }); entry.once("error", err => { reject(new Error(`reading file has failed ${entry.path}: ${err}`)); }); entry.once("end", () => { - const data = Buffer.concat(fileChunks); - const result = parseJson ? JSON.parse(data.toString("utf8")) : data; - - resolve(result); + resolve(parse(Buffer.concat(fileChunks))); }); }, }); if (!fileChunks.length) { - reject(new Error("Not found")); + reject(new Error(`File not found: ${filePath}`)); } }); } diff --git a/src/renderer/components/+extensions/extensions.tsx b/src/renderer/components/+extensions/extensions.tsx index f30168145a..67f0837034 100644 --- a/src/renderer/components/+extensions/extensions.tsx +++ b/src/renderer/components/+extensions/extensions.tsx @@ -39,6 +39,13 @@ interface InstallRequestValidated extends InstallRequestPreloaded { tempFile: string; // temp system path to packed extension for unpacking } +function parseLensExtensionManifest(buf: Buffer): LensExtensionManifest { + const raw = JSON.parse(buf.toString("utf8")); + + // todo: do verification + return raw; +} + @observer export class Extensions extends React.Component { private static supportedFormats = ["tar", "tgz"]; @@ -241,10 +248,10 @@ export class Extensions extends React.Component { throw new Error(`invalid extension bundle, ${manifestFilename} not found`); } - const manifest = await readFileFromTar({ + const manifest = await readFileFromTar({ tarPath: filePath, filePath: manifestLocation, - parseJson: true, + parse: parseLensExtensionManifest, }); if (!manifest.lens && !manifest.renderer) { @@ -298,7 +305,7 @@ export class Extensions extends React.Component { } async requestInstall(init: InstallRequest | InstallRequest[]) { - const requests = Array.isArray(init) ? init : [init]; + const requests = [init].flat(); const preloadedRequests = await this.preloadExtensions(requests); const validatedRequests = await this.createTempFilesAndValidate(preloadedRequests);