diff --git a/src/shared/zip-entry-listing.ts b/src/shared/zip-entry-listing.ts index 730713c84..299ca4452 100644 --- a/src/shared/zip-entry-listing.ts +++ b/src/shared/zip-entry-listing.ts @@ -1,4 +1,4 @@ -import { spawn } from "bun" +import { spawn, spawnSync } from "bun" import type { ArchiveEntry } from "./archive-entry-validator" @@ -48,6 +48,56 @@ export async function listZipEntriesWithTar(archivePath: string): Promise entry !== null) } +export function isPythonZipListingAvailable(): boolean { + const proc = spawnSync(["python3", "--version"], { + stdout: "ignore", + stderr: "ignore", + }) + + return proc.exitCode === 0 +} + +export async function listZipEntriesWithPython(archivePath: string): Promise { + const script = [ + "import json, stat, sys, zipfile", + "entries = []", + "with zipfile.ZipFile(sys.argv[1], 'r') as archive:", + " for info in archive.infolist():", + " mode = (info.external_attr >> 16) & 0xFFFF", + " if stat.S_ISLNK(mode):", + " entry_type = 'symlink'", + " link_path = archive.read(info).decode('utf-8', 'surrogateescape')", + " elif info.filename.endswith('/'):", + " entry_type = 'directory'", + " link_path = None", + " else:", + " entry_type = 'file'", + " link_path = None", + " entry = {'path': info.filename, 'type': entry_type}", + " if link_path is not None:", + " entry['linkPath'] = link_path", + " entries.append(entry)", + "print(json.dumps(entries))", + ].join("\n") + + const proc = spawn(["python3", "-c", script, archivePath], { + stdout: "pipe", + stderr: "pipe", + }) + + const [exitCode, stdout, stderr] = await Promise.all([ + proc.exited, + new Response(proc.stdout).text(), + new Response(proc.stderr).text(), + ]) + + if (exitCode !== 0) { + throw new Error(`zip entry listing failed (exit ${exitCode}): ${stderr}`) + } + + return JSON.parse(stdout) as ArchiveEntry[] +} + export async function listZipEntriesWithPowerShell( archivePath: string, escapePowerShellPath: (path: string) => string, diff --git a/src/shared/zip-extractor.ts b/src/shared/zip-extractor.ts index 58da48ebf..8bb77b42c 100644 --- a/src/shared/zip-extractor.ts +++ b/src/shared/zip-extractor.ts @@ -2,7 +2,12 @@ import { spawn, spawnSync } from "bun" import { release } from "os" import { validateArchiveEntries } from "./archive-entry-validator" -import { listZipEntriesWithPowerShell, listZipEntriesWithTar } from "./zip-entry-listing" +import { + isPythonZipListingAvailable, + listZipEntriesWithPowerShell, + listZipEntriesWithPython, + listZipEntriesWithTar, +} from "./zip-entry-listing" const WINDOWS_BUILD_WITH_TAR = 17134 @@ -98,5 +103,9 @@ async function listZipEntries(archivePath: string) { return listZipEntriesWithPowerShell(archivePath, escapePowerShellPath, extractor) } + if (isPythonZipListingAvailable()) { + return listZipEntriesWithPython(archivePath) + } + return listZipEntriesWithTar(archivePath) }