adbrasi commited on
Commit
b65eae4
ยท
verified ยท
1 Parent(s): a4c7ee6

Upload modal_dev_env.py

Browse files
Files changed (1) hide show
  1. modal_dev_env.py +138 -0
modal_dev_env.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import subprocess
3
+ import time
4
+ import signal
5
+ import sys
6
+
7
+ import modal
8
+
9
+ app = modal.App("dev-environment")
10
+
11
+ # Build optimized image with JupyterLab and code-server
12
+ image = (
13
+ modal.Image.debian_slim(python_version="3.12")
14
+ .apt_install(
15
+ "curl", "git", "wget", "vim", "htop",
16
+ "build-essential", # for compiling Python packages
17
+ "nodejs", "npm"
18
+ )
19
+ .pip_install(
20
+ "jupyterlab>=4.0.0",
21
+ "ipython",
22
+ "matplotlib", "numpy", "pandas", "scipy", # common data science libs
23
+ "requests", "python-dotenv"
24
+ )
25
+ # Install code-server with specific version for stability
26
+ .run_commands(
27
+ "curl -fsSL https://code-server.dev/install.sh | sh",
28
+ # Install useful VS Code extensions
29
+ "mkdir -p /root/.local/share/code-server/extensions"
30
+ )
31
+ # Create working directory
32
+ .run_commands("mkdir -p /workspace")
33
+ )
34
+
35
+ @app.function(
36
+ image=image,
37
+ cpu=2,
38
+ memory=4096, # 4GB RAM
39
+ timeout=6 * 3600, # 6 hours (more reasonable than 24h)
40
+ max_containers=1,
41
+ # Mount for persistent storage (optional)
42
+ # volumes={"/workspace": modal.Volume.from_name("dev-workspace", create_if_missing=True)}
43
+ )
44
+ def serve_dev_environment():
45
+ """Launch Jupyter Lab and VS Code development environment"""
46
+
47
+ # Set up signal handler for graceful shutdown
48
+ def signal_handler(sig, frame):
49
+ print("\nShutting down development environment...")
50
+ sys.exit(0)
51
+
52
+ signal.signal(signal.SIGINT, signal_handler)
53
+ signal.signal(signal.SIGTERM, signal_handler)
54
+
55
+ # Change to workspace directory
56
+ os.chdir("/workspace")
57
+
58
+ # Expose ports to the public internet
59
+ with modal.forward(8888) as jupyter_tunnel, \
60
+ modal.forward(8080) as vscode_tunnel:
61
+
62
+ print("๐Ÿš€ Development Environment Ready!")
63
+ print("=" * 50)
64
+ print(f"๐Ÿ“Š Jupyter Lab โ†’ {jupyter_tunnel.url}")
65
+ print(f"๐Ÿ’ป VS Code โ†’ {vscode_tunnel.url}")
66
+ print("=" * 50)
67
+ print("๐Ÿ’ก Tip: Use Ctrl+C to stop the environment")
68
+ print()
69
+
70
+ # Launch JupyterLab with better configuration
71
+ jupyter_process = subprocess.Popen([
72
+ "jupyter", "lab",
73
+ "--no-browser",
74
+ "--allow-root",
75
+ "--ip=0.0.0.0",
76
+ "--port=8888",
77
+ "--ServerApp.allow_origin='*'",
78
+ "--ServerApp.allow_remote_access=True",
79
+ "--ServerApp.disable_check_xsrf=True",
80
+ "--ServerApp.token=''", # No token for easier access
81
+ "--ServerApp.password=''",
82
+ "--ServerApp.root_dir=/workspace"
83
+ ], env={**os.environ}, cwd="/workspace")
84
+
85
+ # Launch code-server with better configuration
86
+ vscode_process = subprocess.Popen([
87
+ "code-server",
88
+ "--bind-addr", "0.0.0.0:8080",
89
+ "--auth", "none",
90
+ "--disable-telemetry",
91
+ "--disable-update-check",
92
+ "/workspace" # Open workspace directory
93
+ ], env={**os.environ}, cwd="/workspace")
94
+
95
+ try:
96
+ # Health check loop with process monitoring
97
+ while True:
98
+ # Check if processes are still running
99
+ if jupyter_process.poll() is not None:
100
+ print("โš ๏ธ Jupyter Lab process died, restarting...")
101
+ jupyter_process = subprocess.Popen([
102
+ "jupyter", "lab", "--no-browser", "--allow-root",
103
+ "--ip=0.0.0.0", "--port=8888",
104
+ "--ServerApp.allow_origin='*'",
105
+ "--ServerApp.allow_remote_access=True",
106
+ "--ServerApp.token=''", "--ServerApp.password=''"
107
+ ], env={**os.environ}, cwd="/workspace")
108
+
109
+ if vscode_process.poll() is not None:
110
+ print("โš ๏ธ VS Code process died, restarting...")
111
+ vscode_process = subprocess.Popen([
112
+ "code-server", "--bind-addr", "0.0.0.0:8080",
113
+ "--auth", "none", "--disable-telemetry", "/workspace"
114
+ ], env={**os.environ}, cwd="/workspace")
115
+
116
+ time.sleep(30) # Check every 30 seconds instead of 10 minutes
117
+
118
+ except KeyboardInterrupt:
119
+ print("\n๐Ÿ›‘ Stopping development environment...")
120
+ jupyter_process.terminate()
121
+ vscode_process.terminate()
122
+
123
+ # Wait for graceful shutdown
124
+ try:
125
+ jupyter_process.wait(timeout=5)
126
+ vscode_process.wait(timeout=5)
127
+ except subprocess.TimeoutExpired:
128
+ jupyter_process.kill()
129
+ vscode_process.kill()
130
+
131
+ @app.local_entrypoint()
132
+ def main():
133
+ """Start the development environment"""
134
+ print("๐Ÿ”„ Starting development environment...")
135
+ serve_dev_environment.remote()
136
+
137
+ if __name__ == "__main__":
138
+ main()