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/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. 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 {