Skip to content

Commit fdc36ad

Browse files
committed
Switch to yauzl for CodeQL CLI
This switches the CodeQL CLI download to `yauzl` instead of `unzipper`. There should be no changes in behavior. I tested this manually on Insiders by removing the distribution directory and this successfully downloaded and extracted the CLI.
1 parent b833591 commit fdc36ad

File tree

4 files changed

+67
-32
lines changed

4 files changed

+67
-32
lines changed

extensions/ql-vscode/src/codeql-cli/distribution.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
import {
1616
codeQlLauncherName,
1717
deprecatedCodeQlLauncherName,
18-
extractZipArchive,
1918
getRequiredAssetName,
2019
} from "../common/distribution";
2120
import {
@@ -26,6 +25,7 @@ import {
2625
showAndLogErrorMessage,
2726
showAndLogWarningMessage,
2827
} from "../common/logging";
28+
import { unzipToDirectory } from "../common/unzip";
2929

3030
/**
3131
* distribution.ts
@@ -420,7 +420,7 @@ class ExtensionSpecificDistributionManager {
420420
void extLogger.log(
421421
`Extracting CodeQL CLI to ${this.getDistributionStoragePath()}`,
422422
);
423-
await extractZipArchive(archivePath, this.getDistributionStoragePath());
423+
await unzipToDirectory(archivePath, this.getDistributionStoragePath());
424424
} finally {
425425
await remove(tmpDirectory);
426426
}

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

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
import { platform } from "os";
2-
import { Open } from "unzipper";
3-
import { join } from "path";
4-
import { pathExists, chmod } from "fs-extra";
52

63
/**
74
* Get the name of the codeql cli installation we prefer to install, based on our current platform.
@@ -19,31 +16,6 @@ export function getRequiredAssetName(): string {
1916
}
2017
}
2118

22-
export async function extractZipArchive(
23-
archivePath: string,
24-
outPath: string,
25-
): Promise<void> {
26-
const archive = await Open.file(archivePath);
27-
await archive.extract({
28-
concurrency: 4,
29-
path: outPath,
30-
});
31-
// Set file permissions for extracted files
32-
await Promise.all(
33-
archive.files.map(async (file) => {
34-
// Only change file permissions if within outPath (path.join normalises the path)
35-
const extractedPath = join(outPath, file.path);
36-
if (
37-
extractedPath.indexOf(outPath) !== 0 ||
38-
!(await pathExists(extractedPath))
39-
) {
40-
return Promise.resolve();
41-
}
42-
return chmod(extractedPath, file.externalFileAttributes >>> 16);
43-
}),
44-
);
45-
}
46-
4719
export function codeQlLauncherName(): string {
4820
return platform() === "win32" ? "codeql.exe" : "codeql";
4921
}

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

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { Entry as ZipEntry, open, Options as ZipOptions, ZipFile } from "yauzl";
22
import { Readable } from "stream";
3+
import { dirname, join } from "path";
4+
import { WriteStream } from "fs";
5+
import { createWriteStream, ensureDir } from "fs-extra";
36

47
// We can't use promisify because it picks up the wrong overload.
58
export function openZip(
@@ -82,3 +85,63 @@ export async function openZipBuffer(
8285
});
8386
});
8487
}
88+
89+
async function copyStream(
90+
readable: Readable,
91+
writeStream: WriteStream,
92+
): Promise<void> {
93+
return new Promise((resolve, reject) => {
94+
readable.on("error", (err) => {
95+
reject(err);
96+
});
97+
readable.on("end", () => {
98+
resolve();
99+
});
100+
101+
readable.pipe(writeStream);
102+
});
103+
}
104+
105+
export async function unzipToDirectory(
106+
archivePath: string,
107+
destinationPath: string,
108+
): Promise<void> {
109+
const zipFile = await openZip(archivePath, {
110+
autoClose: false,
111+
strictFileNames: true,
112+
lazyEntries: true,
113+
});
114+
115+
try {
116+
const entries = await readZipEntries(zipFile);
117+
118+
for (const entry of entries) {
119+
const path = join(destinationPath, entry.fileName);
120+
121+
if (/\/$/.test(entry.fileName)) {
122+
// Directory file names end with '/'
123+
124+
await ensureDir(path);
125+
} else {
126+
// Ensure the directory exists
127+
await ensureDir(dirname(path));
128+
129+
const readable = await openZipReadStream(zipFile, entry);
130+
131+
let mode: number | undefined = entry.externalFileAttributes >>> 16;
132+
if (mode <= 0) {
133+
mode = undefined;
134+
}
135+
136+
const writeStream = createWriteStream(path, {
137+
autoClose: true,
138+
mode,
139+
});
140+
141+
await copyStream(readable, writeStream);
142+
}
143+
}
144+
} finally {
145+
zipFile.close();
146+
}
147+
}

extensions/ql-vscode/test/vscode-tests/ensureCli.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { existsSync, createWriteStream, mkdirpSync } from "fs-extra";
22
import { normalize, join } from "path";
33
import {
44
getRequiredAssetName,
5-
extractZipArchive,
65
codeQlLauncherName,
76
} from "../../src/common/distribution";
7+
import { unzipToDirectory } from "../../src/common/unzip";
88
import fetch from "node-fetch";
99
import supportedCliVersions from "../../supported_cli_versions.json";
1010

@@ -126,7 +126,7 @@ export async function ensureCli(useCli: boolean) {
126126

127127
console.log(`Unzipping into '${unzipDir}'`);
128128
mkdirpSync(unzipDir);
129-
await extractZipArchive(downloadedFilePath, unzipDir);
129+
await unzipToDirectory(downloadedFilePath, unzipDir);
130130
console.log("Done.");
131131
} catch (e) {
132132
console.error("Failed to download CLI.");

0 commit comments

Comments
 (0)