feat: allow custom install and start commands (#892)

# Description

Gives the ability to define an `install` and `startup` command when
importing a project, so we can work on a project locally without any
issue.

# Preview

<img width="2256" height="1422" alt="image"
src="https://github.com/user-attachments/assets/2132b1cb-5f71-4b88-84db-8ecc81cf1f66"
/>

---------

Co-authored-by: Will Chen <willchen90@gmail.com>
This commit is contained in:
Olyno
2025-08-18 19:41:22 +02:00
committed by GitHub
parent f72157a443
commit 237017acd9
10 changed files with 693 additions and 13 deletions

View File

@@ -83,17 +83,28 @@ async function executeApp({
appId,
event, // Keep event for local-node case
isNeon,
installCommand,
startCommand,
}: {
appPath: string;
appId: number;
event: Electron.IpcMainInvokeEvent;
isNeon: boolean;
installCommand?: string | null;
startCommand?: string | null;
}): Promise<void> {
if (proxyWorker) {
proxyWorker.terminate();
proxyWorker = null;
}
await executeAppLocalNode({ appPath, appId, event, isNeon });
await executeAppLocalNode({
appPath,
appId,
event,
isNeon,
installCommand,
startCommand,
});
}
async function executeAppLocalNode({
@@ -101,22 +112,28 @@ async function executeAppLocalNode({
appId,
event,
isNeon,
installCommand,
startCommand,
}: {
appPath: string;
appId: number;
event: Electron.IpcMainInvokeEvent;
isNeon: boolean;
installCommand?: string | null;
startCommand?: string | null;
}): Promise<void> {
const spawnedProcess = spawn(
"(pnpm install && pnpm run dev --port 32100) || (npm install --legacy-peer-deps && npm run dev -- --port 32100)",
[],
{
cwd: appPath,
shell: true,
stdio: "pipe", // Ensure stdio is piped so we can capture output/errors and detect close
detached: false, // Ensure child process is attached to the main process lifecycle unless explicitly backgrounded
},
);
const defaultCommand =
"(pnpm install && pnpm run dev --port 32100) || (npm install --legacy-peer-deps && npm run dev -- --port 32100)";
const hasCustomCommands = !!installCommand?.trim() && !!startCommand?.trim();
const command = hasCustomCommands
? `${installCommand!.trim()} && ${startCommand!.trim()}`
: defaultCommand;
const spawnedProcess = spawn(command, [], {
cwd: appPath,
shell: true,
stdio: "pipe", // Ensure stdio is piped so we can capture output/errors and detect close
detached: false, // Ensure child process is attached to the main process lifecycle unless explicitly backgrounded
});
// Check if process spawned correctly
if (!spawnedProcess.pid) {
@@ -375,6 +392,8 @@ export function registerAppHandlers() {
supabaseProjectId: null,
githubOrg: null,
githubRepo: null,
installCommand: originalApp.installCommand,
startCommand: originalApp.startCommand,
})
.returning();
@@ -511,6 +530,8 @@ export function registerAppHandlers() {
appId,
event,
isNeon: !!app.neonProjectId,
installCommand: app.installCommand,
startCommand: app.startCommand,
});
return;
@@ -646,6 +667,8 @@ export function registerAppHandlers() {
appId,
event,
isNeon: !!app.neonProjectId,
installCommand: app.installCommand,
startCommand: app.startCommand,
}); // This will handle starting either mode
return;

View File

@@ -69,7 +69,12 @@ export function registerImportHandlers() {
"import-app",
async (
_,
{ path: sourcePath, appName }: ImportAppParams,
{
path: sourcePath,
appName,
installCommand,
startCommand,
}: ImportAppParams,
): Promise<ImportAppResult> => {
// Validate the source path exists
try {
@@ -128,6 +133,8 @@ export function registerImportHandlers() {
name: appName,
// Use the name as the path for now
path: appName,
installCommand: installCommand ?? null,
startCommand: startCommand ?? null,
})
.returning();

View File

@@ -96,6 +96,8 @@ export interface App {
vercelProjectName: string | null;
vercelTeamSlug: string | null;
vercelDeploymentUrl: string | null;
installCommand: string | null;
startCommand: string | null;
}
export interface Version {
@@ -226,6 +228,8 @@ export interface ApproveProposalResult {
export interface ImportAppParams {
path: string;
appName: string;
installCommand?: string;
startCommand?: string;
}
export interface CopyAppParams {