fix boot error
Browse files- Dockerfile +3 -1
- lib/docker/manager.ts +79 -1
- test-fallback.ts +26 -0
Dockerfile
CHANGED
|
@@ -33,7 +33,9 @@ FROM base AS runner
|
|
| 33 |
WORKDIR /app
|
| 34 |
|
| 35 |
# Add libc6-compat and other runtimes for native modules (node-pty)
|
| 36 |
-
|
|
|
|
|
|
|
| 37 |
|
| 38 |
ENV NODE_ENV production
|
| 39 |
ENV NEXT_TELEMETRY_DISABLED 1
|
|
|
|
| 33 |
WORKDIR /app
|
| 34 |
|
| 35 |
# Add libc6-compat and other runtimes for native modules (node-pty)
|
| 36 |
+
# Also add code-server for Native Isolation Mode (when Docker is missing)
|
| 37 |
+
RUN apk add --no-cache libc6-compat python3 make g++ git \
|
| 38 |
+
&& npm install -g code-server --unsafe-perm
|
| 39 |
|
| 40 |
ENV NODE_ENV production
|
| 41 |
ENV NEXT_TELEMETRY_DISABLED 1
|
lib/docker/manager.ts
CHANGED
|
@@ -1,10 +1,23 @@
|
|
| 1 |
import Docker from 'dockerode';
|
| 2 |
import path from 'path';
|
|
|
|
|
|
|
| 3 |
|
| 4 |
// Connect to the local Docker daemon
|
| 5 |
-
// This assumes Docker Desktop or Docker Engine is running locally and accessible
|
| 6 |
const docker = new Docker({ socketPath: process.platform === 'win32' ? '//./pipe/docker_engine' : '/var/run/docker.sock' });
|
| 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
export interface WorkspaceConfig {
|
| 9 |
id: string;
|
| 10 |
userId: string;
|
|
@@ -14,12 +27,69 @@ export interface WorkspaceConfig {
|
|
| 14 |
onLog?: (msg: string) => void;
|
| 15 |
}
|
| 16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
/**
|
| 18 |
* Initializes and starts a Dockerized VS Code Code-Server instance for the given workspace ID.
|
| 19 |
* Optionally spins up a sidecar Android emulator container.
|
| 20 |
*/
|
| 21 |
export async function startWorkspaceContainer(config: WorkspaceConfig) {
|
| 22 |
const { id, userId, projectName, withAndroidEmulator = false, onLog = console.log } = config;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
const containerName = `codeverse-workspace-${id}`;
|
| 24 |
const androidContainerName = `codeverse-android-${id}`;
|
| 25 |
|
|
@@ -191,6 +261,14 @@ export async function startWorkspaceContainer(config: WorkspaceConfig) {
|
|
| 191 |
* Stops and optionally removes a workspace container and its sidecars.
|
| 192 |
*/
|
| 193 |
export async function stopWorkspaceContainer(id: string, remove = false) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
const containerName = `codeverse-workspace-${id}`;
|
| 195 |
const androidContainerName = `codeverse-android-${id}`;
|
| 196 |
|
|
|
|
| 1 |
import Docker from 'dockerode';
|
| 2 |
import path from 'path';
|
| 3 |
+
import { spawn, ChildProcess } from 'child_process';
|
| 4 |
+
import { existsSync } from 'fs';
|
| 5 |
|
| 6 |
// Connect to the local Docker daemon
|
|
|
|
| 7 |
const docker = new Docker({ socketPath: process.platform === 'win32' ? '//./pipe/docker_engine' : '/var/run/docker.sock' });
|
| 8 |
|
| 9 |
+
// Native process registry to manage non-docker workspaces (HF Fallback)
|
| 10 |
+
const nativeProcesses = new Map<string, { process: ChildProcess, port: number }>();
|
| 11 |
+
|
| 12 |
+
async function isDockerAvailable(): Promise<boolean> {
|
| 13 |
+
try {
|
| 14 |
+
await docker.ping();
|
| 15 |
+
return true;
|
| 16 |
+
} catch {
|
| 17 |
+
return false;
|
| 18 |
+
}
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
export interface WorkspaceConfig {
|
| 22 |
id: string;
|
| 23 |
userId: string;
|
|
|
|
| 27 |
onLog?: (msg: string) => void;
|
| 28 |
}
|
| 29 |
|
| 30 |
+
/**
|
| 31 |
+
* Native Mode Fallback: Starts code-server as a child process if Docker is missing.
|
| 32 |
+
*/
|
| 33 |
+
async function startNativeWorkspace(config: WorkspaceConfig) {
|
| 34 |
+
const { id, userId, projectName, onLog = console.log } = config;
|
| 35 |
+
|
| 36 |
+
if (nativeProcesses.has(id)) {
|
| 37 |
+
const existing = nativeProcesses.get(id)!;
|
| 38 |
+
return { success: true, containerId: `native-${id}`, port: String(existing.port) };
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
onLog("[SYSTEM] Docker not detected. Entering Native Isolation Mode...");
|
| 42 |
+
|
| 43 |
+
// HF standard: use /app/workspaces or the resolved mount
|
| 44 |
+
const safeName = projectName.replace(/[^a-zA-Z0-9-_]/g, "-").slice(0, 60);
|
| 45 |
+
const dataPath = path.resolve(process.cwd(), 'workspaces', userId, safeName);
|
| 46 |
+
|
| 47 |
+
// Simple port allocation (multi-workspace on HF isn't common but we handle it)
|
| 48 |
+
const port = 8080 + nativeProcesses.size;
|
| 49 |
+
|
| 50 |
+
onLog(`[NATIVE] Booting code-server for ${projectName} on port ${port}...`);
|
| 51 |
+
|
| 52 |
+
const child = spawn('code-server', [
|
| 53 |
+
'--auth', 'none',
|
| 54 |
+
'--port', String(port),
|
| 55 |
+
'--disable-telemetry',
|
| 56 |
+
'--bind-addr', `0.0.0.0:${port}`,
|
| 57 |
+
dataPath
|
| 58 |
+
], {
|
| 59 |
+
env: { ...process.env, HOME: dataPath },
|
| 60 |
+
shell: true
|
| 61 |
+
});
|
| 62 |
+
|
| 63 |
+
child.stdout.on('data', (data) => onLog(`[NATIVE-STDOUT] ${data}`));
|
| 64 |
+
child.stderr.on('data', (data) => onLog(`[NATIVE-STDERR] ${data}`));
|
| 65 |
+
|
| 66 |
+
nativeProcesses.set(id, { process: child, port });
|
| 67 |
+
|
| 68 |
+
// Give it a moment to bind
|
| 69 |
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
| 70 |
+
|
| 71 |
+
return {
|
| 72 |
+
success: true,
|
| 73 |
+
containerId: `native-${id}`,
|
| 74 |
+
port: String(port),
|
| 75 |
+
androidContainerId: null,
|
| 76 |
+
androidPort: null,
|
| 77 |
+
appetizeUrl: null
|
| 78 |
+
};
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
/**
|
| 82 |
* Initializes and starts a Dockerized VS Code Code-Server instance for the given workspace ID.
|
| 83 |
* Optionally spins up a sidecar Android emulator container.
|
| 84 |
*/
|
| 85 |
export async function startWorkspaceContainer(config: WorkspaceConfig) {
|
| 86 |
const { id, userId, projectName, withAndroidEmulator = false, onLog = console.log } = config;
|
| 87 |
+
|
| 88 |
+
// Check availability first
|
| 89 |
+
if (!await isDockerAvailable()) {
|
| 90 |
+
return startNativeWorkspace(config);
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
const containerName = `codeverse-workspace-${id}`;
|
| 94 |
const androidContainerName = `codeverse-android-${id}`;
|
| 95 |
|
|
|
|
| 261 |
* Stops and optionally removes a workspace container and its sidecars.
|
| 262 |
*/
|
| 263 |
export async function stopWorkspaceContainer(id: string, remove = false) {
|
| 264 |
+
// Check Native Mode first
|
| 265 |
+
if (nativeProcesses.has(id)) {
|
| 266 |
+
const { process: child } = nativeProcesses.get(id)!;
|
| 267 |
+
child.kill();
|
| 268 |
+
nativeProcesses.delete(id);
|
| 269 |
+
return { success: true };
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
const containerName = `codeverse-workspace-${id}`;
|
| 273 |
const androidContainerName = `codeverse-android-${id}`;
|
| 274 |
|
test-fallback.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { startWorkspaceContainer } from './lib/docker/manager';
|
| 2 |
+
|
| 3 |
+
async function testNativeFallback() {
|
| 4 |
+
console.log("--- NATIVE FALLBACK TEST ---");
|
| 5 |
+
try {
|
| 6 |
+
const result = await startWorkspaceContainer({
|
| 7 |
+
id: 'test-id',
|
| 8 |
+
userId: 'test-user',
|
| 9 |
+
projectName: 'test-project',
|
| 10 |
+
onLog: (msg) => console.log(`[TEST-LOG] ${msg}`)
|
| 11 |
+
});
|
| 12 |
+
|
| 13 |
+
console.log("Test Result:", JSON.stringify(result, null, 2));
|
| 14 |
+
|
| 15 |
+
if (result.containerId.startsWith('native-')) {
|
| 16 |
+
console.log("SUCCESS: Fallback to Native Mode detected.");
|
| 17 |
+
} else {
|
| 18 |
+
console.log("INFO: Docker was available, running in Docker mode.");
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
} catch (error) {
|
| 22 |
+
console.error("TEST FAILED:", error);
|
| 23 |
+
}
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
testNativeFallback();
|