gitarsenal-cli 1.6.6 → 1.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitarsenal-cli",
3
- "version": "1.6.6",
3
+ "version": "1.6.9",
4
4
  "description": "CLI tool for creating Modal sandboxes with GitHub repositories",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -60,19 +60,19 @@ except (ImportError, ValueError) as e:
60
60
  # os.environ["MODAL_TOKEN_SECRET"] = TOKEN_SECRET
61
61
  # print(f"✅ Set MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables")
62
62
 
63
- # # Create token file
64
- # modal_dir = Path.home() / ".modal"
65
- # modal_dir.mkdir(exist_ok=True)
66
- # token_file = modal_dir / "token.json"
67
- # with open(token_file, 'w') as f:
68
- # f.write(f'{{"token_id": "{TOKEN_ID}", "token_secret": "{TOKEN_SECRET}"}}')
69
- # print(f"✅ Created token file at {token_file}")
63
+ # Create token file
64
+ modal_dir = Path.home() / ".modal"
65
+ modal_dir.mkdir(exist_ok=True)
66
+ token_file = modal_dir / "token.json"
67
+ with open(token_file, 'w') as f:
68
+ f.write(f'{{"token_id": "{TOKEN_ID}", "token_secret": "{TOKEN_SECRET}"}}')
69
+ print(f"✅ Created token file at {token_file}")
70
70
 
71
- # # Create .modalconfig file
72
- # modalconfig_file = Path.home() / ".modalconfig"
73
- # with open(modalconfig_file, 'w') as f:
74
- # f.write(f"token_id = {TOKEN_ID}\n")
75
- # f.write(f"token_secret = {TOKEN_SECRET}\n")
76
- # print(f"✅ Created .modalconfig file at {modalconfig_file}")
71
+ # Create .modalconfig file
72
+ modalconfig_file = Path.home() / ".modalconfig"
73
+ with open(modalconfig_file, 'w') as f:
74
+ f.write(f"token_id = {TOKEN_ID}\n")
75
+ f.write(f"token_secret = {TOKEN_SECRET}\n")
76
+ print(f"✅ Created .modalconfig file at {modalconfig_file}")
77
77
 
78
- # print("\n✅ Done fixing Modal token. Please try your command again.")
78
+ print("\n✅ Done fixing Modal token. Please try your command again.")
@@ -234,5 +234,6 @@ try:
234
234
 
235
235
  except Exception as e:
236
236
  # print(f"❌ Error testing Modal authentication: {e}")
237
+ print("")
237
238
 
238
239
  # print("\n✅ Done fixing Modal token. Please try your command again.")
@@ -0,0 +1,54 @@
1
+ import subprocess
2
+ import time
3
+ import secrets
4
+ import string
5
+ import modal
6
+
7
+ def generate_random_password(length=16):
8
+ """Generate a random password for SSH access"""
9
+ alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
10
+ password = ''.join(secrets.choice(alphabet) for i in range(length))
11
+ return password
12
+
13
+ image = (
14
+ modal.Image.from_registry("nvidia/cuda:12.4.0-devel-ubuntu22.04", add_python="3.11")
15
+ .apt_install("openssh-server", "sudo", "curl", "wget", "git", "vim", "htop", "tmux", "nvtop")
16
+ .pip_install("cupy-cuda12x", "torch", "transformers")
17
+ .run_commands(
18
+ "mkdir -p /var/run/sshd",
19
+ "ssh-keygen -A",
20
+ "sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
21
+ "sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
22
+ "echo 'export PATH=/usr/local/cuda/bin:$PATH' >> /root/.bashrc"
23
+ )
24
+ )
25
+
26
+ app = modal.App("cuda-ssh-container", image=image)
27
+
28
+ @app.function(gpu="A10G", timeout=3600)
29
+ def start_ssh():
30
+ # Generate SSH password
31
+ password = generate_random_password()
32
+ subprocess.run(["bash", "-c", f"echo 'root:{password}' | chpasswd"], check=True)
33
+
34
+ # Start SSH server
35
+ subprocess.Popen(["/usr/sbin/sshd", "-D"])
36
+ time.sleep(2)
37
+
38
+ # Test CUDA
39
+ subprocess.run(["nvidia-smi"])
40
+ subprocess.run(["nvcc", "--version"])
41
+
42
+ # Forward SSH port
43
+ with modal.forward(port=22, unencrypted=True) as tunnel:
44
+ hostname, port = tunnel.tcp_socket
45
+ print(f"SSH: ssh -p {port} root@{hostname}")
46
+ print(f"Password: {password}")
47
+
48
+ # Keep alive
49
+ while True:
50
+ time.sleep(60)
51
+
52
+ if __name__ == "__main__":
53
+ with app.run():
54
+ start_ssh.remote()
@@ -0,0 +1,54 @@
1
+ import subprocess
2
+ import time
3
+ import secrets
4
+ import string
5
+ import modal
6
+
7
+ def generate_random_password(length=16):
8
+ """Generate a random password for SSH access"""
9
+ alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
10
+ password = ''.join(secrets.choice(alphabet) for i in range(length))
11
+ return password
12
+
13
+ image = (
14
+ modal.Image.from_registry("nvidia/cuda:12.4.0-devel-ubuntu22.04", add_python="3.11")
15
+ .apt_install("openssh-server", "sudo", "curl", "wget", "git", "vim", "htop", "tmux", "nvtop")
16
+ .pip_install("cupy-cuda12x", "torch", "transformers")
17
+ .run_commands(
18
+ "mkdir -p /var/run/sshd",
19
+ "ssh-keygen -A",
20
+ "sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
21
+ "sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
22
+ "echo 'export PATH=/usr/local/cuda/bin:$PATH' >> /root/.bashrc"
23
+ )
24
+ )
25
+
26
+ app = modal.App("cuda-ssh-container", image=image)
27
+
28
+ @app.function(gpu="A10G", timeout=3600)
29
+ def start_ssh():
30
+ # Generate SSH password
31
+ password = generate_random_password()
32
+ subprocess.run(["bash", "-c", f"echo 'root:{password}' | chpasswd"], check=True)
33
+
34
+ # Start SSH server
35
+ subprocess.Popen(["/usr/sbin/sshd", "-D"])
36
+ time.sleep(2)
37
+
38
+ # Test CUDA
39
+ subprocess.run(["nvidia-smi"])
40
+ subprocess.run(["nvcc", "--version"])
41
+
42
+ # Forward SSH port
43
+ with modal.forward(port=22, unencrypted=True) as tunnel:
44
+ hostname, port = tunnel.tcp_socket
45
+ print(f"SSH: ssh -p {port} root@{hostname}")
46
+ print(f"Password: {password}")
47
+
48
+ # Keep alive
49
+ while True:
50
+ time.sleep(60)
51
+
52
+ if __name__ == "__main__":
53
+ with app.run():
54
+ start_ssh.remote()
@@ -1183,15 +1183,16 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1183
1183
  # Create SSH-enabled image
1184
1184
  try:
1185
1185
  print("📦 Building SSH-enabled image...")
1186
+
1187
+ # Use a more stable CUDA base image and avoid problematic packages
1186
1188
  ssh_image = (
1187
- # modal.Image.from_registry("nvidia/cuda:12.4.0-devel-ubuntu22.04", add_python="3.11")
1188
- modal.Image.debian_slim()
1189
+ modal.Image.from_registry("nvidia/cuda:12.4.0-runtime-ubuntu22.04", add_python="3.11")
1189
1190
  .apt_install(
1190
1191
  "openssh-server", "sudo", "curl", "wget", "vim", "htop", "git",
1191
1192
  "python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
1192
1193
  "gpg", "ca-certificates", "software-properties-common"
1193
1194
  )
1194
- .pip_install("uv", "modal", "requests", "openai") # Fast Python package installer and Modal
1195
+ .pip_install("uv", "modal", "requests", "openai") # Remove problematic CUDA packages
1195
1196
  .run_commands(
1196
1197
  # Create SSH directory
1197
1198
  "mkdir -p /var/run/sshd",
@@ -1223,14 +1224,21 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1223
1224
  volumes_config = {}
1224
1225
  if volume:
1225
1226
  volumes_config[volume_mount_path] = volume
1227
+
1228
+ # Create app with image passed directly (THIS IS THE KEY CHANGE)
1229
+ try:
1230
+ print("🔍 Testing app creation...")
1231
+ app = modal.App(app_name, image=ssh_image) # Pass image here
1232
+ print("✅ Created app successfully")
1233
+ except Exception as e:
1234
+ print(f"❌ Error creating app: {e}")
1235
+ return None
1226
1236
 
1227
- # Define the SSH container function
1237
+ # Define the SSH container function (remove image from decorator)
1228
1238
  @app.function(
1229
- image=ssh_image,
1230
1239
  timeout=timeout_minutes * 60, # Convert to seconds
1231
1240
  gpu=gpu_spec['gpu'],
1232
1241
  cpu=2,
1233
- memory=8192,
1234
1242
  serialized=True,
1235
1243
  volumes=volumes_config if volumes_config else None,
1236
1244
  )
@@ -1183,15 +1183,16 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1183
1183
  # Create SSH-enabled image
1184
1184
  try:
1185
1185
  print("📦 Building SSH-enabled image...")
1186
+
1187
+ # Use a more stable CUDA base image and avoid problematic packages
1186
1188
  ssh_image = (
1187
- # modal.Image.from_registry("nvidia/cuda:12.4.0-devel-ubuntu22.04", add_python="3.11")
1188
- modal.Image.debian_slim()
1189
+ modal.Image.from_registry("nvidia/cuda:12.4.0-runtime-ubuntu22.04", add_python="3.11")
1189
1190
  .apt_install(
1190
1191
  "openssh-server", "sudo", "curl", "wget", "vim", "htop", "git",
1191
1192
  "python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
1192
1193
  "gpg", "ca-certificates", "software-properties-common"
1193
1194
  )
1194
- .pip_install("uv", "modal", "requests", "openai") # Fast Python package installer and Modal
1195
+ .pip_install("uv", "modal", "requests", "openai") # Remove problematic CUDA packages
1195
1196
  .run_commands(
1196
1197
  # Create SSH directory
1197
1198
  "mkdir -p /var/run/sshd",
@@ -1223,14 +1224,21 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1223
1224
  volumes_config = {}
1224
1225
  if volume:
1225
1226
  volumes_config[volume_mount_path] = volume
1227
+
1228
+ # Create app with image passed directly (THIS IS THE KEY CHANGE)
1229
+ try:
1230
+ print("🔍 Testing app creation...")
1231
+ app = modal.App(app_name, image=ssh_image) # Pass image here
1232
+ print("✅ Created app successfully")
1233
+ except Exception as e:
1234
+ print(f"❌ Error creating app: {e}")
1235
+ return None
1226
1236
 
1227
- # Define the SSH container function
1237
+ # Define the SSH container function (remove image from decorator)
1228
1238
  @app.function(
1229
- image=ssh_image,
1230
1239
  timeout=timeout_minutes * 60, # Convert to seconds
1231
1240
  gpu=gpu_spec['gpu'],
1232
1241
  cpu=2,
1233
- memory=8192,
1234
1242
  serialized=True,
1235
1243
  volumes=volumes_config if volumes_config else None,
1236
1244
  )
@@ -1,148 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Test script to verify CUDA setup in GitArsenal containers
4
- """
5
-
6
- import subprocess
7
- import sys
8
- import os
9
-
10
- def test_cuda_basic():
11
- """Test basic CUDA functionality"""
12
- print("🧪 Testing basic CUDA functionality...")
13
-
14
- try:
15
- # Test nvidia-smi
16
- result = subprocess.run(["nvidia-smi"], capture_output=True, text=True, timeout=30)
17
- if result.returncode == 0:
18
- print("✅ nvidia-smi working")
19
- print(f"Output: {result.stdout[:200]}...")
20
- else:
21
- print(f"❌ nvidia-smi failed: {result.stderr}")
22
- return False
23
-
24
- # Test nvcc
25
- result = subprocess.run(["nvcc", "--version"], capture_output=True, text=True, timeout=30)
26
- if result.returncode == 0:
27
- print("✅ nvcc available")
28
- print(f"Version: {result.stdout.split('release')[0].strip()}")
29
- else:
30
- print(f"❌ nvcc failed: {result.stderr}")
31
- return False
32
-
33
- return True
34
-
35
- except subprocess.TimeoutExpired:
36
- print("❌ CUDA test timed out")
37
- return False
38
- except Exception as e:
39
- print(f"❌ CUDA test error: {e}")
40
- return False
41
-
42
- def test_cupy_import():
43
- """Test cupy import"""
44
- print("🧪 Testing cupy import...")
45
-
46
- try:
47
- import cupy as cp
48
- print("✅ cupy imported successfully")
49
-
50
- # Test basic cupy functionality
51
- x = cp.array([1, 2, 3, 4, 5])
52
- y = cp.square(x)
53
- print(f"✅ cupy basic operation: {y}")
54
-
55
- return True
56
-
57
- except ImportError as e:
58
- print(f"❌ cupy import failed: {e}")
59
- return False
60
- except Exception as e:
61
- print(f"❌ cupy test error: {e}")
62
- return False
63
-
64
- def test_gpu_environment():
65
- """Test GPU environment variables"""
66
- print("🧪 Testing GPU environment variables...")
67
-
68
- gpu_vars = {
69
- 'CUDA_VISIBLE_DEVICES': '0',
70
- 'NVIDIA_VISIBLE_DEVICES': 'all',
71
- 'NVIDIA_DRIVER_CAPABILITIES': 'compute,utility'
72
- }
73
-
74
- for var, value in gpu_vars.items():
75
- os.environ[var] = value
76
- print(f"✅ Set {var}={value}")
77
-
78
- # Verify they're set
79
- for var, expected_value in gpu_vars.items():
80
- actual_value = os.environ.get(var)
81
- if actual_value == expected_value:
82
- print(f"✅ {var} correctly set to {actual_value}")
83
- else:
84
- print(f"❌ {var} not set correctly. Expected: {expected_value}, Got: {actual_value}")
85
- return False
86
-
87
- return True
88
-
89
- def test_modal_cuda_image():
90
- """Test Modal CUDA image creation"""
91
- print("🧪 Testing Modal CUDA image creation...")
92
-
93
- try:
94
- import modal
95
-
96
- # Test the same image configuration as the SSH container
97
- image = (
98
- modal.Image.from_registry("nvidia/cuda:12.4.0-devel-ubuntu22.04", add_python="3.11")
99
- .pip_install("cupy-cuda12x", "setuptools", "uv", "modal", "requests", "openai")
100
- )
101
-
102
- print("✅ Modal CUDA image created successfully")
103
- return True
104
-
105
- except ImportError as e:
106
- print(f"❌ Modal import failed: {e}")
107
- return False
108
- except Exception as e:
109
- print(f"❌ Modal CUDA image creation failed: {e}")
110
- return False
111
-
112
- def main():
113
- """Run all CUDA tests"""
114
- print("🧪 GitArsenal CUDA Setup Tests")
115
- print("=" * 50)
116
-
117
- tests = [
118
- ("GPU Environment", test_gpu_environment),
119
- ("Modal CUDA Image", test_modal_cuda_image),
120
- ("Basic CUDA", test_cuda_basic),
121
- ("Cupy Import", test_cupy_import),
122
- ]
123
-
124
- passed = 0
125
- total = len(tests)
126
-
127
- for test_name, test_func in tests:
128
- print(f"\n🔍 Running: {test_name}")
129
- try:
130
- if test_func():
131
- passed += 1
132
- print(f"✅ {test_name} PASSED")
133
- else:
134
- print(f"❌ {test_name} FAILED")
135
- except Exception as e:
136
- print(f"❌ {test_name} ERROR: {e}")
137
-
138
- print(f"\n📊 Test Results: {passed}/{total} tests passed")
139
-
140
- if passed == total:
141
- print("🎉 All CUDA tests passed! The SSH container should work correctly.")
142
- return 0
143
- else:
144
- print("⚠️ Some CUDA tests failed. The SSH container may have issues.")
145
- return 1
146
-
147
- if __name__ == "__main__":
148
- sys.exit(main())