Expand software catalog with archived releases
This commit is contained in:
+35
-12
@@ -22,6 +22,7 @@ type Options = {
|
||||
source: string;
|
||||
output: string;
|
||||
replace: boolean;
|
||||
temporaryDirectory: string;
|
||||
};
|
||||
|
||||
type TreeEntry = {
|
||||
@@ -35,17 +36,19 @@ const execFileAsync = promisify(execFile);
|
||||
const supportedExtensions = new Set([".7z", ".exe", ".rar", ".zip"]);
|
||||
|
||||
function usage(): void {
|
||||
console.log(`Usage: pnpm repack-archive [options] <archive-or-exe>
|
||||
console.log(`Usage: pnpm repack-archive [options] <archive-exe-or-directory>
|
||||
|
||||
Creates a Windows XP-compatible ZIP next to the source file.
|
||||
Creates a Windows XP-compatible ZIP next to the source.
|
||||
|
||||
-o, --output <path> write to a specific ZIP path
|
||||
--temp-dir <path> use a specific directory for temporary files
|
||||
--replace delete the source after successful verification
|
||||
--help show this help message`);
|
||||
}
|
||||
|
||||
function parseArguments(args: string[]): Options {
|
||||
let output: string | undefined;
|
||||
let temporaryDirectory = tmpdir();
|
||||
let replace = false;
|
||||
const positional: string[] = [];
|
||||
|
||||
@@ -62,6 +65,13 @@ function parseArguments(args: string[]): Options {
|
||||
throw new Error(`${argument} requires a path`);
|
||||
}
|
||||
index += 1;
|
||||
} else if (argument === "--temp-dir") {
|
||||
const value = args[index + 1];
|
||||
if (value === undefined) {
|
||||
throw new Error(`${argument} requires a path`);
|
||||
}
|
||||
temporaryDirectory = path.resolve(value);
|
||||
index += 1;
|
||||
} else if (argument.startsWith("-")) {
|
||||
throw new Error(`Unknown option: ${argument}`);
|
||||
} else {
|
||||
@@ -70,17 +80,17 @@ function parseArguments(args: string[]): Options {
|
||||
}
|
||||
|
||||
if (positional.length !== 1) {
|
||||
throw new Error("Expected one archive or EXE path");
|
||||
throw new Error("Expected one archive, EXE, or directory path");
|
||||
}
|
||||
|
||||
const source = path.resolve(positional[0]);
|
||||
const extension = path.extname(source).toLowerCase();
|
||||
if (!supportedExtensions.has(extension)) {
|
||||
throw new Error(`Unsupported source type: ${extension || "no extension"}`);
|
||||
}
|
||||
const sourceBasename = supportedExtensions.has(extension)
|
||||
? path.basename(source, extension)
|
||||
: path.basename(source);
|
||||
|
||||
const destination = path.resolve(
|
||||
output ?? path.join(path.dirname(source), `${path.basename(source, extension)}.zip`),
|
||||
output ?? path.join(path.dirname(source), `${sourceBasename}.zip`),
|
||||
);
|
||||
if (path.extname(destination).toLowerCase() !== ".zip") {
|
||||
throw new Error("Output path must have the .zip extension");
|
||||
@@ -89,7 +99,7 @@ function parseArguments(args: string[]): Options {
|
||||
throw new Error("Source and output paths are the same; use --output for ZIP input");
|
||||
}
|
||||
|
||||
return { source, output: destination, replace };
|
||||
return { source, output: destination, replace, temporaryDirectory };
|
||||
}
|
||||
|
||||
async function run(
|
||||
@@ -217,12 +227,23 @@ async function ensureOutputDoesNotExist(output: string): Promise<void> {
|
||||
async function main(): Promise<void> {
|
||||
const options = parseArguments(process.argv.slice(2));
|
||||
const sourceStat = await stat(options.source);
|
||||
if (!sourceStat.isFile()) {
|
||||
throw new Error(`Source is not a regular file: ${options.source}`);
|
||||
if (!sourceStat.isFile() && !sourceStat.isDirectory()) {
|
||||
throw new Error(`Unsupported source type: ${options.source}`);
|
||||
}
|
||||
if (sourceStat.isFile()) {
|
||||
const extension = path.extname(options.source).toLowerCase();
|
||||
if (!supportedExtensions.has(extension)) {
|
||||
throw new Error(`Unsupported source type: ${extension || "no extension"}`);
|
||||
}
|
||||
} else if (options.replace) {
|
||||
throw new Error("--replace is not supported for directory sources");
|
||||
}
|
||||
await ensureOutputDoesNotExist(options.output);
|
||||
|
||||
const temporaryRoot = await mkdtemp(path.join(tmpdir(), "repack-archive-"));
|
||||
await mkdir(options.temporaryDirectory, { recursive: true });
|
||||
const temporaryRoot = await mkdtemp(
|
||||
path.join(options.temporaryDirectory, "repack-archive-"),
|
||||
);
|
||||
const originalDirectory = path.join(temporaryRoot, "original");
|
||||
const verifiedDirectory = path.join(temporaryRoot, "verified");
|
||||
const temporaryZip = path.join(temporaryRoot, "output.zip");
|
||||
@@ -230,7 +251,9 @@ async function main(): Promise<void> {
|
||||
await mkdir(verifiedDirectory);
|
||||
|
||||
try {
|
||||
if (path.extname(options.source).toLowerCase() === ".exe") {
|
||||
if (sourceStat.isDirectory()) {
|
||||
await run("cp", ["-a", "--", path.join(options.source, "."), originalDirectory]);
|
||||
} else if (path.extname(options.source).toLowerCase() === ".exe") {
|
||||
await run("cp", ["-p", "--", options.source, originalDirectory]);
|
||||
} else {
|
||||
await run("7z", ["t", options.source, "-bso0", "-bsp0"]);
|
||||
|
||||
Reference in New Issue
Block a user