@@ -6,6 +6,8 @@ import { DockgeSocket, fileExists, ValidationError } from "./util-server";
66import path from "path" ;
77import {
88 acceptedComposeFileNames ,
9+ acceptedComposeFileNamePattern ,
10+ ArbitrarilyNestedLooseObject ,
911 COMBINED_TERMINAL_COLS ,
1012 COMBINED_TERMINAL_ROWS ,
1113 CREATED_FILE ,
@@ -282,6 +284,7 @@ export class Stack {
282284 }
283285
284286 let composeList = JSON . parse ( res . stdout . toString ( ) ) ;
287+ let pathSearchTree : ArbitrarilyNestedLooseObject = { } ; // search structure for matching paths
285288
286289 for ( let composeStack of composeList ) {
287290 try {
@@ -296,11 +299,69 @@ export class Stack {
296299 continue ;
297300 }
298301 stackList . set ( composeStack . Name , stack ) ;
302+
303+ // add project path to search tree so we can quickly decide if we have seen it before later
304+ // e.g. path "/opt/stacks" would yield the tree { opt: stacks: {} }
305+ path . join ( stack . _configFilePath , stack . _composeFileName ) . split ( path . sep ) . reduce ( ( searchTree , pathComponent ) => {
306+ if ( pathComponent == "" ) {
307+ return searchTree ;
308+ }
309+ if ( ! searchTree [ pathComponent ] ) {
310+ searchTree [ pathComponent ] = { } ;
311+ }
312+ return searchTree [ pathComponent ] ;
313+ } , pathSearchTree ) ;
299314 } catch ( e ) {
300315 if ( e instanceof Error ) {
301- log . warn ( "getStackList" , `Failed to get stack ${ composeStack . Name } , error: ${ e . message } ` ) ;
316+ log . error ( "getStackList" , `Failed to get stack ${ composeStack . Name } , error: ${ e . message } ` ) ;
317+ }
318+ }
319+ }
320+
321+ // Search stacks directory for compose files not associated with a running compose project (ie. never started through CLI)
322+ try {
323+ // Hopefully the user has access to everything in this directory! If they don't, log the error. It is a small price to pay for fast searching.
324+ let rawFilesList = fs . readdirSync ( server . stacksDir , {
325+ recursive : true ,
326+ withFileTypes : true
327+ } ) ;
328+ let acceptedComposeFiles = rawFilesList . filter ( ( dirEnt : fs . Dirent ) => dirEnt . isFile ( ) && ! ! dirEnt . name . match ( acceptedComposeFileNamePattern ) ) ;
329+ log . debug ( "getStackList" , `Folder scan yielded ${ acceptedComposeFiles . length } files` ) ;
330+ for ( let composeFile of acceptedComposeFiles ) {
331+ // check if we have seen this file before
332+ let fullPath = composeFile . parentPath ;
333+ let previouslySeen = fullPath . split ( path . sep ) . reduce ( ( searchTree : ArbitrarilyNestedLooseObject | boolean , pathComponent ) => {
334+ if ( pathComponent == "" ) {
335+ return searchTree ;
336+ }
337+
338+ // end condition
339+ if ( searchTree == false || ! ( searchTree as ArbitrarilyNestedLooseObject ) [ pathComponent ] ) {
340+ return false ;
341+ }
342+
343+ // path (so far) has been previously seen
344+ return ( searchTree as ArbitrarilyNestedLooseObject ) [ pathComponent ] ;
345+ } , pathSearchTree ) ;
346+ if ( ! previouslySeen ) {
347+ // a file with an accepted compose filename has been found that did not appear in `docker compose ls`. Use its config file path as a temp name
348+ log . info ( "getStackList" , `Found project unknown to docker compose: ${ fullPath } /${ composeFile . name } ` ) ;
349+ let [ configFilePath , configFilename , inferredProjectName ] = [ fullPath , composeFile . name , path . basename ( fullPath ) ] ;
350+ if ( stackList . get ( inferredProjectName ) ) {
351+ log . info ( "getStackList" , `... but it was ignored. A project named ${ inferredProjectName } already exists` ) ;
352+ } else {
353+ let stack = new Stack ( server , inferredProjectName ) ;
354+ stack . _status = UNKNOWN ;
355+ stack . _configFilePath = configFilePath ;
356+ stack . _composeFileName = configFilename ;
357+ stackList . set ( inferredProjectName , stack ) ;
358+ }
302359 }
303360 }
361+ } catch ( e ) {
362+ if ( e instanceof Error ) {
363+ log . error ( "getStackList" , `Got error searching for undiscovered stacks:\n${ e . message } ` ) ;
364+ }
304365 }
305366
306367 this . managedStackList = stackList ;
@@ -492,6 +553,5 @@ export class Stack {
492553 log . error ( "getServiceStatusList" , e ) ;
493554 return statusList ;
494555 }
495-
496556 }
497557}
0 commit comments