1+ #!/usr/bin/env python3
2+ """
3+ Debug script to identify CI environment issues.
4+ This script runs comprehensive checks to identify differences between local and CI environments.
5+ """
6+
7+ import sys
8+ import os
9+ import subprocess
10+ import json
11+ import traceback
12+ from pathlib import Path
13+
14+
15+ def run_command (cmd , check = False ):
16+ """Run a command and return the result."""
17+ try :
18+ result = subprocess .run (cmd , shell = True , check = check , capture_output = True , text = True )
19+ return result
20+ except subprocess .CalledProcessError as e :
21+ return e
22+
23+
24+ def check_python_environment ():
25+ """Check Python environment details."""
26+ print ("🐍 Python Environment Check" )
27+ print ("=" * 40 )
28+
29+ print (f"Python version: { sys .version } " )
30+ print (f"Python executable: { sys .executable } " )
31+ print (f"Python path: { sys .path } " )
32+
33+ # Check installed packages
34+ result = run_command ("python3 -m pip list" )
35+ if result .returncode == 0 :
36+ print ("📦 Installed packages:" )
37+ for line in result .stdout .split ('\n ' )[:10 ]: # Show first 10 packages
38+ if line .strip ():
39+ print (f" { line } " )
40+ print (" ..." )
41+ else :
42+ print (f"❌ Failed to list packages: { result .stderr } " )
43+
44+ print ()
45+
46+
47+ def check_file_system ():
48+ """Check file system and permissions."""
49+ print ("📁 File System Check" )
50+ print ("=" * 40 )
51+
52+ # Check current directory
53+ print (f"Current directory: { os .getcwd ()} " )
54+
55+ # Check important files exist
56+ important_files = [
57+ "src/patterns/singleton.py" ,
58+ "src/patterns/repository.py" ,
59+ "requirements.txt" ,
60+ "requirements-dev.txt" ,
61+ "notebooks/01_singleton_pattern.ipynb"
62+ ]
63+
64+ for file_path in important_files :
65+ if os .path .exists (file_path ):
66+ print (f"✅ { file_path } exists" )
67+ else :
68+ print (f"❌ { file_path } missing" )
69+
70+ # Check Python path setup
71+ src_path = os .path .join (os .getcwd (), 'src' )
72+ if src_path in sys .path :
73+ print (f"✅ { src_path } is in Python path" )
74+ else :
75+ print (f"❌ { src_path } not in Python path" )
76+ sys .path .insert (0 , src_path )
77+ print (f"🔧 Added { src_path } to Python path" )
78+
79+ print ()
80+
81+
82+ def check_imports ():
83+ """Check if critical imports work."""
84+ print ("📥 Import Check" )
85+ print ("=" * 40 )
86+
87+ import_tests = [
88+ ("sqlite3" , "import sqlite3" ),
89+ ("json" , "import json" ),
90+ ("tempfile" , "import tempfile" ),
91+ ("patterns.singleton" , "from patterns.singleton import singleton" ),
92+ ("patterns.repository" , "from patterns.repository import User, SqliteUserRepository, JsonFileUserRepository" ),
93+ ("patterns.factory" , "from patterns.factory import ShapeFactory" ),
94+ ("patterns.observer" , "from patterns.observer import WeatherStation" ),
95+ ]
96+
97+ for name , import_stmt in import_tests :
98+ try :
99+ exec (import_stmt )
100+ print (f"✅ { name } imports successfully" )
101+ except Exception as e :
102+ print (f"❌ { name } import failed: { e } " )
103+ traceback .print_exc ()
104+
105+ print ()
106+
107+
108+ def check_repository_functionality ():
109+ """Check repository pattern functionality in detail."""
110+ print ("🗄️ Repository Functionality Check" )
111+ print ("=" * 40 )
112+
113+ try :
114+ # Import required modules
115+ from patterns .repository import User , SqliteUserRepository , JsonFileUserRepository
116+
117+ # Test SQLite repository
118+ print ("Testing SQLite repository..." )
119+ sqlite_repo = SqliteUserRepository (':memory:' )
120+ test_user = User (id = None , name = 'Test User' , email = 'test@example.com' )
121+ saved_user = sqlite_repo .save (test_user )
122+
123+ print (f"✅ SQLite: User saved with ID { saved_user .id } " )
124+
125+ found_user = sqlite_repo .find_by_id (saved_user .id )
126+ if found_user :
127+ print (f"✅ SQLite: User found - { found_user .name } " )
128+ else :
129+ print ("❌ SQLite: User not found" )
130+
131+ # Test JSON file repository with new file
132+ print ("Testing JSON file repository (new file)..." )
133+ import tempfile
134+ with tempfile .NamedTemporaryFile (mode = 'w' , delete = False , suffix = '.json' ) as f :
135+ temp_path = f .name
136+
137+ try :
138+ json_repo = JsonFileUserRepository (temp_path )
139+ print (f"✅ JSON: Repository created, users count: { len (json_repo ._users )} " )
140+
141+ saved_user2 = json_repo .save (test_user )
142+ print (f"✅ JSON: User saved with ID { saved_user2 .id } " )
143+
144+ finally :
145+ os .unlink (temp_path )
146+
147+ # Test JSON file repository with existing file
148+ print ("Testing JSON file repository (existing file)..." )
149+ with tempfile .NamedTemporaryFile (mode = 'w' , delete = False , suffix = '.json' ) as f :
150+ test_data = {
151+ 'users' : [
152+ {
153+ 'id' : 1 ,
154+ 'name' : 'Alice' ,
155+ 'email' : 'alice@example.com' ,
156+ 'created_at' : '2023-01-01T12:00:00' ,
157+ }
158+ ],
159+ 'next_id' : 2 ,
160+ }
161+ json .dump (test_data , f )
162+ temp_path2 = f .name
163+
164+ try :
165+ json_repo2 = JsonFileUserRepository (temp_path2 )
166+ print (f"✅ JSON existing: Repository loaded, users count: { len (json_repo2 ._users )} " )
167+ print (f"✅ JSON existing: Next ID: { json_repo2 ._next_id } " )
168+ print (f"✅ JSON existing: First user: { json_repo2 ._users [0 ].name } " )
169+
170+ finally :
171+ os .unlink (temp_path2 )
172+
173+ # Test corrupted file handling
174+ print ("Testing corrupted file handling..." )
175+ with tempfile .NamedTemporaryFile (mode = 'w' , delete = False , suffix = '.json' ) as f :
176+ f .write ('invalid json content' )
177+ temp_path3 = f .name
178+
179+ try :
180+ json_repo3 = JsonFileUserRepository (temp_path3 )
181+ print (f"✅ Corrupted: Repository created, users count: { len (json_repo3 ._users )} " )
182+
183+ finally :
184+ os .unlink (temp_path3 )
185+
186+ print ("✅ All repository functionality tests passed" )
187+
188+ except Exception as e :
189+ print (f"❌ Repository functionality test failed: { e } " )
190+ traceback .print_exc ()
191+
192+ print ()
193+
194+
195+ def check_notebook_validation ():
196+ """Check notebook validation."""
197+ print ("📓 Notebook Validation Check" )
198+ print ("=" * 40 )
199+
200+ notebooks_dir = Path ("notebooks" )
201+ if not notebooks_dir .exists ():
202+ print ("❌ Notebooks directory does not exist" )
203+ return
204+
205+ notebooks = list (notebooks_dir .glob ("*.ipynb" ))
206+ print (f"Found { len (notebooks )} notebooks" )
207+
208+ for notebook in notebooks :
209+ try :
210+ with open (notebook , 'r' ) as f :
211+ data = json .load (f )
212+
213+ # Check structure
214+ if 'cells' not in data or 'metadata' not in data :
215+ print (f"❌ { notebook .name } : Missing basic structure" )
216+ continue
217+
218+ # Check for outputs
219+ has_outputs = any (
220+ cell .get ('cell_type' ) == 'code' and cell .get ('outputs' )
221+ for cell in data .get ('cells' , [])
222+ )
223+
224+ if has_outputs :
225+ print (f"⚠️ { notebook .name } : Has outputs" )
226+ else :
227+ print (f"✅ { notebook .name } : Clean (no outputs)" )
228+
229+ except Exception as e :
230+ print (f"❌ { notebook .name } : Error - { e } " )
231+
232+ print ()
233+
234+
235+ def check_nbval_compatibility ():
236+ """Check nbval compatibility."""
237+ print ("🔬 nbval Compatibility Check" )
238+ print ("=" * 40 )
239+
240+ try :
241+ import nbval
242+ print (f"✅ nbval version: { nbval .__version__ } " )
243+
244+ # Try to run nbval on a notebook
245+ notebooks = list (Path ("notebooks" ).glob ("*.ipynb" ))
246+ if notebooks :
247+ test_notebook = notebooks [0 ]
248+ print (f"Testing nbval on { test_notebook .name } ..." )
249+
250+ result = run_command (f"python3 -m pytest --nbval { test_notebook } " )
251+ if result .returncode == 0 :
252+ print ("✅ nbval test passed" )
253+ else :
254+ print (f"❌ nbval test failed: { result .stderr } " )
255+
256+ except ImportError :
257+ print ("❌ nbval not installed" )
258+ except Exception as e :
259+ print (f"❌ nbval test error: { e } " )
260+
261+ print ()
262+
263+
264+ def main ():
265+ """Main debugging function."""
266+ print ("🔍 CI Environment Debugging Script" )
267+ print ("=" * 50 )
268+ print ()
269+
270+ check_python_environment ()
271+ check_file_system ()
272+ check_imports ()
273+ check_repository_functionality ()
274+ check_notebook_validation ()
275+ check_nbval_compatibility ()
276+
277+ print ("=" * 50 )
278+ print ("🏁 Debug script completed" )
279+
280+
281+ if __name__ == "__main__" :
282+ main ()
0 commit comments