Skip to content

Commit

Permalink
use pytorch+nvidia in production build (#25)
Browse files Browse the repository at this point in the history
* add "build-gpu" arg to linux/windows github build actions

* add build-gpu arg to Build step in debug/publish build for linux/windows

* fix prettier line length => 120
  • Loading branch information
telamonian authored Sep 14, 2024
1 parent 42fbc6c commit d765dc5
Show file tree
Hide file tree
Showing 16 changed files with 52 additions and 91 deletions.
7 changes: 6 additions & 1 deletion .github/actions/build/linux/app/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ inputs:
required: false
default: ''
type: string
build-gpu:
description: which gpu to download pytorch for when building the assets bundle
required: false
default: 'cpu'
type: string
runs:
using: composite
steps:
Expand Down Expand Up @@ -52,7 +57,7 @@ runs:
run: |
set -x
pip install comfy-cli
yarn make:assets:cpu
yarn make:assets:${{inputs.build-gpu}}
yarn clean:assets:dev
shell: bash
- name: Make app
Expand Down
7 changes: 6 additions & 1 deletion .github/actions/build/windows/app/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ inputs:
required: false
default: ''
type: string
build-gpu:
description: which gpu to download pytorch for when building the assets bundle
required: false
default: 'cpu'
type: string
runs:
using: composite
steps:
Expand All @@ -37,7 +42,7 @@ runs:
run: |
set -x
pip install comfy-cli
yarn make:assets:cpu
yarn make:assets:${{inputs.build-gpu}}
yarn clean:assets:dev
shell: bash
- name: Make app
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/debug_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
- name: Build
uses: ./.github/actions/build/linux/app
with:
build-gpu: 'cpu'
build-targets: '--targets=@electron-forge/maker-zip'
sign-and-publish: false
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/debug_windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
- name: Build
uses: ./.github/actions/build/windows/app
with:
build-gpu: 'cpu'
build-targets: '--targets=@electron-forge/maker-zip'
sign-and-publish: false
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/publish_windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
- name: Build
uses: ./.github/actions/build/windows/app
with:
build-gpu: 'nvidia'
sign-and-publish: true
DIGICERT_FINGERPRINT: ${{secrets.DIGICERT_FINGERPRINT}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"printWidth": 120,
"semi": true,
"singleQuote": true,
"tabWidth": 2,
Expand Down
5 changes: 1 addition & 4 deletions forge.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,7 @@ const config: ForgeConfig = {
}),
extraResource: ['./assets/ComfyUI', './assets/python.tgz', './assets/UI'],

icon:
process.platform === 'linux'
? 'assets/UI/Comfy_Logo_x128.png'
: 'assets/UI/Comfy_Logo',
icon: process.platform === 'linux' ? 'assets/UI/Comfy_Logo_x128.png' : 'assets/UI/Comfy_Logo',
},
rebuildConfig: {},
hooks: {
Expand Down
8 changes: 2 additions & 6 deletions forge.env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ declare global {
}
}

type VitePluginConfig = ConstructorParameters<
typeof import('@electron-forge/plugin-vite').VitePlugin
>[0];
type VitePluginConfig = ConstructorParameters<typeof import('@electron-forge/plugin-vite').VitePlugin>[0];

interface VitePluginRuntimeKeys {
VITE_DEV_SERVER_URL: `${string}_VITE_DEV_SERVER_URL`;
Expand All @@ -31,9 +29,7 @@ declare global {
}

declare module 'vite' {
interface ConfigEnv<
K extends keyof VitePluginConfig = keyof VitePluginConfig,
> {
interface ConfigEnv<K extends keyof VitePluginConfig = keyof VitePluginConfig> {
root: string;
forgeConfig: VitePluginConfig;
forgeConfigSelf: VitePluginConfig[K][number];
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"clean": "rimraf .vite dist out",
"clean:assets": "rimraf assets/.env assets/ComfyUI assets/python.tgz & yarn run clean:assets:dev",
"clean:assets:dev": "rimraf assets/python assets/override.txt & rimraf assets/cpython*.tar.gz & rimraf assets/requirements.*",
"clean:slate": "yarn run clean && yarn run clean:assets && rimraf node_modules",
"clean:slate": "yarn run clean & yarn run clean:assets & rimraf node_modules",
"lint": "eslint --ext .ts,.tsx .",
"lint:fix": "eslint --fix --ext .ts,.tsx .",
"format": "prettier --check .",
Expand Down
3 changes: 1 addition & 2 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,
Arial, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
margin: auto;
max-width: 38rem;
padding: 2rem;
Expand Down
59 changes: 21 additions & 38 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ const createWindow = async () => {
console.log('Loading Vite Dev Server');
await mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
} else {
mainWindow.loadFile(
path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`)
);
mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));
}

// Set up the System Tray Icon for all platforms
Expand Down Expand Up @@ -103,10 +101,7 @@ const maxFailWait: number = 50 * 1000; // 50seconds
let currentWaitTime = 0;
const spawnServerTimeout: NodeJS.Timeout = null;

const launchPythonServer = async (args: {
userResourcesPath: string;
appResourcesPath: string;
}) => {
const launchPythonServer = async (args: { userResourcesPath: string; appResourcesPath: string }) => {
const { userResourcesPath, appResourcesPath } = args;

const isServerRunning = await isPortInUse(host, port);
Expand All @@ -119,10 +114,7 @@ const launchPythonServer = async (args: {
}, 5000);
clearInterval(serverHeartBeatReference);
webContents.getAllWebContents()[0].loadURL('http://localhost:8188/');
serverHeartBeatReference = setInterval(
serverHeartBeat,
serverHeartBeatInterval
);
serverHeartBeatReference = setInterval(serverHeartBeat, serverHeartBeatInterval);
return Promise.resolve();
}

Expand Down Expand Up @@ -167,20 +159,24 @@ const launchPythonServer = async (args: {

try {
// check for existence of both interpreter and INSTALLER record to ensure a correctly installed python env
await Promise.all([
fsPromises.access(pythonInterpreterPath),
fsPromises.access(pythonRecordPath),
]);
await Promise.all([fsPromises.access(pythonInterpreterPath), fsPromises.access(pythonRecordPath)]);
pythonProcess = spawnPython(comfyMainCmd, path.dirname(scriptPath));
} catch {
console.log('Running one-time python installation on first startup...');
//clean up any possible existing non-functional python env
try {
await fsPromises.rm(pythonRootPath, {recursive: true});
} catch {null;}

const pythonTarPath = path.join(appResourcesPath, 'python.tgz');
await tar.extract({file: pythonTarPath, cwd: userResourcesPath, strict: true});
try {
// clean up any possible existing non-functional python env
await fsPromises.rm(pythonRootPath, { recursive: true });
} catch {
null;
}

const pythonTarPath = path.join(appResourcesPath, 'python.tgz');
await tar.extract({
file: pythonTarPath,
cwd: userResourcesPath,
strict: true,
});

const wheelsPath = path.join(pythonRootPath, 'wheels');
// TODO: report space bug to uv upstream, then revert below mac fix
Expand All @@ -191,9 +187,7 @@ const launchPythonServer = async (args: {
'install',
'--no-index',
'--no-deps',
...(await fsPromises.readdir(wheelsPath)).map((x) =>
path.join(wheelsPath, x)
),
...(await fsPromises.readdir(wheelsPath)).map((x) => path.join(wheelsPath, x)),
];
const rehydrateProc = spawn(pythonInterpreterPath, rehydrateCmd, {
cwd: wheelsPath,
Expand Down Expand Up @@ -229,21 +223,10 @@ const launchPythonServer = async (args: {
sendProgressUpdate(90, 'Finishing...');
console.log('Python server is ready');
// Start the Heartbeat listener, send connected message to Renderer and resolve promise.
serverHeartBeatReference = setInterval(
serverHeartBeat,
serverHeartBeatInterval
);
webContents
.getAllWebContents()[0]
.send('python-server-status', 'active');
serverHeartBeatReference = setInterval(serverHeartBeat, serverHeartBeatInterval);
webContents.getAllWebContents()[0].send('python-server-status', 'active');
//For now just replace the source of the main window to the python server
setTimeout(
() =>
webContents
.getAllWebContents()[0]
.loadURL('http://localhost:8188/'),
1000
);
setTimeout(() => webContents.getAllWebContents()[0].loadURL('http://localhost:8188/'), 1000);
clearTimeout(spawnServerTimeout);
resolve();
} else {
Expand Down
4 changes: 1 addition & 3 deletions src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import { contextBridge, ipcRenderer } from 'electron';
import { IPC_CHANNELS, ELECTRON_BRIDGE_API } from './constants';

const electronAPI = {
onProgressUpdate: (
callback: (update: { percentage: number; status: string }) => void
) => {
onProgressUpdate: (callback: (update: { percentage: number; status: string }) => void) => {
ipcRenderer.on(IPC_CHANNELS.LOADING_PROGRESS, (_event, value) => {
console.log(`Received ${IPC_CHANNELS.LOADING_PROGRESS} event`, value);
callback(value);
Expand Down
4 changes: 1 addition & 3 deletions src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@

import './index.css';
import { IPC_CHANNELS, ELECTRON_BRIDGE_API } from './constants';
console.log(
'👋 This message is being logged by "renderer.ts", included via Vite'
);
console.log('👋 This message is being logged by "renderer.ts", included via Vite');

interface ProgressUpdate {
percentage: number;
Expand Down
12 changes: 2 additions & 10 deletions src/tray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ export function SetupTray(mainView: BrowserWindow): Tray {
const trayImage = path.join(
app.isPackaged ? process.resourcesPath : './assets',
'UI',
process.platform === 'darwin'
? 'Comfy_Logo_x16_BW.png'
: 'Comfy_Logo_x32.png'
process.platform === 'darwin' ? 'Comfy_Logo_x16_BW.png' : 'Comfy_Logo_x32.png'
);
let tray = new Tray(trayImage);

Expand All @@ -19,13 +17,7 @@ export function SetupTray(mainView: BrowserWindow): Tray {
// For Mac you can have a separate icon when you press.
// The current design language for Mac Eco System is White or Black icon then when you click it is in color
if (process.platform === 'darwin') {
tray.setPressedImage(
path.join(
app.isPackaged ? process.resourcesPath : './assets',
'UI',
'Comfy_Logo_x16.png'
)
);
tray.setPressedImage(path.join(app.isPackaged ? process.resourcesPath : './assets', 'UI', 'Comfy_Logo_x16.png'));
}

const contextMenu = Menu.buildFromTemplate([
Expand Down
21 changes: 5 additions & 16 deletions vite.base.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@ import type { AddressInfo } from 'node:net';
import type { ConfigEnv, Plugin, UserConfig } from 'vite';
import pkg from './package.json';

export const builtins = [
'electron',
...builtinModules.map((m) => [m, `node:${m}`]).flat(),
];
export const builtins = ['electron', ...builtinModules.map((m) => [m, `node:${m}`]).flat()];

export const external = [
...builtins,
...Object.keys(
'dependencies' in pkg ? (pkg.dependencies as Record<string, unknown>) : {}
),
...Object.keys('dependencies' in pkg ? (pkg.dependencies as Record<string, unknown>) : {}),
];

export function getBuildConfig(env: ConfigEnv<'build'>): UserConfig {
Expand Down Expand Up @@ -49,18 +44,13 @@ export function getDefineKeys(names: string[]) {

export function getBuildDefine(env: ConfigEnv<'build'>) {
const { command, forgeConfig } = env;
const names = forgeConfig.renderer
.filter(({ name }) => name != null)
.map(({ name }) => name!);
const names = forgeConfig.renderer.filter(({ name }) => name != null).map(({ name }) => name!);
const defineKeys = getDefineKeys(names);
const define = Object.entries(defineKeys).reduce(
(acc, [name, keys]) => {
const { VITE_DEV_SERVER_URL, VITE_NAME } = keys;
const def = {
[VITE_DEV_SERVER_URL]:
command === 'serve'
? JSON.stringify(process.env[VITE_DEV_SERVER_URL])
: undefined,
[VITE_DEV_SERVER_URL]: command === 'serve' ? JSON.stringify(process.env[VITE_DEV_SERVER_URL]) : undefined,
[VITE_NAME]: JSON.stringify(name),
};
return { ...acc, ...def };
Expand All @@ -84,8 +74,7 @@ export function pluginExposeRenderer(name: string): Plugin {
server.httpServer?.once('listening', () => {
const addressInfo = server.httpServer!.address() as AddressInfo;
// Expose env constant for main process use.
process.env[VITE_DEV_SERVER_URL] =
`http://localhost:${addressInfo?.port}`;
process.env[VITE_DEV_SERVER_URL] = `http://localhost:${addressInfo?.port}`;
});
},
};
Expand Down
7 changes: 1 addition & 6 deletions vite.main.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import type { ConfigEnv, UserConfig } from 'vite';
import { defineConfig, mergeConfig } from 'vite';
import {
getBuildConfig,
getBuildDefine,
external,
pluginHotRestart,
} from './vite.base.config';
import { getBuildConfig, getBuildDefine, external, pluginHotRestart } from './vite.base.config';

// https://vitejs.dev/config
export default defineConfig((env) => {
Expand Down

0 comments on commit d765dc5

Please sign in to comment.