From 0a81f0db234d1154b239df85a67285ac6c54c030 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Tue, 10 Mar 2026 09:00:27 +0100 Subject: [PATCH] feat(linux): bundle Python CLI and fix Electron packaged build - contrib/linux/Dockerfile: add libpython3.11 + PyInstaller to build standalone cli binary; fix venv isolation via .dockerignore - frontend/electron/main.cjs: use bundled cli binary in prod via process.resourcesPath, fallback to venv/python3 in dev - frontend/package.json: add extraResources for cli binary, set output dir to release/ - frontend/vite.config.js: set base './' for file:// protocol in prod - .dockerignore: exclude venv/, node_modules/, dist/, .git/ - .gitignore: ignore release/ output directories --- .dockerignore | 10 ++++++++++ .gitignore | 2 ++ contrib/linux/Dockerfile | 31 +++++++++++++++++++++++++++++++ contrib/linux/build.sh | 18 ++++++++++++++++++ frontend/electron/main.cjs | 23 +++++++++++++---------- frontend/index.html | 2 +- frontend/package.json | 12 +++++++++--- frontend/vite.config.js | 1 + 8 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 .dockerignore create mode 100644 contrib/linux/Dockerfile create mode 100755 contrib/linux/build.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..141e71a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +venv/ +.venv/ +__pycache__/ +*.pyc +node_modules/ +frontend/node_modules/ +frontend/dist/ +frontend/release/ +release/ +.git/ diff --git a/.gitignore b/.gitignore index bf09b78..ef329e5 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ __pycache__/ node_modules/ frontend/node_modules/ frontend/dist/ +frontend/release/ +release/ diff --git a/contrib/linux/Dockerfile b/contrib/linux/Dockerfile new file mode 100644 index 0000000..22b7725 --- /dev/null +++ b/contrib/linux/Dockerfile @@ -0,0 +1,31 @@ +FROM node:22.14.0-bookworm + +# System dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3 python3-pip python3-venv libpython3.11 \ + binutils \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /build + +# Copy repo +COPY . . + +# Python venv + dependencies +RUN python3 -m venv venv && \ + venv/bin/pip install --no-cache-dir -r requirements.txt + +# Compile Python CLI into a standalone binary +RUN venv/bin/pip install --no-cache-dir pyinstaller && \ + venv/bin/pyinstaller --onefile --name cli src/cli.py && \ + mkdir -p frontend/resources && \ + cp dist/cli frontend/resources/cli + +# JS dependencies + electron-builder +RUN cd frontend && npm ci && npm install --no-save electron-builder + +# Build +RUN cd frontend && npx vite build && npx electron-builder --linux AppImage --publish never + +# Export AppImage +CMD cp frontend/release/*.AppImage /out/ diff --git a/contrib/linux/build.sh b/contrib/linux/build.sh new file mode 100755 index 0000000..5d66c8d --- /dev/null +++ b/contrib/linux/build.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_ROOT=$(cd "$(dirname "$0")/../.." && pwd) +OUT_DIR="$REPO_ROOT/release" + +mkdir -p "$OUT_DIR" + +docker build -t wallet-generator-builder \ + -f "$REPO_ROOT/contrib/linux/Dockerfile" \ + "$REPO_ROOT" + +docker run --rm \ + -v "$OUT_DIR:/out" \ + wallet-generator-builder + +echo "AppImage saved to: $OUT_DIR" +ls "$OUT_DIR"/*.AppImage diff --git a/frontend/electron/main.cjs b/frontend/electron/main.cjs index 15bbd7c..86a1902 100644 --- a/frontend/electron/main.cjs +++ b/frontend/electron/main.cjs @@ -48,21 +48,24 @@ function resolveWalletFile(kind, filename) { return filePath } -// ── Resolve Python executable ────────────────────────────────────────────────── -function findPython() { +// ── Resolve CLI command (dev: python + cli.py, prod: bundled binary) ────────── +function getCliCommand() { + if (!isDev) { + const bundled = path.join(process.resourcesPath, 'cli') + if (fs.existsSync(bundled)) return { exec: bundled, baseArgs: [] } + } const repoRoot = path.join(__dirname, '..', '..') - const venvPython = path.join(repoRoot, 'venv', 'bin', 'python') - return fs.existsSync(venvPython) ? venvPython : 'python3' + const python = fs.existsSync(path.join(repoRoot, 'venv', 'bin', 'python')) + ? path.join(repoRoot, 'venv', 'bin', 'python') + : 'python3' + return { exec: python, baseArgs: [path.join(repoRoot, 'src', 'cli.py')] } } // ── Call Python CLI ──────────────────────────────────────────────────────────── function callPython(command, args = {}) { return new Promise((resolve, reject) => { - const python = findPython() - const repoRoot = path.join(__dirname, '..', '..') - const cliPath = path.join(repoRoot, 'src', 'cli.py') - - execFile(python, [cliPath, command, JSON.stringify(args)], { cwd: repoRoot }, (err, stdout, stderr) => { + const { exec, baseArgs } = getCliCommand() + execFile(exec, [...baseArgs, command, JSON.stringify(args)], (err, stdout, stderr) => { if (err) { reject(new Error(stderr || err.message)); return } try { const result = JSON.parse(stdout.trim()) @@ -139,7 +142,7 @@ function createWindow() { height: 750, minWidth: 320, minHeight: 480, - title: 'Wallet Generator', + title: 'wallet-gen', backgroundColor: '#0f1117', icon: path.join(__dirname, '..', 'public', 'icons', 'icon.png'), webPreferences: { diff --git a/frontend/index.html b/frontend/index.html index e8c63f8..6584924 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - Wallet Generator + wallet-gen
diff --git a/frontend/package.json b/frontend/package.json index d24c815..7ff4bbe 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,5 +1,5 @@ { - "name": "wallet-generator", + "name": "wallet-gen", "private": true, "version": "1.0.0", "type": "module", @@ -12,10 +12,16 @@ "dist": "vite build && electron-builder" }, "build": { - "appId": "com.walletgenerator.app", - "productName": "Wallet Generator", + "appId": "com.walletgen.app", + "productName": "wallet-gen", "icon": "public/icons/icon.png", "files": ["dist/**", "electron/**"], + "extraResources": [ + { "from": "resources/cli", "to": "cli" } + ], + "directories": { + "output": "release" + }, "linux": { "target": "AppImage", "category": "Finance" diff --git a/frontend/vite.config.js b/frontend/vite.config.js index abccf69..b2fb280 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -3,6 +3,7 @@ import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], + base: './', server: { port: 5173, },