66
77import { spawn } from 'child_process' ;
88import path from 'path' ;
9+ import fs from 'fs-extra' ;
910import { VERSION as DOCKER_VERSION } from '@walkeros/docker' ;
10- import { isUrl } from '../config/utils.js' ;
11+ import { VERSION as CLI_VERSION } from '../version.js' ;
12+ import { isUrl , downloadFromUrl } from '../config/utils.js' ;
1113import type { GlobalOptions } from '../types/global.js' ;
1214
13- // Version injected at build time via tsup define
14- declare const __VERSION__ : string ;
15- const CLI_VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : '0.0.0' ;
16-
1715/**
1816 * Docker image for CLI/build tools (bundle, simulate)
1917 * Uses explicit version by default, can be overridden with env var
@@ -128,10 +126,13 @@ export function buildDockerCommand(
128126/**
129127 * Execute command in Docker container
130128 *
129+ * For remote URLs, downloads the config to a temp file first since Docker
130+ * containers can't access URLs directly via volume mounting.
131+ *
131132 * @param command - CLI command
132133 * @param args - Command arguments
133134 * @param options - Global options
134- * @param configFile - Optional config file path to mount in Docker
135+ * @param configFile - Optional config file path or URL to mount in Docker
135136 * @returns Promise that resolves when command completes
136137 */
137138export async function executeInDocker (
@@ -140,38 +141,59 @@ export async function executeInDocker(
140141 options : GlobalOptions = { } ,
141142 configFile ?: string ,
142143) : Promise < void > {
143- // Force --local execution inside container to prevent nested Docker attempts
144- // Architecture: Host CLI decides environment (Docker vs local),
145- // Container CLI always executes locally (no Docker-in-Docker)
146- const containerArgs = [ ...args , '--local' ] ;
147-
148- const dockerCmd = buildDockerCommand (
149- command ,
150- containerArgs ,
151- options ,
152- configFile ,
153- ) ;
154-
155- return new Promise ( ( resolve , reject ) => {
156- const proc = spawn ( dockerCmd [ 0 ] , dockerCmd . slice ( 1 ) , {
157- stdio : options . silent ? 'ignore' : 'inherit' ,
158- shell : false ,
159- } ) ;
144+ let tempFile : string | undefined ;
145+ let effectiveConfigFile = configFile ;
146+
147+ try {
148+ // Pre-download URL configs to temp file for Docker mounting
149+ // Docker can only mount local files, not remote URLs
150+ if ( configFile && isUrl ( configFile ) ) {
151+ tempFile = await downloadFromUrl ( configFile ) ;
152+ effectiveConfigFile = tempFile ;
153+ }
160154
161- proc . on ( 'error' , ( error ) => {
162- reject ( new Error ( `Docker execution failed: ${ error . message } ` ) ) ;
155+ // Force --local execution inside container to prevent nested Docker attempts
156+ // Architecture: Host CLI decides environment (Docker vs local),
157+ // Container CLI always executes locally (no Docker-in-Docker)
158+ const containerArgs = [ ...args , '--local' ] ;
159+
160+ const dockerCmd = buildDockerCommand (
161+ command ,
162+ containerArgs ,
163+ options ,
164+ effectiveConfigFile ,
165+ ) ;
166+
167+ return await new Promise ( ( resolve , reject ) => {
168+ const proc = spawn ( dockerCmd [ 0 ] , dockerCmd . slice ( 1 ) , {
169+ stdio : options . silent ? 'ignore' : 'inherit' ,
170+ shell : false ,
171+ } ) ;
172+
173+ proc . on ( 'error' , ( error ) => {
174+ reject ( new Error ( `Docker execution failed: ${ error . message } ` ) ) ;
175+ } ) ;
176+
177+ proc . on ( 'exit' , ( code ) => {
178+ if ( code === 0 ) {
179+ resolve ( ) ;
180+ } else {
181+ // Docker already logged the error via stdio inherit
182+ // Just exit with same code - no duplicate message
183+ process . exit ( code || 1 ) ;
184+ }
185+ } ) ;
163186 } ) ;
164-
165- proc . on ( 'exit' , ( code ) => {
166- if ( code === 0 ) {
167- resolve ( ) ;
168- } else {
169- // Docker already logged the error via stdio inherit
170- // Just exit with same code - no duplicate message
171- process . exit ( code || 1 ) ;
187+ } finally {
188+ // Clean up temp file if we created one
189+ if ( tempFile ) {
190+ try {
191+ await fs . remove ( tempFile ) ;
192+ } catch {
193+ // Ignore cleanup errors
172194 }
173- } ) ;
174- } ) ;
195+ }
196+ }
175197}
176198
177199/**
0 commit comments