From 7bb64790eea1434c6cfe75b48edd8c2617a15c2c Mon Sep 17 00:00:00 2001 From: Shuai Zhang Date: Fri, 30 Oct 2026 03:52:25 +0000 Subject: [PATCH 1/2] fix(windows): add license files to the symbols zip and force LF The repository owner decided on 2026-10-30, after the alpha.31 release review, that the symbols zip carries the package's LICENSE.txt and THIRD-PARTY-NOTICES.txt and that packaged text files use LF line endings regardless of the build host's Git configuration. package.ps1 now writes the four text files with LF and fails on a carriage return, and stages the symbols zip with the symbol files and both license files. .gitattributes also checks out the packaged files and LICENSE.txt with LF on every host. Refs: #506 --- .gitattributes | 5 +++++ packaging/windows/package.ps1 | 39 +++++++++++++++++++++++++++-------- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/.gitattributes b/.gitattributes index 340cfc34..48b687e9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,3 +9,8 @@ tools/learn-cli/package-lock.json text eol=lf # C# sources compile with their checked-out bytes; keep them LF on every host. *.cs text eol=lf + +# Files copied into the Windows package keep LF on every host; package.ps1 also +# normalizes them. +packaging/windows/** text eol=lf +LICENSE.txt text eol=lf diff --git a/packaging/windows/package.ps1 b/packaging/windows/package.ps1 index f71a39f5..176095be 100644 --- a/packaging/windows/package.ps1 +++ b/packaging/windows/package.ps1 @@ -10,8 +10,9 @@ AOT requires. The script refuses a dirty worktree, restores in locked mode from repository's nuget.org-only configuration, publishes Native AOT, enforces the archive content allowlist, checks that native imports resolve only to inbox System32 DLLs and API sets, checks the executable's version resource, runs the packaged --version, and -writes the zip and its SHA-256. It also writes the publish output's symbol files into a -separate symbols zip with its own SHA-256, beside the package and never inside it. +writes the zip and its SHA-256. It also writes the publish output's symbol files, with the +same license and notices, into a separate symbols zip with its own SHA-256, beside the +package and never inside it. Packaged text files use LF line endings. The script runs no authentication. It writes only under -OutputDirectory, the repository's ignored obj and bin directories, and the NuGet package folder. @@ -153,10 +154,19 @@ try { } finally { $env:PATH = $savedPath } if ($reported -ne "$CommandName $full") { Fail "packaged --version reported '$reported', expected '$CommandName $full'." } + # Packaged text files use LF line endings whatever the checkout's Git configuration. + function Write-LfText([string] $Text, [string] $Destination) { + $lf = $Text.Replace("`r`n", "`n").Replace("`r", "`n") + [IO.File]::WriteAllText($Destination, $lf, [Text.UTF8Encoding]::new($false)) + } + function Copy-LfText([string] $Source, [string] $Destination) { + Write-LfText ([IO.File]::ReadAllText($Source)) $Destination + } + New-Item -ItemType Directory -Path (Join-Path $stage 'profiles') | Out-Null - Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'profiles\visual-studio-legacy-wam.json') -Destination (Join-Path $stage 'profiles') - Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'README.md') -Destination $stage - Copy-Item -LiteralPath (Join-Path $Repository 'LICENSE.txt') -Destination $stage + Copy-LfText (Join-Path $PSScriptRoot 'profiles\visual-studio-legacy-wam.json') (Join-Path $stage 'profiles\visual-studio-legacy-wam.json') + Copy-LfText (Join-Path $PSScriptRoot 'README.md') (Join-Path $stage 'README.md') + Copy-LfText (Join-Path $Repository 'LICENSE.txt') (Join-Path $stage 'LICENSE.txt') # Third-party notices: the fixed header, then the .NET runtime notices from the # ILCompiler runtime package that the lock file pins. @@ -170,20 +180,31 @@ try { if (-not (Test-Path -LiteralPath $runtimeNotices)) { Fail "runtime notices not found: $runtimeNotices" } $notices = (Get-Content -Raw -LiteralPath (Join-Path $PSScriptRoot 'THIRD-PARTY-NOTICES.header.txt')) + (Get-Content -Raw -LiteralPath $runtimeNotices) - [IO.File]::WriteAllText((Join-Path $stage 'THIRD-PARTY-NOTICES.txt'), $notices, [Text.UTF8Encoding]::new($false)) + Write-LfText $notices (Join-Path $stage 'THIRD-PARTY-NOTICES.txt') $archive = @(Get-ChildItem -LiteralPath $stage -Recurse -File | ForEach-Object { [IO.Path]::GetRelativePath($stage, $_.FullName) } | Sort-Object) $allowed = @("$CommandName.exe", 'LICENSE.txt', 'README.md', 'THIRD-PARTY-NOTICES.txt', 'msalruntime.dll', 'profiles\visual-studio-legacy-wam.json') | Sort-Object if (Compare-Object $archive $allowed) { Fail "archive content differs from the allowlist: $($archive -join ', ')" } + $textFiles = @('LICENSE.txt', 'README.md', 'THIRD-PARTY-NOTICES.txt', 'profiles\visual-studio-legacy-wam.json') + foreach ($name in $textFiles) { + if ([IO.File]::ReadAllBytes((Join-Path $stage $name)) -contains 13) { Fail "$name contains a carriage return." } + } + + # The symbols zip holds the symbol files and the same license and notices. + $symbolsStage = Join-Path $work 'symbols' + New-Item -ItemType Directory -Path $symbolsStage | Out-Null + foreach ($name in $symbolFiles) { Copy-Item -LiteralPath (Join-Path $publish $name) -Destination $symbolsStage } + foreach ($name in @('LICENSE.txt', 'THIRD-PARTY-NOTICES.txt')) { + Copy-Item -LiteralPath (Join-Path $stage $name) -Destination $symbolsStage + } Compress-Archive -Path (Join-Path $stage '*') -DestinationPath $zip -CompressionLevel Optimal $hash = (Get-FileHash -LiteralPath $zip -Algorithm SHA256).Hash.ToLowerInvariant() [IO.File]::WriteAllText("$zip.sha256", "$hash $packageName.zip`n", [Text.UTF8Encoding]::new($false)) - Compress-Archive -LiteralPath ($symbolFiles | ForEach-Object { Join-Path $publish $_ }) ` - -DestinationPath $symbolsZip -CompressionLevel Optimal + Compress-Archive -Path (Join-Path $symbolsStage '*') -DestinationPath $symbolsZip -CompressionLevel Optimal $symbolsHash = (Get-FileHash -LiteralPath $symbolsZip -Algorithm SHA256).Hash.ToLowerInvariant() [IO.File]::WriteAllText("$symbolsZip.sha256", "$symbolsHash $packageName-symbols.zip`n", [Text.UTF8Encoding]::new($false)) @@ -191,7 +212,7 @@ try { Write-Host "Wrote $zip" Write-Host "SHA-256 $hash" - Write-Host "Wrote $symbolsZip ($($symbolFiles -join ', '))" + Write-Host "Wrote $symbolsZip ($(($symbolFiles + 'LICENSE.txt', 'THIRD-PARTY-NOTICES.txt') -join ', '))" Write-Host "SHA-256 $symbolsHash" } finally { From b158130f1d57b00c9c08264b60e014c47ae198b1 Mon Sep 17 00:00:00 2001 From: Shuai Zhang Date: Fri, 30 Oct 2026 03:52:28 +0000 Subject: [PATCH 2/2] docs(design): describe the LF package files and the symbols zip license Describe the bundled Profile as JSON-equal to the schema candidate, which is what the test asserts, and record the LF text files and the license and notices in the symbols zip. Refs: #506 --- docs/designs/windows-ado-authentication.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/designs/windows-ado-authentication.md b/docs/designs/windows-ado-authentication.md index 902adf6a..e74fe2b4 100644 --- a/docs/designs/windows-ado-authentication.md +++ b/docs/designs/windows-ado-authentication.md @@ -153,7 +153,7 @@ Native AOT, and admits exactly this content: | --- | --- | | `azureauth-unofficial.exe` | Native AOT publish output. | | `msalruntime.dll` | NativeInterop's `win-x64` asset from the same publish; it must stay beside the executable because loading is restricted to the application directory and System32. | -| `profiles/visual-studio-legacy-wam.json` | `packaging/windows/profiles/`, byte-for-byte the schema's [Visual Studio legacy candidate](#visual-studio-legacy-candidate); a test asserts that equality and that the product reader accepts the file. | +| `profiles/visual-studio-legacy-wam.json` | `packaging/windows/profiles/`, JSON-equal to the schema's [Visual Studio legacy candidate](#visual-studio-legacy-candidate) (same members and values); a test asserts that equality and that the product reader accepts the file. | | `README.md` | `packaging/windows/README.md`: unofficial notice, extraction, Windows and WSL use, the bundled Profile's ownership and limits, unsigned and SmartScreen notes, and removal. | | `LICENSE.txt` | The repository license. | | `THIRD-PARTY-NOTICES.txt` | A fixed header for the MSAL components and the .NET runtime notices from the locked ILCompiler runtime package. | @@ -166,9 +166,12 @@ System32 DLLs and API sets, rejecting Visual C++ redistributable DLLs by name be they are not part of Windows even when a development host has them in System32. The script then checks the version resource described below and runs the staged executable's `--version` from its own directory -with `PATH` limited to System32, and finally writes the zip and a SHA-256 file. Beside -them it writes `azureauth-unofficial--win-x64-symbols.zip`, holding the symbol -files, with its own SHA-256 file; the script refuses to overwrite any of these outputs. +with `PATH` limited to System32, and finally writes the zip and a SHA-256 file. The +four text files are written with LF line endings whatever the checkout's Git +configuration, and the script fails if any contains a carriage return. Beside them it +writes `azureauth-unofficial--win-x64-symbols.zip`, holding the symbol files and +the same `LICENSE.txt` and `THIRD-PARTY-NOTICES.txt`, with its own SHA-256 file; the +script refuses to overwrite any of these outputs. These checks are packaging checks on the build host, not evidence of deployment without development-machine dependencies.