Skip to content

Commit 1543d16

Browse files
committed
fix: resolve GitHub deployment issues
- Fix file path handling to use proper relative paths - Add auto_init for new repositories to avoid 'empty repository' errors - Implement proper tree creation with base trees - Add robust error handling for Git operations - Add detailed logging for debugging
1 parent 5d3fb1d commit 1543d16

File tree

3 files changed

+1130
-1
lines changed

3 files changed

+1130
-1
lines changed
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import { toast } from 'react-toastify';
2+
import { useStore } from '@nanostores/react';
3+
import { workbenchStore } from '~/lib/stores/workbench';
4+
import { webcontainer } from '~/lib/webcontainer';
5+
import { path } from '~/utils/path';
6+
import { useState } from 'react';
7+
import type { ActionCallbackData } from '~/lib/runtime/message-parser';
8+
import { chatId } from '~/lib/persistence/useChatHistory';
9+
import { getLocalStorage } from '~/lib/persistence/localStorage';
10+
11+
export function useGitHubDeploy() {
12+
const [isDeploying, setIsDeploying] = useState(false);
13+
const currentChatId = useStore(chatId);
14+
15+
const handleGitHubDeploy = async () => {
16+
const connection = getLocalStorage('github_connection');
17+
18+
if (!connection?.token || !connection?.user) {
19+
toast.error('Please connect your GitHub account in Settings > Connections first');
20+
return false;
21+
}
22+
23+
if (!currentChatId) {
24+
toast.error('No active chat found');
25+
return false;
26+
}
27+
28+
try {
29+
setIsDeploying(true);
30+
31+
const artifact = workbenchStore.firstArtifact;
32+
33+
if (!artifact) {
34+
throw new Error('No active project found');
35+
}
36+
37+
// Create a deployment artifact for visual feedback
38+
const deploymentId = `deploy-github-project`;
39+
workbenchStore.addArtifact({
40+
id: deploymentId,
41+
messageId: deploymentId,
42+
title: 'GitHub Deployment',
43+
type: 'standalone',
44+
});
45+
46+
const deployArtifact = workbenchStore.artifacts.get()[deploymentId];
47+
48+
// Notify that build is starting
49+
deployArtifact.runner.handleDeployAction('building', 'running', { source: 'github' });
50+
51+
const actionId = 'build-' + Date.now();
52+
const actionData: ActionCallbackData = {
53+
messageId: 'github build',
54+
artifactId: artifact.id,
55+
actionId,
56+
action: {
57+
type: 'build' as const,
58+
content: 'npm run build',
59+
},
60+
};
61+
62+
// Add the action first
63+
artifact.runner.addAction(actionData);
64+
65+
// Then run it
66+
await artifact.runner.runAction(actionData);
67+
68+
if (!artifact.runner.buildOutput) {
69+
// Notify that build failed
70+
deployArtifact.runner.handleDeployAction('building', 'failed', {
71+
error: 'Build failed. Check the terminal for details.',
72+
source: 'github',
73+
});
74+
throw new Error('Build failed');
75+
}
76+
77+
// Notify that build succeeded and deployment preparation is starting
78+
deployArtifact.runner.handleDeployAction('deploying', 'running', {
79+
message: 'Preparing files for GitHub deployment...',
80+
source: 'github'
81+
});
82+
83+
// Get all project files instead of just the build directory since we're deploying to a repository
84+
const container = await webcontainer;
85+
86+
// Get all files recursively - we'll deploy the entire project, not just the build directory
87+
async function getAllFiles(dirPath: string, basePath: string = ''): Promise<Record<string, string>> {
88+
const files: Record<string, string> = {};
89+
const entries = await container.fs.readdir(dirPath, { withFileTypes: true });
90+
91+
for (const entry of entries) {
92+
const fullPath = path.join(dirPath, entry.name);
93+
// Create a relative path without the leading slash for GitHub
94+
const relativePath = basePath ? `${basePath}/${entry.name}` : entry.name;
95+
96+
// Skip node_modules, .git directories and other common excludes
97+
if (entry.isDirectory() && (
98+
entry.name === 'node_modules' ||
99+
entry.name === '.git' ||
100+
entry.name === 'dist' ||
101+
entry.name === 'build' ||
102+
entry.name === '.cache' ||
103+
entry.name === '.next'
104+
)) {
105+
continue;
106+
}
107+
108+
if (entry.isFile()) {
109+
// Skip binary files, large files and other common excludes
110+
if (entry.name.endsWith('.DS_Store') ||
111+
entry.name.endsWith('.log') ||
112+
entry.name.startsWith('.env')) {
113+
continue;
114+
}
115+
116+
try {
117+
const content = await container.fs.readFile(fullPath, 'utf-8');
118+
// Store the file with its relative path, not the full system path
119+
files[relativePath] = content;
120+
} catch (error) {
121+
console.warn(`Could not read file ${fullPath}:`, error);
122+
continue;
123+
}
124+
} else if (entry.isDirectory()) {
125+
const subFiles = await getAllFiles(fullPath, relativePath);
126+
Object.assign(files, subFiles);
127+
}
128+
}
129+
130+
return files;
131+
}
132+
133+
const fileContents = await getAllFiles('/');
134+
135+
// Show GitHub deployment dialog here - it will handle the actual deployment
136+
// and will receive these files to deploy
137+
138+
// For now, we'll just complete the deployment with a success message
139+
// Notify that deployment preparation is complete
140+
deployArtifact.runner.handleDeployAction('deploying', 'complete', {
141+
message: 'Files prepared for GitHub deployment',
142+
source: 'github'
143+
});
144+
145+
return {
146+
success: true,
147+
files: fileContents,
148+
projectName: artifact.title || 'bolt-project'
149+
};
150+
} catch (err) {
151+
console.error('GitHub deploy error:', err);
152+
toast.error(err instanceof Error ? err.message : 'GitHub deployment preparation failed');
153+
return false;
154+
} finally {
155+
setIsDeploying(false);
156+
}
157+
};
158+
159+
return {
160+
isDeploying,
161+
handleGitHubDeploy,
162+
isConnected: !!getLocalStorage('github_connection')?.user,
163+
};
164+
}

0 commit comments

Comments
 (0)