Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions eng/Signing.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
<FileSignInfo Include="Newtonsoft.Json.dll" CertificateName="None" />
</ItemGroup>

<ItemGroup>
<!-- Catalog-sign non-PE content files (XML docs, templates). The .cat file
generated by the GenerateCatalogFiles target covers these files; Arcade
signs the .cat itself with Microsoft400. -->
<FileExtensionSignInfo Include=".cat" CertificateName="Microsoft400" />
</ItemGroup>

<PropertyGroup>
<UseDotNetCertificate>true</UseDotNetCertificate>
</PropertyGroup>
Expand Down
157 changes: 157 additions & 0 deletions eng/generate-catalog.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
<#
.SYNOPSIS
Generates a Catalog Definition File (.cdf) and optionally runs makecat.exe to
produce a signed catalog (.cat) file.
.DESCRIPTION
Recursively scans a directory for files matching a filter and produces a .cdf
file suitable for makecat.exe. Optionally invokes makecat.exe to generate the
.cat file directly.

Used in component team pipelines (Arcade SDK, MicroBuild, or custom) to
catalog-sign customer-modifiable or non-PE files that cannot use direct
Authenticode signing.

If -RunMakecat is specified, the script finds and runs makecat.exe automatically.
Otherwise, it prints the commands to run manually.
.PARAMETER RootPath
The directory containing files to include in the catalog.
.PARAMETER CdfPath
The output path for the .cdf file. If not specified and CatOutputPath is set,
defaults to the CatOutputPath with a .cdf extension.
.PARAMETER CatOutputPath
The path where makecat.exe will create the .cat file. Defaults to the CDF
path with a .cat extension.
.PARAMETER Filter
File filter pattern (e.g., '*.js', '*.xml', '*.ttf'). Default: '*.*' (all files).
.PARAMETER RunMakecat
If specified, finds and runs makecat.exe to produce the .cat file.
.PARAMETER WindowsSdkDir
Optional path to the Windows SDK. Used to locate makecat.exe when -RunMakecat is set.
.PARAMETER ErrorIfMakecatNotFound
If specified with -RunMakecat, throws an error when makecat.exe is not found
instead of warning and skipping. Use in CI/official builds.
.EXAMPLE
# Generate CDF only (print manual steps):
.\New-CatalogDefinitionFile.ps1 -RootPath ".\content" -CdfPath ".\obj\my-files.cdf"
.EXAMPLE
# Generate CDF and run makecat.exe:
.\New-CatalogDefinitionFile.ps1 -RootPath ".\content" -CatOutputPath ".\obj\my-files.cat" -Filter "*.js" -RunMakecat
.EXAMPLE
# CI usage (error if makecat.exe not found):
.\New-CatalogDefinitionFile.ps1 -RootPath "$(_ContentRoot)" -CatOutputPath "$(_CatOutputPath)" -Filter "*.js" -RunMakecat -ErrorIfMakecatNotFound
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$RootPath,

[string]$CdfPath,

[string]$CatOutputPath,

[string]$Filter = '*.*',

[string]$WindowsSdkDir = '',

[switch]$RunMakecat,

[switch]$ErrorIfMakecatNotFound
)

$ErrorActionPreference = 'Stop'

if (-not (Test-Path $RootPath)) {
Write-Error "Root path not found: $RootPath"
return
}

# Resolve paths: need at least one of CdfPath or CatOutputPath
if (-not $CdfPath -and -not $CatOutputPath) {
Write-Error "Specify at least one of -CdfPath or -CatOutputPath."
return
}
if (-not $CatOutputPath) {
$CatOutputPath = [System.IO.Path]::ChangeExtension($CdfPath, '.cat')
}
if (-not $CdfPath) {
$CdfPath = [System.IO.Path]::ChangeExtension($CatOutputPath, '.cdf')
}

# Ensure output directories exist
$cdfDir = Split-Path $CdfPath -Parent
if ($cdfDir -and -not (Test-Path $cdfDir)) {
New-Item -ItemType Directory -Path $cdfDir -Force | Out-Null
}
$catDir = Split-Path $CatOutputPath -Parent
if ($catDir -and -not (Test-Path $catDir)) {
New-Item -ItemType Directory -Path $catDir -Force | Out-Null
}

$files = Get-ChildItem -Path $RootPath -Recurse -Filter $Filter -File
if ($files.Count -eq 0) {
Write-Warning "No files matching '$Filter' found under $RootPath - skipping catalog generation."
return
}

$cdfContent = @()
$cdfContent += "[CatalogHeader]"
$cdfContent += "Name=$CatOutputPath"
$cdfContent += "CatalogVersion=2"
$cdfContent += "HashAlgorithms=SHA256"
$cdfContent += ""
$cdfContent += "[CatalogFiles]"

$i = 0
foreach ($f in $files) {
$ext = $f.Extension.TrimStart('.').ToLower()
$label = "${ext}_${i}_" + ($f.Name -replace '[^\w\.-]', '_')
$cdfContent += "<hash>$label=$($f.FullName)"
$i++
}

$cdfContent | Set-Content -Path $CdfPath -Encoding ASCII

Write-Host "Generated CDF with $($files.Count) file(s) matching '$Filter' at $CdfPath"

if ($RunMakecat) {
# Find makecat.exe — ships with the Windows SDK
$makecat = $null
if ($WindowsSdkDir -and (Test-Path $WindowsSdkDir)) {
$makecat = Get-ChildItem -Path (Join-Path $WindowsSdkDir 'bin') -Recurse -Filter 'makecat.exe' -File |
Where-Object { $_.DirectoryName -match 'x64' } |
Sort-Object DirectoryName -Descending |
Select-Object -First 1
}
if (-not $makecat) { $makecat = Get-Command makecat.exe -ErrorAction SilentlyContinue }
if (-not $makecat) {
$sdkRoot = "${env:ProgramFiles(x86)}\Windows Kits\10\bin"
if (Test-Path $sdkRoot) {
$makecat = Get-ChildItem -Path $sdkRoot -Recurse -Filter 'makecat.exe' -File |
Where-Object { $_.DirectoryName -match 'x64' } |
Sort-Object DirectoryName -Descending |
Select-Object -First 1
}
}
if (-not $makecat) {
if ($ErrorIfMakecatNotFound) {
throw "makecat.exe not found. Catalog signing requires the Windows SDK."
}
Write-Warning "makecat.exe not found - skipping catalog generation. Install Windows SDK for catalog signing."
return
}

$makecatPath = if ($makecat -is [System.Management.Automation.CommandInfo]) { $makecat.Source } else { $makecat.FullName }
Write-Host "Using makecat.exe at: $makecatPath"

& $makecatPath $CdfPath
if ($LASTEXITCODE -ne 0) {
throw "makecat.exe failed with exit code $LASTEXITCODE"
}

Write-Host "Generated catalog file: $CatOutputPath"
} else {
Write-Host ""
Write-Host "Next steps:"
Write-Host " 1. Run: makecat.exe `"$CdfPath`""
Write-Host " 2. Sign: dotnet ddsignfiles.dll -- /file:`"$CatOutputPath`" /certs:Microsoft400"
}
19 changes: 19 additions & 0 deletions vsintegration/Vsix/VisualFSharpFull/VisualFSharpDebug.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,23 @@
</Content>
</ItemGroup>
</Target>

<!--
Generate a catalog (.cat) file covering non-PE content files (XML templates)
so the VS signing scan does not flag them as unsigned.
Only runs on Windows (makecat.exe is a Windows SDK tool).
-->
<Target Name="GenerateCatalogFiles" AfterTargets="CreateVsixContainer"
Condition="$([MSBuild]::IsOSPlatform('Windows')) and Exists('$(TargetVsixContainer)')">
<PropertyGroup>
<_ContentRoot>$(TargetVsixContainerIntermediateOutputPath)</_ContentRoot>
<_CatOutputPath>$(TargetVsixContainerIntermediateOutputPath)FSharpDebugContent.cat</_CatOutputPath>
<_ErrorFlag Condition="'$(ContinuousIntegrationBuild)' == 'true' or '$(OfficialBuild)' == 'true'">-ErrorIfMakecatNotFound</_ErrorFlag>
</PropertyGroup>

<Exec Command="powershell.exe -NoProfile -ExecutionPolicy Bypass -Command &quot;&amp; '$(RepoRoot)eng\generate-catalog.ps1' -RootPath '$(_ContentRoot)' -CatOutputPath '$(_CatOutputPath)' -Filter '*.xml' -RunMakecat -WindowsSdkDir '$(WindowsSdkDir)' $(_ErrorFlag)&quot;"
StandardOutputImportance="High"
IgnoreExitCode="true"
Condition="$([MSBuild]::IsOSPlatform('Windows'))" />
</Target>
</Project>
Loading