Switch to yauzl for variant analysis results

This commit is contained in:
Koen Vlaswinkel
2023-12-18 15:10:32 +01:00
parent 6cfa0a93c9
commit 770834756a
6 changed files with 139 additions and 19 deletions

View File

@@ -44,6 +44,7 @@
"vscode-languageclient": "^8.0.2",
"vscode-test-adapter-api": "^1.7.0",
"vscode-test-adapter-util": "^0.7.0",
"yauzl": "^2.10.0",
"zip-a-folder": "^3.1.3"
},
"devDependencies": {
@@ -95,6 +96,7 @@
"@types/vscode": "^1.82.0",
"@types/webpack": "^5.28.0",
"@types/webpack-env": "^1.18.0",
"@types/yauzl": "^2.10.3",
"@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.14.0",
"@vscode/test-electron": "^2.2.0",
@@ -8080,6 +8082,15 @@
"integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
"dev": true
},
"node_modules/@types/yauzl": {
"version": "2.10.3",
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
"integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.13.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz",
@@ -15012,7 +15023,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
"dev": true,
"dependencies": {
"pend": "~1.2.0"
}
@@ -24673,8 +24683,7 @@
"node_modules/pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
"dev": true
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
},
"node_modules/picocolors": {
"version": "1.0.0",
@@ -30756,7 +30765,6 @@
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
"dev": true,
"dependencies": {
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"

View File

@@ -1946,6 +1946,7 @@
"vscode-languageclient": "^8.0.2",
"vscode-test-adapter-api": "^1.7.0",
"vscode-test-adapter-util": "^0.7.0",
"yauzl": "^2.10.0",
"zip-a-folder": "^3.1.3"
},
"devDependencies": {
@@ -1997,6 +1998,7 @@
"@types/vscode": "^1.82.0",
"@types/webpack": "^5.28.0",
"@types/webpack-env": "^1.18.0",
"@types/yauzl": "^2.10.3",
"@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.14.0",
"@vscode/test-electron": "^2.2.0",

View File

@@ -0,0 +1,121 @@
import { Entry as ZipEntry, open, Options as ZipOptions, ZipFile } from "yauzl";
import { Readable } from "stream";
import { dirname, join } from "path";
import { WriteStream } from "fs";
import { createWriteStream, ensureDir } from "fs-extra";
// We can't use promisify because it picks up the wrong overload.
function openZip(path: string, options: ZipOptions = {}): Promise<ZipFile> {
return new Promise((resolve, reject) => {
open(path, options, (err, zipFile) => {
if (err) {
reject(err);
return;
}
resolve(zipFile);
});
});
}
function readZipEntries(zipFile: ZipFile): Promise<ZipEntry[]> {
return new Promise((resolve, reject) => {
const files: ZipEntry[] = [];
zipFile.readEntry();
zipFile.on("entry", (entry: ZipEntry) => {
if (/\/$/.test(entry.fileName)) {
// Directory file names end with '/'
// We don't need to do anything for directories.
} else {
files.push(entry);
}
zipFile.readEntry();
});
zipFile.on("end", () => {
resolve(files);
});
zipFile.on("error", (err) => {
reject(err);
});
});
}
function openZipReadStream(
zipFile: ZipFile,
entry: ZipEntry,
): Promise<Readable> {
return new Promise((resolve, reject) => {
zipFile.openReadStream(entry, (err, readStream) => {
if (err) {
reject(err);
return;
}
resolve(readStream);
});
});
}
async function copyStream(
readable: Readable,
writeStream: WriteStream,
): Promise<void> {
return new Promise((resolve, reject) => {
readable.on("error", (err) => {
reject(err);
});
readable.on("end", () => {
resolve();
});
readable.pipe(writeStream);
});
}
export async function unzipToDirectory(
archivePath: string,
destinationPath: string,
): Promise<void> {
const zipFile = await openZip(archivePath, {
autoClose: false,
strictFileNames: true,
lazyEntries: true,
});
try {
const entries = await readZipEntries(zipFile);
for (const entry of entries) {
const path = join(destinationPath, entry.fileName);
if (/\/$/.test(entry.fileName)) {
// Directory file names end with '/'
await ensureDir(path);
} else {
// Ensure the directory exists
await ensureDir(dirname(path));
const readable = await openZipReadStream(zipFile, entry);
let mode: number | undefined = entry.externalFileAttributes >>> 16;
if (mode <= 0) {
mode = undefined;
}
const writeStream = createWriteStream(path, {
autoClose: true,
mode,
});
await copyStream(readable, writeStream);
}
}
} finally {
zipFile.close();
}
}

View File

@@ -1,11 +0,0 @@
import { Open } from "unzipper";
/**
* Unzips a zip file to a directory.
* @param sourcePath The path to the zip file.
* @param destinationPath The path to the directory to unzip to.
*/
export async function unzipFile(sourcePath: string, destinationPath: string) {
const file = await Open.file(sourcePath);
await file.extract({ path: destinationPath });
}

View File

@@ -16,7 +16,7 @@ import {
} from "./shared/variant-analysis";
import { DisposableObject, DisposeHandler } from "../common/disposable-object";
import { EventEmitter } from "vscode";
import { unzipFile } from "../common/zip";
import { unzipToDirectory } from "../common/unzip";
import { readRepoTask, writeRepoTask } from "./repo-tasks-store";
type CacheKey = `${number}/${string}`;
@@ -106,7 +106,7 @@ export class VariantAnalysisResultsManager extends DisposableObject {
VariantAnalysisResultsManager.RESULTS_DIRECTORY,
);
await unzipFile(zipFilePath, unzippedFilesDirectory);
await unzipToDirectory(zipFilePath, unzippedFilesDirectory);
this._onResultDownloaded.fire({
variantAnalysisId,

View File

@@ -54,8 +54,8 @@ describe(VariantAnalysisResultsManager.name, () => {
});
afterEach(async () => {
if (fs.existsSync(variantAnalysisStoragePath)) {
fs.rmSync(variantAnalysisStoragePath, { recursive: true });
if (await fs.pathExists(variantAnalysisStoragePath)) {
await fs.remove(variantAnalysisStoragePath);
}
});