@@ -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,98 @@ 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+ const normalizedContainerWorkspaceFolder = containerWorkspaceFolder && normalizeDevContainerLabelPath ( 'win32' , containerWorkspaceFolder ) ;
650+ if ( ! normalizedContainerWorkspaceFolder || normalizedContainerWorkspaceFolder !== normalizedWorkspaceFolder ) {
651+ return false ;
652+ }
653+
654+ const containerConfigFile = labels [ configFileLabel ] ;
655+ const normalizedContainerConfigFile = containerConfigFile && normalizeDevContainerLabelPath ( 'win32' , containerConfigFile ) ;
656+ return ! ! normalizedContainerConfigFile
657+ && normalizedContainerConfigFile === normalizedConfigFile ;
658+ } ) ;
659+ }
660+
661+ async function findLegacyDevContainerByNormalizedWorkspaceFolder ( params : DockerResolverParameters | DockerCLIParameters , normalizedWorkspaceFolder : string ) {
662+ if ( process . platform !== 'win32' ) {
663+ return undefined ;
664+ }
665+
666+ const ids = await listContainers ( params , true , [ hostFolderLabel ] ) ;
667+ if ( ! ids . length ) {
668+ return undefined ;
669+ }
670+
671+ const details = await inspectContainers ( params , ids ) ;
672+ return details
673+ . filter ( container => container . State . Status !== 'removing' )
674+ . find ( container => {
675+ const labels = container . Config . Labels || { } ;
676+ const containerWorkspaceFolder = labels [ hostFolderLabel ] ;
677+ const normalizedContainerWorkspaceFolder = containerWorkspaceFolder && normalizeDevContainerLabelPath ( 'win32' , containerWorkspaceFolder ) ;
678+ return normalizedContainerWorkspaceFolder === normalizedWorkspaceFolder ;
679+ } ) ;
680+ }
681+
617682export async function findContainerAndIdLabels ( params : DockerResolverParameters | DockerCLIParameters , containerId : string | undefined , providedIdLabels : string [ ] | undefined , workspaceFolder : string | undefined , configFile : string | undefined , removeContainerWithOldLabels ?: boolean | string ) {
618683 if ( providedIdLabels ) {
619684 return {
620685 container : containerId ? await inspectContainer ( params , containerId ) : await findDevContainer ( params , providedIdLabels ) ,
621686 idLabels : providedIdLabels ,
622687 } ;
623688 }
689+
690+ const normalizedWorkspaceFolder = workspaceFolder ? normalizeDevContainerLabelPath ( process . platform , workspaceFolder ) : workspaceFolder ;
691+ const normalizedConfigFile = configFile ? normalizeDevContainerLabelPath ( process . platform , configFile ) : configFile ;
692+ const oldLabels = [ `${ hostFolderLabel } =${ normalizedWorkspaceFolder } ` ] ;
693+ const newLabels = [ ...oldLabels , `${ configFileLabel } =${ normalizedConfigFile } ` ] ;
694+
624695 let container : ContainerDetails | undefined ;
625696 if ( containerId ) {
626697 container = await inspectContainer ( params , containerId ) ;
627- } else if ( workspaceFolder && configFile ) {
628- container = await findDevContainer ( params , [ `${ hostFolderLabel } =${ workspaceFolder } ` , `${ configFileLabel } =${ configFile } ` ] ) ;
698+ } else if ( normalizedWorkspaceFolder && normalizedConfigFile ) {
699+ container = await findDevContainer ( params , newLabels ) ;
700+ if ( ! container ) {
701+ container = await findDevContainerByNormalizedLabels ( params , normalizedWorkspaceFolder , normalizedConfigFile ) ;
702+ }
629703 if ( ! container ) {
630704 // Fall back to old labels.
631- container = await findDevContainer ( params , [ `${ hostFolderLabel } =${ workspaceFolder } ` ] ) ;
705+ container = await findDevContainer ( params , oldLabels ) ;
706+ if ( ! container ) {
707+ container = await findLegacyDevContainerByNormalizedWorkspaceFolder ( params , normalizedWorkspaceFolder ) ;
708+ }
632709 if ( container ) {
633710 if ( container . Config . Labels ?. [ configFileLabel ] ) {
634711 // But ignore containers with new labels.
@@ -645,9 +722,7 @@ export async function findContainerAndIdLabels(params: DockerResolverParameters
645722 }
646723 return {
647724 container,
648- idLabels : ! container || container . Config . Labels ?. [ configFileLabel ] ?
649- [ `${ hostFolderLabel } =${ workspaceFolder } ` , `${ configFileLabel } =${ configFile } ` ] :
650- [ `${ hostFolderLabel } =${ workspaceFolder } ` ] ,
725+ idLabels : ! container || container . Config . Labels ?. [ configFileLabel ] ? newLabels : oldLabels ,
651726 } ;
652727}
653728
0 commit comments