11use crate :: config:: { BuildMode , InstanceInfo } ;
22use crate :: project:: ProjectContext ;
3- use crate :: utils:: { print_confirm , print_status, print_warning } ;
3+ use crate :: utils:: print_status;
44use eyre:: { Result , eyre} ;
55use std:: process:: { Command , Output } ;
6- use std:: thread;
7- use std:: time:: Duration ;
86
97pub struct DockerManager < ' a > {
108 project : & ' a ProjectContext ,
@@ -46,8 +44,12 @@ impl<'a> DockerManager<'a> {
4644 }
4745
4846 /// Get environment variables for an instance
47+ /// Loads from .env file and shell environment
4948 pub ( crate ) fn environment_variables ( & self , instance_name : & str ) -> Vec < String > {
50- vec ! [
49+ // Load .env file (silently ignore if it doesn't exist)
50+ let _ = dotenvy:: dotenv ( ) ;
51+
52+ let mut env_vars = vec ! [
5153 {
5254 let port = self
5355 . project
@@ -64,7 +66,17 @@ impl<'a> DockerManager<'a> {
6466 let project_name = & self . project. config. project. name;
6567 format!( "HELIX_PROJECT={project_name}" )
6668 } ,
67- ]
69+ ] ;
70+
71+ // Add API keys from environment (which includes .env after dotenv() call)
72+ if let Ok ( openai_key) = std:: env:: var ( "OPENAI_API_KEY" ) {
73+ env_vars. push ( format ! ( "OPENAI_API_KEY={openai_key}" ) ) ;
74+ }
75+ if let Ok ( gemini_key) = std:: env:: var ( "GEMINI_API_KEY" ) {
76+ env_vars. push ( format ! ( "GEMINI_API_KEY={gemini_key}" ) ) ;
77+ }
78+
79+ env_vars
6880 }
6981
7082 /// Get the container name for an instance
@@ -107,112 +119,6 @@ impl<'a> DockerManager<'a> {
107119 Ok ( output)
108120 }
109121
110- /// Detect the current operating system platform
111- fn detect_platform ( ) -> & ' static str {
112- #[ cfg( target_os = "macos" ) ]
113- return "macos" ;
114-
115- #[ cfg( target_os = "linux" ) ]
116- return "linux" ;
117-
118- #[ cfg( target_os = "windows" ) ]
119- return "windows" ;
120-
121- #[ cfg( not( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ) ]
122- return "unknown" ;
123- }
124-
125- /// Start the Docker daemon based on the platform
126- fn start_docker_daemon ( ) -> Result < ( ) > {
127- let platform = Self :: detect_platform ( ) ;
128-
129- match platform {
130- "macos" => {
131- print_status ( "DOCKER" , "Starting Docker Desktop for macOS..." ) ;
132- Command :: new ( "open" )
133- . args ( [ "-a" , "Docker" ] )
134- . output ( )
135- . map_err ( |e| eyre ! ( "Failed to start Docker Desktop: {}" , e) ) ?;
136- }
137- "linux" => {
138- print_status ( "DOCKER" , "Attempting to start Docker daemon on Linux..." ) ;
139- // Try systemctl first, then service command as fallback
140- let systemctl_result = Command :: new ( "systemctl" ) . args ( [ "start" , "docker" ] ) . output ( ) ;
141-
142- match systemctl_result {
143- Ok ( output) if output. status . success ( ) => {
144- // systemctl succeeded
145- }
146- _ => {
147- // Try service command as fallback
148- let service_result = Command :: new ( "service" )
149- . args ( [ "docker" , "start" ] )
150- . output ( )
151- . map_err ( |e| eyre ! ( "Failed to start Docker daemon: {}" , e) ) ?;
152-
153- if !service_result. status . success ( ) {
154- let stderr = String :: from_utf8_lossy ( & service_result. stderr ) ;
155- return Err ( eyre ! ( "Failed to start Docker daemon: {}" , stderr) ) ;
156- }
157- }
158- }
159- }
160- "windows" => {
161- print_status ( "DOCKER" , "Starting Docker Desktop for Windows..." ) ;
162- // Try Docker Desktop CLI (4.37+) first
163- let cli_result = Command :: new ( "docker" )
164- . args ( [ "desktop" , "start" ] )
165- . output ( ) ;
166-
167- match cli_result {
168- Ok ( output) if output. status . success ( ) => {
169- // Modern Docker Desktop CLI worked
170- }
171- _ => {
172- // Fallback to direct executable path for older versions
173- // Note: Empty string "" is required as window title parameter
174- Command :: new ( "cmd" )
175- . args ( [ "/c" , "start" , "" , "\" C:\\ Program Files\\ Docker\\ Docker\\ Docker Desktop.exe\" " ] )
176- . output ( )
177- . map_err ( |e| eyre ! ( "Failed to start Docker Desktop: {}" , e) ) ?;
178- }
179- }
180- }
181- _ => {
182- return Err ( eyre ! ( "Unsupported platform for auto-starting Docker" ) ) ;
183- }
184- }
185-
186- Ok ( ( ) )
187- }
188-
189- /// Wait for Docker daemon to be ready
190- fn wait_for_docker ( timeout_secs : u64 ) -> Result < ( ) > {
191- print_status ( "DOCKER" , "Waiting for Docker daemon to start..." ) ;
192-
193- let start = std:: time:: Instant :: now ( ) ;
194- let timeout = Duration :: from_secs ( timeout_secs) ;
195-
196- while start. elapsed ( ) < timeout {
197- // Check if Docker daemon is responding
198- let output = Command :: new ( "docker" ) . args ( [ "info" ] ) . output ( ) ;
199-
200- if let Ok ( output) = output
201- && output. status . success ( )
202- {
203- print_status ( "DOCKER" , "Docker daemon is now running" ) ;
204- return Ok ( ( ) ) ;
205- }
206-
207- // Wait a bit before retrying
208- thread:: sleep ( Duration :: from_millis ( 500 ) ) ;
209- }
210-
211- Err ( eyre ! (
212- "Timeout waiting for Docker daemon to start. Please start Docker manually and try again."
213- ) )
214- }
215-
216122 /// Check if Docker is installed and running
217123 pub fn check_docker_available ( ) -> Result < ( ) > {
218124 let output = Command :: new ( "docker" )
@@ -231,33 +137,7 @@ impl<'a> DockerManager<'a> {
231137 . map_err ( |_| eyre ! ( "Failed to check Docker daemon status" ) ) ?;
232138
233139 if !output. status . success ( ) {
234- // Docker daemon is not running - prompt user
235- let should_start =
236- print_confirm ( "Docker daemon is not running. Would you like to start Docker?" )
237- . unwrap_or ( false ) ;
238-
239- if should_start {
240- // Try to start Docker
241- Self :: start_docker_daemon ( ) ?;
242-
243- // Wait for Docker to be ready
244- Self :: wait_for_docker ( 15 ) ?;
245-
246- // Verify Docker is now running
247- let verify_output = Command :: new ( "docker" )
248- . args ( [ "info" ] )
249- . output ( )
250- . map_err ( |_| eyre ! ( "Failed to verify Docker daemon status" ) ) ?;
251-
252- if !verify_output. status . success ( ) {
253- return Err ( eyre ! (
254- "Docker daemon failed to start. Please start Docker manually and try again."
255- ) ) ;
256- }
257- } else {
258- print_warning ( "Docker daemon must be running to execute this command." ) ;
259- return Err ( eyre ! ( "Docker daemon is not running. Please start Docker." ) ) ;
260- }
140+ return Err ( eyre ! ( "Docker daemon is not running. Please start Docker." ) ) ;
261141 }
262142
263143 Ok ( ( ) )
@@ -354,6 +234,14 @@ CMD ["helix-container"]
354234 let container_name = self . container_name ( instance_name) ;
355235 let network_name = self . network_name ( instance_name) ;
356236
237+ // Get all environment variables dynamically
238+ let env_vars = self . environment_variables ( instance_name) ;
239+ let env_section = env_vars
240+ . iter ( )
241+ . map ( |var| format ! ( " - {var}" ) )
242+ . collect :: < Vec < _ > > ( )
243+ . join ( "\n " ) ;
244+
357245 let compose = format ! (
358246 r#"# Generated docker-compose.yml for Helix instance: {instance_name}
359247services:
@@ -369,10 +257,7 @@ services:
369257 volumes:
370258 - ../.volumes/{instance_name}:/data
371259 environment:
372- - HELIX_PORT={port}
373- - HELIX_DATA_DIR={data_dir}
374- - HELIX_INSTANCE={instance_name}
375- - HELIX_PROJECT={project_name}
260+ {env_section}
376261 restart: unless-stopped
377262 networks:
378263 - {network_name}
@@ -384,8 +269,6 @@ networks:
384269 platform = instance_config
385270 . docker_build_target( )
386271 . map_or( "" . to_string( ) , |p| format!( "platforms:\n - {p}" ) ) ,
387- project_name = self . project. config. project. name,
388- data_dir = HELIX_DATA_DIR ,
389272 ) ;
390273
391274 Ok ( compose)
0 commit comments