Fix scaffold copy and better error handling for create app (#202)

I was over-eager in https://github.com/dyad-sh/dyad/pull/200 and removed
copyDirectoryRecursive which is actually needed to copy the scaffold
out.
This commit is contained in:
Will Chen
2025-05-19 17:58:49 -07:00
committed by GitHub
parent b4b9556e2c
commit 4d2b4783bc
3 changed files with 60 additions and 42 deletions

View File

@@ -12,7 +12,10 @@ import { promises as fsPromises } from "node:fs";
// Import our utility modules // Import our utility modules
import { withLock } from "../utils/lock_utils"; import { withLock } from "../utils/lock_utils";
import { getFilesRecursively } from "../utils/file_utils"; import {
copyDirectoryRecursive,
getFilesRecursively,
} from "../utils/file_utils";
import { import {
runningApps, runningApps,
processCounter, processCounter,
@@ -187,50 +190,43 @@ export function registerAppHandlers() {
}) })
.returning(); .returning();
// Start async operations in background // Why do we not use fs.cp here?
try { // Because scaffold is inside ASAR and it does NOT
await fsPromises.cp( // behave like a regular directory if you use fs.cp
path.join(__dirname, "..", "..", "scaffold"), // https://www.electronjs.org/docs/latest/tutorial/asar-archives#limitations-of-the-node-api
fullAppPath, await copyDirectoryRecursive(
{ path.join(__dirname, "..", "..", "scaffold"),
recursive: true, fullAppPath,
// Scaffold should *not* have node_modules anyways, but );
// just in case, we filter it out. // Initialize git repo and create first commit
filter: (source) => !source.includes("node_modules"), await git.init({
}, fs: fs,
); dir: fullAppPath,
// Initialize git repo and create first commit defaultBranch: "main",
await git.init({ });
fs: fs,
dir: fullAppPath,
defaultBranch: "main",
});
// Stage all files // Stage all files
await git.add({ await git.add({
fs: fs, fs: fs,
dir: fullAppPath, dir: fullAppPath,
filepath: ".", filepath: ".",
}); });
// Create initial commit // Create initial commit
const commitHash = await git.commit({ const commitHash = await git.commit({
fs: fs, fs: fs,
dir: fullAppPath, dir: fullAppPath,
message: "Init Dyad app", message: "Init Dyad app",
author: await getGitAuthor(), author: await getGitAuthor(),
}); });
// Update chat with initial commit hash // Update chat with initial commit hash
await db await db
.update(chats) .update(chats)
.set({ .set({
initialCommitHash: commitHash, initialCommitHash: commitHash,
}) })
.where(eq(chats.id, chat.id)); .where(eq(chats.id, chat.id));
} catch (error) {
logger.error("Error in background app initialization:", error);
}
return { app, chatId: chat.id }; return { app, chatId: chat.id };
}, },

View File

@@ -1,4 +1,5 @@
import fs from "node:fs"; import fs from "node:fs";
import { promises as fsPromises } from "node:fs";
import path from "node:path"; import path from "node:path";
/** /**
@@ -31,3 +32,22 @@ export function getFilesRecursively(dir: string, baseDir: string): string[] {
return files; return files;
} }
export async function copyDirectoryRecursive(
source: string,
destination: string,
) {
await fsPromises.mkdir(destination, { recursive: true });
const entries = await fsPromises.readdir(source, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path.join(source, entry.name);
const destPath = path.join(destination, entry.name);
if (entry.isDirectory()) {
await copyDirectoryRecursive(srcPath, destPath);
} else {
await fsPromises.copyFile(srcPath, destPath);
}
}
}

View File

@@ -25,6 +25,7 @@ import { useTheme } from "@/contexts/ThemeContext";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { ExternalLink } from "lucide-react"; import { ExternalLink } from "lucide-react";
import { ImportAppButton } from "@/components/ImportAppButton"; import { ImportAppButton } from "@/components/ImportAppButton";
import { showError } from "@/lib/toast";
// Adding an export for attachments // Adding an export for attachments
export interface HomeSubmitOptions { export interface HomeSubmitOptions {
@@ -133,6 +134,7 @@ export default function HomePage() {
navigate({ to: "/chat", search: { id: result.chatId } }); navigate({ to: "/chat", search: { id: result.chatId } });
} catch (error) { } catch (error) {
console.error("Failed to create chat:", error); console.error("Failed to create chat:", error);
showError("Failed to create app. " + (error as any).toString());
setIsLoading(false); // Ensure loading state is reset on error setIsLoading(false); // Ensure loading state is reset on error
} }
// No finally block needed for setIsLoading(false) here if navigation happens on success // No finally block needed for setIsLoading(false) here if navigation happens on success