convertor/src/helpers/printVersions.ts

170 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { exec } from "node:child_process";
import { readFile } from "node:fs/promises";
import { version } from "../../package.json";
console.log(`ConvertX v${version}`);
// 帶 timeout 的 exec 包裝函數,防止 CI 環境卡住
const execWithTimeout = (
cmd: string,
timeoutMs = 5000,
): Promise<{ stdout: string; stderr: string; error: Error | null }> => {
return new Promise((resolve) => {
const child = exec(cmd, { timeout: timeoutMs }, (error, stdout, stderr) => {
resolve({ stdout: stdout || "", stderr: stderr || "", error });
});
// 設定額外保險 timeout
const timer = setTimeout(() => {
child.kill("SIGTERM");
resolve({ stdout: "", stderr: "", error: new Error(`Command timeout: ${cmd}`) });
}, timeoutMs + 1000);
// 正常完成時清除 timer
child.on("close", () => clearTimeout(timer));
});
};
// 定義所有要檢查的工具
const tools = [
{
cmd: "pandoc -v",
name: "Pandoc",
errorMsg: "Pandoc is not installed.",
formatter: (s: string) => s.split("\n")[0],
},
{
cmd: "ffmpeg -version",
name: "FFmpeg",
errorMsg: "FFmpeg is not installed.",
formatter: (s: string) => s.split("\n")[0],
},
{
cmd: "vips -v",
name: "Vips",
errorMsg: "Vips is not installed.",
formatter: (s: string) => s.split("\n")[0],
},
{
cmd: "convert --version",
name: "ImageMagick",
errorMsg: "ImageMagick is not installed.",
formatter: (s: string) => s.split("\n")[0]?.replace("Version: ", ""),
},
{
cmd: "gm version",
name: "GraphicsMagick",
errorMsg: "GraphicsMagick is not installed.",
formatter: (s: string) => s.split("\n")[0],
},
{
cmd: "inkscape --version",
name: "Inkscape",
errorMsg: "Inkscape is not installed.",
formatter: (s: string) => s.split("\n")[0],
},
{
cmd: "djxl --version",
name: "libjxl",
errorMsg: "libjxl-tools is not installed.",
formatter: (s: string) => s.split("\n")[0],
},
{
cmd: "dasel --version",
name: "dasel",
errorMsg: "dasel is not installed.",
formatter: (s: string) => s.split("\n")[0],
},
{
cmd: "xelatex -version",
name: "XeTeX",
errorMsg: "Tex Live with XeTeX is not installed.",
formatter: (s: string) => s.split("\n")[0],
},
{
cmd: "resvg -V",
name: "resvg",
errorMsg: "resvg is not installed",
formatter: (s: string) => `resvg v${s.split("\n")[0]}`,
},
{
cmd: "assimp version",
name: "assimp",
errorMsg: "assimp is not installed",
formatter: (s: string) => `assimp ${s.split("\n")[5]}`,
},
{
cmd: "ebook-convert --version",
name: "calibre",
errorMsg: "ebook-convert (calibre) is not installed",
formatter: (s: string) => s.split("\n")[0],
},
{
cmd: "dpkg -l libheif1 | tail -1 | awk '{print $3}'",
name: "libheif",
errorMsg: "libheif is not installed",
formatter: (s: string) => `libheif v${s.trim()}`,
},
{
cmd: "potrace -v",
name: "potrace",
errorMsg: "potrace is not installed",
formatter: (s: string) => s.split("\n")[0],
},
{
cmd: "soffice --version",
name: "LibreOffice",
errorMsg: "libreoffice is not installed",
formatter: (s: string) => s.split("\n")[0],
},
{
cmd: "which msgconvert && dpkg -l libemail-outlook-message-perl | tail -1 | awk '{print $3}'",
name: "msgconvert",
errorMsg: "msgconvert (libemail-outlook-message-perl) is not installed",
formatter: (s: string) => {
const lines = s.split("\n").filter((l) => l.trim());
return `msgconvert v${lines[lines.length - 1] || "unknown"}`;
},
},
{
cmd: "bun -v",
name: "Bun",
errorMsg: "Bun is not installed. wait what",
formatter: (s: string) => `Bun v${s.split("\n")[0]}`,
},
];
if (process.env.NODE_ENV === "production") {
(async () => {
// 讀取 OS 資訊
try {
const osRelease = await readFile("/etc/os-release", "utf8");
const prettyName = osRelease.split('PRETTY_NAME="')[1]?.split('"')[0];
if (prettyName) console.log(prettyName);
} catch {
console.error("Not running on docker, this is not supported.");
}
// 並行執行所有工具版本檢查,每個都有 timeout
const results = await Promise.allSettled(
tools.map(async (tool) => {
const { stdout, stderr, error } = await execWithTimeout(tool.cmd, 5000);
return { tool, stdout, stderr, error };
}),
);
// 輸出結果
for (const result of results) {
if (result.status === "fulfilled") {
const { tool, stdout, stderr, error } = result.value;
if (error) {
console.error(tool.errorMsg);
} else {
// 有些工具把版本資訊輸出到 stderr例如 djxl
const output = stdout || stderr;
if (output) {
console.log(tool.formatter(output));
}
}
}
}
})();
}