Skip to content

Commit 7708347

Browse files
committed
Switch to yauzl for variant analysis results
1 parent 6cfa0a9 commit 7708347

File tree

6 files changed

+139
-19
lines changed

6 files changed

+139
-19
lines changed

extensions/ql-vscode/package-lock.json

Lines changed: 12 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extensions/ql-vscode/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1946,6 +1946,7 @@
19461946
"vscode-languageclient": "^8.0.2",
19471947
"vscode-test-adapter-api": "^1.7.0",
19481948
"vscode-test-adapter-util": "^0.7.0",
1949+
"yauzl": "^2.10.0",
19491950
"zip-a-folder": "^3.1.3"
19501951
},
19511952
"devDependencies": {
@@ -1997,6 +1998,7 @@
19971998
"@types/vscode": "^1.82.0",
19981999
"@types/webpack": "^5.28.0",
19992000
"@types/webpack-env": "^1.18.0",
2001+
"@types/yauzl": "^2.10.3",
20002002
"@typescript-eslint/eslint-plugin": "^6.2.1",
20012003
"@typescript-eslint/parser": "^6.14.0",
20022004
"@vscode/test-electron": "^2.2.0",
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { Entry as ZipEntry, open, Options as ZipOptions, ZipFile } from "yauzl";
2+
import { Readable } from "stream";
3+
import { dirname, join } from "path";
4+
import { WriteStream } from "fs";
5+
import { createWriteStream, ensureDir } from "fs-extra";
6+
7+
// We can't use promisify because it picks up the wrong overload.
8+
function openZip(path: string, options: ZipOptions = {}): Promise<ZipFile> {
9+
return new Promise((resolve, reject) => {
10+
open(path, options, (err, zipFile) => {
11+
if (err) {
12+
reject(err);
13+
return;
14+
}
15+
16+
resolve(zipFile);
17+
});
18+
});
19+
}
20+
21+
function readZipEntries(zipFile: ZipFile): Promise<ZipEntry[]> {
22+
return new Promise((resolve, reject) => {
23+
const files: ZipEntry[] = [];
24+
25+
zipFile.readEntry();
26+
zipFile.on("entry", (entry: ZipEntry) => {
27+
if (/\/$/.test(entry.fileName)) {
28+
// Directory file names end with '/'
29+
// We don't need to do anything for directories.
30+
} else {
31+
files.push(entry);
32+
}
33+
34+
zipFile.readEntry();
35+
});
36+
37+
zipFile.on("end", () => {
38+
resolve(files);
39+
});
40+
41+
zipFile.on("error", (err) => {
42+
reject(err);
43+
});
44+
});
45+
}
46+
47+
function openZipReadStream(
48+
zipFile: ZipFile,
49+
entry: ZipEntry,
50+
): Promise<Readable> {
51+
return new Promise((resolve, reject) => {
52+
zipFile.openReadStream(entry, (err, readStream) => {
53+
if (err) {
54+
reject(err);
55+
return;
56+
}
57+
58+
resolve(readStream);
59+
});
60+
});
61+
}
62+
63+
async function copyStream(
64+
readable: Readable,
65+
writeStream: WriteStream,
66+
): Promise<void> {
67+
return new Promise((resolve, reject) => {
68+
readable.on("error", (err) => {
69+
reject(err);
70+
});
71+
readable.on("end", () => {
72+
resolve();
73+
});
74+
75+
readable.pipe(writeStream);
76+
});
77+
}
78+
79+
export async function unzipToDirectory(
80+
archivePath: string,
81+
destinationPath: string,
82+
): Promise<void> {
83+
const zipFile = await openZip(archivePath, {
84+
autoClose: false,
85+
strictFileNames: true,
86+
lazyEntries: true,
87+
});
88+
89+
try {
90+
const entries = await readZipEntries(zipFile);
91+
92+
for (const entry of entries) {
93+
const path = join(destinationPath, entry.fileName);
94+
95+
if (/\/$/.test(entry.fileName)) {
96+
// Directory file names end with '/'
97+
98+
await ensureDir(path);
99+
} else {
100+
// Ensure the directory exists
101+
await ensureDir(dirname(path));
102+
103+
const readable = await openZipReadStream(zipFile, entry);
104+
105+
let mode: number | undefined = entry.externalFileAttributes >>> 16;
106+
if (mode <= 0) {
107+
mode = undefined;
108+
}
109+
110+
const writeStream = createWriteStream(path, {
111+
autoClose: true,
112+
mode,
113+
});
114+
115+
await copyStream(readable, writeStream);
116+
}
117+
}
118+
} finally {
119+
zipFile.close();
120+
}
121+
}

extensions/ql-vscode/src/common/zip.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.

extensions/ql-vscode/src/variant-analysis/variant-analysis-results-manager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
} from "./shared/variant-analysis";
1717
import { DisposableObject, DisposeHandler } from "../common/disposable-object";
1818
import { EventEmitter } from "vscode";
19-
import { unzipFile } from "../common/zip";
19+
import { unzipToDirectory } from "../common/unzip";
2020
import { readRepoTask, writeRepoTask } from "./repo-tasks-store";
2121

2222
type CacheKey = `${number}/${string}`;
@@ -106,7 +106,7 @@ export class VariantAnalysisResultsManager extends DisposableObject {
106106
VariantAnalysisResultsManager.RESULTS_DIRECTORY,
107107
);
108108

109-
await unzipFile(zipFilePath, unzippedFilesDirectory);
109+
await unzipToDirectory(zipFilePath, unzippedFilesDirectory);
110110

111111
this._onResultDownloaded.fire({
112112
variantAnalysisId,

extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-results-manager.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ describe(VariantAnalysisResultsManager.name, () => {
5454
});
5555

5656
afterEach(async () => {
57-
if (fs.existsSync(variantAnalysisStoragePath)) {
58-
fs.rmSync(variantAnalysisStoragePath, { recursive: true });
57+
if (await fs.pathExists(variantAnalysisStoragePath)) {
58+
await fs.remove(variantAnalysisStoragePath);
5959
}
6060
});
6161

0 commit comments

Comments
 (0)