@@ -47,39 +47,47 @@ struct ComposeOptions: ParsableArguments {
4747 }
4848
4949 func getComposeFileURLs( ) -> [ URL ] {
50- // If no files specified, use default
51- let files = file. isEmpty ? [ " docker-compose.yaml " , " docker-compose.yml " , " compose.yaml " , " compose.yml " ] : file
52-
53- var urls : [ URL ] = [ ]
5450 let currentPath = FileManager . default. currentDirectoryPath
5551
56- for fileName in files {
57- let url : URL
58- if fileName. hasPrefix ( " / " ) {
59- url = URL ( fileURLWithPath: fileName)
60- } else {
61- url = URL ( fileURLWithPath: currentPath) . appendingPathComponent ( fileName)
62- }
63-
64- // For default files, only add if they exist
65- if file. isEmpty {
66- if FileManager . default. fileExists ( atPath: url. path) {
67- urls. append ( url)
68- break // Use first found default file
69- }
70- } else {
71- // For explicitly specified files, add them all (parser will check existence)
72- urls. append ( url)
52+ // If files were explicitly specified, return all of them (relative to cwd)
53+ if !file. isEmpty {
54+ return file. map { name in
55+ if name. hasPrefix ( " / " ) { return URL ( fileURLWithPath: name) }
56+ return URL ( fileURLWithPath: currentPath) . appendingPathComponent ( name)
7357 }
7458 }
7559
76- // If no files found from defaults, return the first default for error message
77- if urls. isEmpty && file. isEmpty {
78- let defaultFile = URL ( fileURLWithPath: currentPath) . appendingPathComponent ( " docker-compose.yaml " )
79- urls. append ( defaultFile)
60+ // Default behavior: detect base compose file and include matching override
61+ let candidates = [
62+ " docker-compose.yml " ,
63+ " docker-compose.yaml " ,
64+ " compose.yml " ,
65+ " compose.yaml " ,
66+ ]
67+
68+ for base in candidates {
69+ let baseURL = URL ( fileURLWithPath: currentPath) . appendingPathComponent ( base)
70+ if FileManager . default. fileExists ( atPath: baseURL. path) {
71+ var urls = [ baseURL]
72+ // Include override for the chosen base
73+ let overrideCandidates : [ String ]
74+ if base. hasPrefix ( " docker-compose " ) {
75+ overrideCandidates = [ " docker-compose.override.yml " , " docker-compose.override.yaml " ]
76+ } else {
77+ overrideCandidates = [ " compose.override.yml " , " compose.override.yaml " ]
78+ }
79+ for o in overrideCandidates {
80+ let oURL = URL ( fileURLWithPath: currentPath) . appendingPathComponent ( o)
81+ if FileManager . default. fileExists ( atPath: oURL. path) {
82+ urls. append ( oURL)
83+ }
84+ }
85+ return urls
86+ }
8087 }
8188
82- return urls
89+ // Nothing found: return a sensible default path for better error message downstream
90+ return [ URL ( fileURLWithPath: currentPath) . appendingPathComponent ( " docker-compose.yml " ) ]
8391 }
8492
8593 func setEnvironmentVariables( ) {
@@ -90,4 +98,28 @@ struct ComposeOptions: ParsableArguments {
9098 }
9199 }
92100 }
101+
102+ /// Load .env from current working directory and export vars into process env
103+ /// Compose uses .env for interpolation; we approximate by exporting to env before parsing
104+ func loadDotEnvIfPresent( ) {
105+ let cwd = FileManager . default. currentDirectoryPath
106+ let dotEnvURL = URL ( fileURLWithPath: cwd) . appendingPathComponent ( " .env " )
107+ guard FileManager . default. fileExists ( atPath: dotEnvURL. path) else { return }
108+ if let contents = try ? String ( contentsOf: dotEnvURL, encoding: . utf8) {
109+ for line in contents. split ( whereSeparator: { $0. isNewline } ) {
110+ var s = String ( line) . trimmingCharacters ( in: . whitespaces)
111+ if s. isEmpty || s. hasPrefix ( " # " ) { continue }
112+ if s. hasPrefix ( " export " ) { s. removeFirst ( " export " . count) }
113+ let parts = s. split ( separator: " = " , maxSplits: 1 )
114+ if parts. count == 2 {
115+ let key = String ( parts [ 0 ] ) . trimmingCharacters ( in: . whitespaces)
116+ var val = String ( parts [ 1 ] ) . trimmingCharacters ( in: . whitespaces)
117+ if ( val. hasPrefix ( " \" " ) && val. hasSuffix ( " \" " ) ) || ( val. hasPrefix ( " ' " ) && val. hasSuffix ( " ' " ) ) {
118+ val = String ( val. dropFirst ( ) . dropLast ( ) )
119+ }
120+ setenv ( key, val, 1 ) // override to ensure deterministic interpolation in this process
121+ }
122+ }
123+ }
124+ }
93125}
0 commit comments