@@ -15,7 +15,7 @@ import { CommonDevContainerConfig, ContainerProperties, getContainerProperties,
1515import { Workspace } from '../spec-utils/workspaces' ;
1616import { URI } from 'vscode-uri' ;
1717import { ShellServer } from '../spec-common/shellServer' ;
18- import { inspectContainer , inspectImage , getEvents , ContainerDetails , DockerCLIParameters , dockerExecFunction , dockerPtyCLI , dockerPtyExecFunction , toDockerImageName , DockerComposeCLI , ImageDetails , dockerCLI , removeContainer } from '../spec-shutdown/dockerUtils' ;
18+ import { inspectContainer , inspectContainers , inspectImage , getEvents , listContainers , ContainerDetails , DockerCLIParameters , dockerExecFunction , dockerPtyCLI , dockerPtyExecFunction , toDockerImageName , DockerComposeCLI , ImageDetails , dockerCLI , removeContainer } from '../spec-shutdown/dockerUtils' ;
1919import { getRemoteWorkspaceFolder } from './dockerCompose' ;
2020import { findGitRootFolder } from '../spec-common/git' ;
2121import { parentURI , uriToFsPath } from '../spec-configuration/configurationCommonUtils' ;
@@ -614,21 +614,99 @@ export function getEmptyContextFolder(common: ResolverParameters) {
614614 return common . cliHost . path . join ( common . persistedFolder , 'empty-folder' ) ;
615615}
616616
617+ export function normalizeDevContainerLabelPath ( platform : NodeJS . Platform , value : string ) : string {
618+ if ( platform !== 'win32' ) {
619+ return value ;
620+ }
621+
622+ // Normalize separators and dot segments, then explicitly lowercase the drive
623+ // letter because devcontainer.local_folder / devcontainer.config_file labels
624+ // should compare case-insensitively on Windows.
625+ const normalized = path . win32 . normalize ( value ) ;
626+ if ( normalized . length >= 2 && normalized [ 1 ] === ':' ) {
627+ return normalized [ 0 ] . toLowerCase ( ) + normalized . slice ( 1 ) ;
628+ }
629+
630+ return normalized ;
631+ }
632+
633+ async function findDevContainerByNormalizedLabels ( params : DockerResolverParameters | DockerCLIParameters , normalizedWorkspaceFolder : string , normalizedConfigFile : string ) {
634+ if ( process . platform !== 'win32' ) {
635+ return undefined ;
636+ }
637+
638+ const ids = await listContainers ( params , true , [ hostFolderLabel ] ) ;
639+ if ( ! ids . length ) {
640+ return undefined ;
641+ }
642+
643+ const details = await inspectContainers ( params , ids ) ;
644+ return details
645+ . filter ( container => container . State . Status !== 'removing' )
646+ . find ( container => {
647+ const labels = container . Config . Labels || { } ;
648+ const containerWorkspaceFolder = labels [ hostFolderLabel ] ;
649+ if ( ! containerWorkspaceFolder || normalizeDevContainerLabelPath ( 'win32' , containerWorkspaceFolder ) !== normalizedWorkspaceFolder ) {
650+ return false ;
651+ }
652+
653+ const containerConfigFile = labels [ configFileLabel ] ;
654+ return ! ! containerConfigFile
655+ && normalizeDevContainerLabelPath ( 'win32' , containerConfigFile ) === normalizedConfigFile ;
656+ } ) ;
657+ }
658+
659+ async function findLegacyDevContainerByNormalizedWorkspaceFolder ( params : DockerResolverParameters | DockerCLIParameters , normalizedWorkspaceFolder : string ) {
660+ if ( process . platform !== 'win32' ) {
661+ return undefined ;
662+ }
663+
664+ const ids = await listContainers ( params , true , [ hostFolderLabel ] ) ;
665+ if ( ! ids . length ) {
666+ return undefined ;
667+ }
668+
669+ const details = await inspectContainers ( params , ids ) ;
670+ return details
671+ . filter ( container => container . State . Status !== 'removing' )
672+ . find ( container => {
673+ const labels = container . Config . Labels || { } ;
674+ const containerWorkspaceFolder = labels [ hostFolderLabel ] ;
675+ if ( ! containerWorkspaceFolder ) {
676+ return false ;
677+ }
678+
679+ return normalizeDevContainerLabelPath ( 'win32' , containerWorkspaceFolder ) === normalizedWorkspaceFolder ;
680+ } ) ;
681+ }
682+
617683export async function findContainerAndIdLabels ( params : DockerResolverParameters | DockerCLIParameters , containerId : string | undefined , providedIdLabels : string [ ] | undefined , workspaceFolder : string | undefined , configFile : string | undefined , removeContainerWithOldLabels ?: boolean | string ) {
618684 if ( providedIdLabels ) {
619685 return {
620686 container : containerId ? await inspectContainer ( params , containerId ) : await findDevContainer ( params , providedIdLabels ) ,
621687 idLabels : providedIdLabels ,
622688 } ;
623689 }
690+
691+ const normalizedWorkspaceFolder = workspaceFolder ? normalizeDevContainerLabelPath ( process . platform , workspaceFolder ) : workspaceFolder ;
692+ const normalizedConfigFile = configFile ? normalizeDevContainerLabelPath ( process . platform , configFile ) : configFile ;
693+ const newLabels = [ `${ hostFolderLabel } =${ normalizedWorkspaceFolder } ` , `${ configFileLabel } =${ normalizedConfigFile } ` ] ;
694+ const oldLabels = [ `${ hostFolderLabel } =${ normalizedWorkspaceFolder } ` ] ;
695+
624696 let container : ContainerDetails | undefined ;
625697 if ( containerId ) {
626698 container = await inspectContainer ( params , containerId ) ;
627- } else if ( workspaceFolder && configFile ) {
628- container = await findDevContainer ( params , [ `${ hostFolderLabel } =${ workspaceFolder } ` , `${ configFileLabel } =${ configFile } ` ] ) ;
699+ } else if ( normalizedWorkspaceFolder && normalizedConfigFile ) {
700+ container = await findDevContainer ( params , newLabels ) ;
701+ if ( ! container ) {
702+ container = await findDevContainerByNormalizedLabels ( params , normalizedWorkspaceFolder , normalizedConfigFile ) ;
703+ }
629704 if ( ! container ) {
630705 // Fall back to old labels.
631- container = await findDevContainer ( params , [ `${ hostFolderLabel } =${ workspaceFolder } ` ] ) ;
706+ container = await findDevContainer ( params , oldLabels ) ;
707+ if ( ! container ) {
708+ container = await findLegacyDevContainerByNormalizedWorkspaceFolder ( params , normalizedWorkspaceFolder ) ;
709+ }
632710 if ( container ) {
633711 if ( container . Config . Labels ?. [ configFileLabel ] ) {
634712 // But ignore containers with new labels.
@@ -645,9 +723,7 @@ export async function findContainerAndIdLabels(params: DockerResolverParameters
645723 }
646724 return {
647725 container,
648- idLabels : ! container || container . Config . Labels ?. [ configFileLabel ] ?
649- [ `${ hostFolderLabel } =${ workspaceFolder } ` , `${ configFileLabel } =${ configFile } ` ] :
650- [ `${ hostFolderLabel } =${ workspaceFolder } ` ] ,
726+ idLabels : ! container || container . Config . Labels ?. [ configFileLabel ] ? newLabels : oldLabels ,
651727 } ;
652728}
653729
0 commit comments