chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
const REPO_ROOT = path.resolve(__dirname, '..');
|
||||
const PACKAGE_JSON = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'package.json'), 'utf8'));
|
||||
const PRODUCT_NAME = PACKAGE_JSON.build?.productName || 'Open Generative AI';
|
||||
const PACKAGE_NAME = 'open-generative-ai';
|
||||
const COMMAND_NAME = 'open-generative-ai';
|
||||
const INSTALL_DIR_NAME = PACKAGE_NAME;
|
||||
const LINUX_DEPENDS = [
|
||||
'libasound2t64 | libasound2',
|
||||
'libatk-bridge2.0-0',
|
||||
'libatk1.0-0',
|
||||
'libc6',
|
||||
'libcairo2',
|
||||
'libcups2t64 | libcups2',
|
||||
'libdrm2',
|
||||
'libgbm1',
|
||||
'libglib2.0-0',
|
||||
'libgtk-3-0',
|
||||
'libnspr4',
|
||||
'libnss3',
|
||||
'libpango-1.0-0',
|
||||
'libx11-6',
|
||||
'libx11-xcb1',
|
||||
'libxcb-dri3-0',
|
||||
'libxcomposite1',
|
||||
'libxdamage1',
|
||||
'libxext6',
|
||||
'libxfixes3',
|
||||
'libxkbcommon0',
|
||||
'libxrandr2',
|
||||
'xdg-utils',
|
||||
].join(', ');
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = {};
|
||||
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const arg = argv[i];
|
||||
if (!arg.startsWith('--')) continue;
|
||||
|
||||
const [rawKey, inlineValue] = arg.slice(2).split('=');
|
||||
if (inlineValue !== undefined) {
|
||||
args[rawKey] = inlineValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
const nextValue = argv[i + 1];
|
||||
if (nextValue && !nextValue.startsWith('--')) {
|
||||
args[rawKey] = nextValue;
|
||||
i += 1;
|
||||
} else {
|
||||
args[rawKey] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
function toDebArch(arch) {
|
||||
if (arch === 'x64') return 'amd64';
|
||||
if (arch === 'arm64') return 'arm64';
|
||||
return arch;
|
||||
}
|
||||
|
||||
function getDefaultAppDir(arch) {
|
||||
const folderName = arch === 'arm64' ? 'linux-arm64-unpacked' : 'linux-unpacked';
|
||||
return path.join(REPO_ROOT, 'release', folderName);
|
||||
}
|
||||
|
||||
function detectExecutableName(appDir) {
|
||||
const preferredNames = [PRODUCT_NAME, PACKAGE_JSON.productName, PACKAGE_JSON.name]
|
||||
.filter(Boolean);
|
||||
|
||||
for (const fileName of preferredNames) {
|
||||
const fullPath = path.join(appDir, fileName);
|
||||
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
|
||||
const denyList = new Set([
|
||||
'chrome-sandbox',
|
||||
'chrome_crashpad_handler',
|
||||
]);
|
||||
|
||||
const candidates = fs.readdirSync(appDir, { withFileTypes: true })
|
||||
.filter((entry) => entry.isFile())
|
||||
.map((entry) => entry.name)
|
||||
.filter((name) => !denyList.has(name))
|
||||
.filter((name) => !name.endsWith('.so') && !name.includes('.so.'))
|
||||
.filter((name) => (fs.statSync(path.join(appDir, name)).mode & 0o111) !== 0);
|
||||
|
||||
if (candidates.length === 0) {
|
||||
throw new Error(`Could not detect the packaged executable in ${appDir}`);
|
||||
}
|
||||
|
||||
return candidates[0];
|
||||
}
|
||||
|
||||
function writeFile(targetPath, contents, mode) {
|
||||
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
||||
fs.writeFileSync(targetPath, contents);
|
||||
if (mode) fs.chmodSync(targetPath, mode);
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (process.platform !== 'linux') {
|
||||
console.error('This packaging script must be run on Linux.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
const arch = args.arch || process.arch;
|
||||
const debArch = toDebArch(arch);
|
||||
const appDir = path.resolve(args['app-dir'] || getDefaultAppDir(arch));
|
||||
const outputDir = path.resolve(args['output-dir'] || path.join(REPO_ROOT, 'release'));
|
||||
const version = args.version || PACKAGE_JSON.version;
|
||||
|
||||
if (!fs.existsSync(appDir)) {
|
||||
console.error(`Packaged app directory not found: ${appDir}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const executableName = detectExecutableName(appDir);
|
||||
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'oga-deb-'));
|
||||
const packageRoot = path.join(tempRoot, 'pkgroot');
|
||||
const installDir = path.join(packageRoot, 'opt', INSTALL_DIR_NAME);
|
||||
const wrapperPath = path.join(packageRoot, 'usr', 'bin', COMMAND_NAME);
|
||||
const desktopPath = path.join(packageRoot, 'usr', 'share', 'applications', `${PACKAGE_NAME}.desktop`);
|
||||
const iconPath = path.join(packageRoot, 'usr', 'share', 'pixmaps', `${PACKAGE_NAME}.png`);
|
||||
const controlPath = path.join(packageRoot, 'DEBIAN', 'control');
|
||||
const outputPath = path.join(outputDir, `${PACKAGE_NAME}_${version}_${debArch}.deb`);
|
||||
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
fs.cpSync(appDir, installDir, { recursive: true });
|
||||
|
||||
const chromeSandboxPath = path.join(installDir, 'chrome-sandbox');
|
||||
if (fs.existsSync(chromeSandboxPath)) {
|
||||
fs.chmodSync(chromeSandboxPath, 0o4755);
|
||||
}
|
||||
|
||||
writeFile(
|
||||
wrapperPath,
|
||||
`#!/bin/sh\nexec "/opt/${INSTALL_DIR_NAME}/${executableName}" "$@"\n`,
|
||||
0o755
|
||||
);
|
||||
|
||||
writeFile(
|
||||
desktopPath,
|
||||
`[Desktop Entry]
|
||||
Name=${PRODUCT_NAME}
|
||||
Exec=${COMMAND_NAME}
|
||||
Icon=${PACKAGE_NAME}
|
||||
Type=Application
|
||||
Categories=Graphics;Utility;
|
||||
Terminal=false
|
||||
StartupNotify=true
|
||||
`,
|
||||
0o644
|
||||
);
|
||||
|
||||
fs.mkdirSync(path.dirname(iconPath), { recursive: true });
|
||||
fs.copyFileSync(path.join(REPO_ROOT, 'public', 'banner.png'), iconPath);
|
||||
fs.chmodSync(iconPath, 0o644);
|
||||
|
||||
writeFile(
|
||||
controlPath,
|
||||
`Package: ${PACKAGE_NAME}
|
||||
Version: ${version}
|
||||
Section: graphics
|
||||
Priority: optional
|
||||
Architecture: ${debArch}
|
||||
Maintainer: Open Generative AI Team
|
||||
Depends: ${LINUX_DEPENDS}
|
||||
Description: Local-first generative AI studio for image, video, and design workflows
|
||||
`,
|
||||
0o644
|
||||
);
|
||||
|
||||
execFileSync('dpkg-deb', ['--build', '--root-owner-group', packageRoot, outputPath], {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
fs.rmSync(tempRoot, { recursive: true, force: true });
|
||||
console.log(`Created ${outputPath}`);
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user