Skip to content

Commit 68a134b

Browse files
Handle cross-platform YAML path aliases
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 2a1bb48 commit 68a134b

2 files changed

Lines changed: 72 additions & 7 deletions

File tree

src/functions/public/Import-Yaml.ps1

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -457,12 +457,18 @@ function Import-Yaml {
457457
[System.IO.Directory]::EnumerateFileSystemEntries($directoryPath)
458458
)) {
459459
$entryName = [System.IO.Path]::GetFileName($directoryEntry)
460+
$entryIdentityName = $entryName.Normalize(
461+
[System.Text.NormalizationForm]::FormC
462+
)
460463
$nameGroup = $null
461-
if ($nameGroups.TryGetValue($entryName, [ref]$nameGroup)) {
464+
if ($nameGroups.TryGetValue(
465+
$entryIdentityName,
466+
[ref]$nameGroup
467+
)) {
462468
$nameGroup.Count++
463469
} else {
464470
$nameGroups.Add(
465-
$entryName,
471+
$entryIdentityName,
466472
[pscustomobject]@{
467473
Count = 1
468474
CanonicalName = $entryName
@@ -474,7 +480,13 @@ function Import-Yaml {
474480
}
475481

476482
$nameGroup = $null
477-
if (-not $nameGroups.TryGetValue($leafName, [ref]$nameGroup)) {
483+
$leafIdentityName = $leafName.Normalize(
484+
[System.Text.NormalizationForm]::FormC
485+
)
486+
if (-not $nameGroups.TryGetValue(
487+
$leafIdentityName,
488+
[ref]$nameGroup
489+
)) {
478490
$identityFailed = $true
479491
break
480492
}
@@ -503,7 +515,13 @@ function Import-Yaml {
503515
}
504516
$identityComponents.Add($rootPath)
505517
$identityComponents.Reverse()
506-
$identityKey = $identityComponents -join [char]0x1F
518+
$identityKeyBuilder = [System.Text.StringBuilder]::new()
519+
foreach ($identityComponent in $identityComponents) {
520+
$null = $identityKeyBuilder.Append($identityComponent.Length)
521+
$null = $identityKeyBuilder.Append(':')
522+
$null = $identityKeyBuilder.Append($identityComponent)
523+
}
524+
$identityKey = $identityKeyBuilder.ToString()
507525
}
508526

509527
$null = $pathsByIdentity.TryAdd($identityKey, $resolvedPath)

tests/Import-Yaml.Tests.ps1

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,27 +85,74 @@ Describe 'Import-Yaml' {
8585
@($result.value) | Should -Be @('first', 'second')
8686
}
8787

88-
It 'keeps case-distinct files separate on case-sensitive filesystems' -Skip:$IsWindows {
88+
It 'keeps case-distinct files separate on case-sensitive filesystems' {
8989
$upperPath = Join-Path $TestDrive 'Case.yaml'
9090
$lowerPath = Join-Path $TestDrive 'case.yaml'
9191
[System.IO.File]::WriteAllText($upperPath, 'value: upper')
9292
[System.IO.File]::WriteAllText($lowerPath, 'value: lower')
93+
if ([System.IO.File]::ReadAllText($upperPath) -eq
94+
[System.IO.File]::ReadAllText($lowerPath)) {
95+
Set-ItResult -Skipped -Because 'the test filesystem is case-insensitive'
96+
return
97+
}
9398

9499
$result = @(Import-Yaml -Path @($upperPath, $lowerPath))
95100

96101
@($result.value) | Should -Be @('upper', 'lower')
97102
}
98103

99-
It 'suppresses path case variants on case-insensitive filesystems' -Skip:(-not $IsWindows) {
104+
It 'suppresses path case variants on case-insensitive filesystems' {
100105
$path = Join-Path $TestDrive 'CaseVariant.yaml'
101106
[System.IO.File]::WriteAllText($path, 'value: once')
107+
$caseVariantPath = $path.ToLowerInvariant()
108+
try {
109+
$null = [System.IO.File]::GetAttributes($caseVariantPath)
110+
} catch [System.IO.FileNotFoundException] {
111+
Set-ItResult -Skipped -Because 'the test filesystem is case-sensitive'
112+
return
113+
}
114+
115+
$result = @(Import-Yaml -Path @($path, $caseVariantPath))
116+
117+
$result.Count | Should -Be 1
118+
$result[0].value | Should -Be 'once'
119+
}
120+
121+
It 'suppresses Unicode normalization aliases on matching filesystems' {
122+
$composedPath = Join-Path $TestDrive "caf$([char] 0x00E9).yaml"
123+
$decomposedPath = Join-Path $TestDrive "cafe$([char] 0x0301).yaml"
124+
[System.IO.File]::WriteAllText($composedPath, 'value: once')
125+
try {
126+
$null = [System.IO.File]::GetAttributes($decomposedPath)
127+
} catch [System.IO.FileNotFoundException] {
128+
Set-ItResult -Skipped -Because (
129+
'the test filesystem distinguishes Unicode normalization forms'
130+
)
131+
return
132+
}
102133

103-
$result = @(Import-Yaml -Path @($path, $path.ToLowerInvariant()))
134+
$result = @(Import-Yaml -LiteralPath @($composedPath, $decomposedPath))
104135

105136
$result.Count | Should -Be 1
106137
$result[0].value | Should -Be 'once'
107138
}
108139

140+
It 'keeps delimiter-bearing Unix paths distinct' -Skip:$IsWindows {
141+
$separator = [char] 0x001F
142+
$firstDirectory = Join-Path $TestDrive 'identity-a'
143+
$secondDirectory = Join-Path $TestDrive "identity-a${separator}identity-b"
144+
$null = [System.IO.Directory]::CreateDirectory($firstDirectory)
145+
$null = [System.IO.Directory]::CreateDirectory($secondDirectory)
146+
$firstPath = Join-Path $firstDirectory "identity-b${separator}value.yaml"
147+
$secondPath = Join-Path $secondDirectory 'value.yaml'
148+
[System.IO.File]::WriteAllText($firstPath, 'value: first')
149+
[System.IO.File]::WriteAllText($secondPath, 'value: second')
150+
151+
$result = @(Import-Yaml -LiteralPath @($firstPath, $secondPath))
152+
153+
@($result.value | Sort-Object) | Should -Be @('first', 'second')
154+
}
155+
109156
It 'sorts by canonical identity regardless of duplicate path spelling' {
110157
$firstPath = Join-Path $TestDrive 'canonical-a.yaml'
111158
$secondPath = Join-Path $TestDrive 'canonical-b.yaml'

0 commit comments

Comments
 (0)