1
1
"""Command-line interface for codegate."""
2
2
3
3
import asyncio
4
+ import signal
4
5
import sys
5
6
from pathlib import Path
6
7
from typing import Dict , Optional
18
19
from codegate .server import init_app
19
20
from codegate .storage .utils import restore_storage_backup
20
21
21
- logger = structlog .get_logger ("codegate" )
22
-
23
22
24
23
class UvicornServer :
25
24
def __init__ (self , config : UvicornConfig , server : Server ):
@@ -32,10 +31,16 @@ def __init__(self, config: UvicornConfig, server: Server):
32
31
self ._startup_complete = asyncio .Event ()
33
32
self ._shutdown_event = asyncio .Event ()
34
33
self ._should_exit = False
34
+ self .logger = structlog .get_logger ("codegate" )
35
35
36
36
async def serve (self ) -> None :
37
37
"""Start the uvicorn server and handle shutdown gracefully."""
38
- logger .debug (f"Starting server on { self .host } :{ self .port } " )
38
+ self .logger .debug (f"Starting server on { self .host } :{ self .port } " )
39
+
40
+ # Set up signal handlers
41
+ loop = asyncio .get_running_loop ()
42
+ loop .add_signal_handler (signal .SIGTERM , lambda : asyncio .create_task (self .cleanup ()))
43
+ loop .add_signal_handler (signal .SIGINT , lambda : asyncio .create_task (self .cleanup ()))
39
44
40
45
self .server = Server (config = self .config )
41
46
self .server .force_exit = True
@@ -44,40 +49,41 @@ async def serve(self) -> None:
44
49
self ._startup_complete .set ()
45
50
await self .server .serve ()
46
51
except asyncio .CancelledError :
47
- logger .info ("Server received cancellation" )
52
+ self . logger .info ("Server received cancellation" )
48
53
except Exception as e :
49
- logger .exception ("Unexpected error occurred during server execution" , exc_info = e )
54
+ self . logger .exception ("Unexpected error occurred during server execution" , exc_info = e )
50
55
finally :
51
56
await self .cleanup ()
52
57
53
58
async def wait_startup_complete (self ) -> None :
54
59
"""Wait for the server to complete startup."""
55
- logger .debug ("Waiting for server startup to complete" )
60
+ self . logger .debug ("Waiting for server startup to complete" )
56
61
await self ._startup_complete .wait ()
57
62
58
63
async def cleanup (self ) -> None :
59
64
"""Cleanup server resources and ensure graceful shutdown."""
60
- logger .debug ("Cleaning up server resources" )
65
+ self . logger .debug ("Cleaning up server resources" )
61
66
if not self ._should_exit :
62
67
self ._should_exit = True
63
- logger .debug ("Initiating server shutdown" )
68
+ self . logger .debug ("Initiating server shutdown" )
64
69
self ._shutdown_event .set ()
65
70
66
71
if hasattr (self .server , "shutdown" ):
67
- logger .debug ("Shutting down server" )
72
+ self . logger .debug ("Shutting down server" )
68
73
await self .server .shutdown ()
69
74
70
75
# Ensure all connections are closed
71
76
tasks = [t for t in asyncio .all_tasks () if t is not asyncio .current_task ()]
72
77
[task .cancel () for task in tasks ]
73
78
74
79
await asyncio .gather (* tasks , return_exceptions = True )
75
- logger .debug ("Server shutdown complete" )
80
+ self . logger .debug ("Server shutdown complete" )
76
81
77
82
78
83
def validate_port (ctx : click .Context , param : click .Parameter , value : int ) -> int :
79
- logger .debug (f"Validating port number: { value } " )
80
84
"""Validate the port number is in valid range."""
85
+ logger = structlog .get_logger ("codegate" )
86
+ logger .debug (f"Validating port number: { value } " )
81
87
if value is not None and not (1 <= value <= 65535 ):
82
88
raise click .BadParameter ("Port must be between 1 and 65535" )
83
89
return value
@@ -286,10 +292,14 @@ def serve(
286
292
db_path = db_path ,
287
293
)
288
294
295
+ # Set up logging first
296
+ setup_logging (cfg .log_level , cfg .log_format )
297
+ logger = structlog .get_logger ("codegate" )
298
+
289
299
init_db_sync (cfg .db_path )
290
300
291
301
# Check certificates and create CA if necessary
292
- logger .info ("Checking certificates and creating CA our created " )
302
+ logger .info ("Checking certificates and creating CA if needed " )
293
303
ca = CertificateAuthority .get_instance ()
294
304
ca .ensure_certificates_exist ()
295
305
@@ -311,16 +321,15 @@ def serve(
311
321
click .echo (f"Configuration error: { e } " , err = True )
312
322
sys .exit (1 )
313
323
except Exception as e :
314
- if logger :
315
- logger .exception ("Unexpected error occurred" )
324
+ logger = structlog . get_logger ( "codegate" )
325
+ logger .exception ("Unexpected error occurred" )
316
326
click .echo (f"Error: { e } " , err = True )
317
327
sys .exit (1 )
318
328
319
329
320
330
async def run_servers (cfg : Config , app ) -> None :
321
331
"""Run the codegate server."""
322
332
try :
323
- setup_logging (cfg .log_level , cfg .log_format )
324
333
logger = structlog .get_logger ("codegate" )
325
334
logger .info (
326
335
"Starting server" ,
0 commit comments