Replace Microbuild with 1ES PT + ESRP#1972
Conversation
Remove all MicroBuild.Core PackageReference declarations and the associated <FilesToSign> ItemGroups from GVFS.Payload, GVFS.Installers, and FastFetch, and remove MicroBuild.Core from Directory.Packages.props. Also drop the BeforeTargets="SignFiles" hooks from the CreatePayload and CreateInstaller targets — without MicroBuild.Core's targets the SignFiles target no longer exists, and the hooks are unnecessary now that signing is performed externally by the release pipeline. This commit is a pure removal of the in-build signing wiring; signing itself moves to ESRP-driven steps in the release pipeline in a follow-up commit. The GitHub Actions CI / PR build never signed anything and is unaffected. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Add a Condition="'$(SkipCreateInstaller)' != 'true'" to the CreateInstaller MSBuild target so callers can opt out of the Inno Setup compile step. This lets the release pipeline build all of the managed binaries first (via Build.bat with the env var set), ESRP-sign them in place, and only then invoke the Inno Setup compile to produce SetupGVFS.<version>.exe — packaging the already-signed binaries in a single, deterministic pass instead of building, deleting, and rebuilding the installer. Default behavior is unchanged: with the property unset (the case for local builds and the GitHub Actions PR/CI workflow), CreateInstaller fires as before during the regular Build target. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
There was a problem hiding this comment.
Pull request overview
This PR replaces MicroBuild-based release signing with a 1ES Pipeline Template + ESRP signing flow and adds draft GitHub Release publishing for release builds.
Changes:
- Removes MicroBuild package/signing metadata from project files.
- Adds ESRP signing template and Azure release pipeline stages for build, signing, artifact staging, and GitHub release creation.
- Adds CI helper scripts for installing VS C++ workload and enabling ProjFS.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/Build.bat | Fails the build when VS MSBuild is unavailable. |
| GVFS/GVFS.Payload/layout.bat | Removes C++ code-analysis marker files from payload output. |
| GVFS/GVFS.Payload/GVFS.Payload.csproj | Removes MicroBuild signing integration from payload generation. |
| GVFS/GVFS.Installers/GVFS.Installers.csproj | Removes MicroBuild signing and allows installer creation to be skipped. |
| GVFS/FastFetch/FastFetch.csproj | Removes MicroBuild signing integration. |
| Directory.Packages.props | Removes the MicroBuild.Core package version. |
| .azure-pipelines/scripts/install-vs-cpp-workload.ps1 | Adds VS C++ workload bootstrap/verification logic. |
| .azure-pipelines/scripts/enable-projfs.ps1 | Adds ProjFS optional-feature enablement logic for CI. |
| .azure-pipelines/release.yml | Replaces MicroBuild release pipeline with 1ES PT build/sign/release flow. |
| .azure-pipelines/esrp/sign.yml | Adds reusable ESRP signing step template. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| inlineOperation: | | ||
| [ | ||
| { | ||
| "KeyCode": "CP-230012", | ||
| "OperationCode": "SigntoolSign", | ||
| "ToolName": "sign", | ||
| "ToolVersion": "1.0", | ||
| "Parameters": { | ||
| "OpusName": "Microsoft", | ||
| "OpusInfo": "https://www.microsoft.com", | ||
| "FileDigest": "/fd SHA256", | ||
| "PageHash": "/NPH", | ||
| "TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" | ||
| } | ||
| }, | ||
| { | ||
| "KeyCode": "CP-230012", | ||
| "OperationCode": "SigntoolVerify", | ||
| "ToolName": "sign", | ||
| "ToolVersion": "1.0", | ||
| "Parameters": {} | ||
| } | ||
| ] |
There was a problem hiding this comment.
Fair point, but given we treat the ESRP inline op JSON blob as an opaque data structure (generated by an ESRP tool we use) I'd like to keep this per artifact. Especially if we ever decide to publish, say, a NuGet package in the future.. that uses different signing key codes and settings.
| inputs: | ||
| - input: pipelineArtifact | ||
| artifactName: Installer | ||
| targetPath: $(Pipeline.Workspace)/assets/Installer | ||
| - input: pipelineArtifact | ||
| artifactName: Symbols | ||
| targetPath: $(Pipeline.Workspace)/assets/Symbols |
There was a problem hiding this comment.
I believe that we've never shipped FastFetch through the public project before. But should we? Is this an opportunity to unify and clarify that tool's signing and publishing story?
There was a problem hiding this comment.
Yeah, I intentionally omitted publishing FastFetch since we've not published it externally (at least not in the past 6 years!) If we find teams or others want FastFetch published 'officially' then we can add this easily to the GitHub releases later. For now it can be manually downloaded from the pipeline artifacts.
| - checkout: self | ||
| displayName: 'Checkout VFS for Git' | ||
| path: vfsforgit/src | ||
|
|
There was a problem hiding this comment.
Should we even allow manual runs? Should the only trigger be a push to the release branch (gated by PR)?
There was a problem hiding this comment.
We have allowed manual runs in the past to allow for testing and debugging of the release workflow against non-release branches. Pipeline queue permissions are still restricted to members of the Git Client team, so only a restricted few of us can make releases still.
I'd like to keep manual as an option for now until we have put this pipeline through more real-world usage :-)
… ESRP Rewrite the release pipeline to extend the 1ES Pipeline Templates directly and to perform code signing via inline ADO tasks, replacing the existing MicroBuild.1ES.Official.yml@MicroBuildTemplate + MicroBuild.Core <FilesToSign> mechanism. This new pipeline matches the pattern already used by microsoft/git and git-ecosystem/git-credential-manager. We build the Payload and FastFetch with SkipCreateInstaller=true so the Inno Setup compile is deferred until after ESRP signing, then build the installer in a dedicated dotnet build step (--no-dependencies prevents the Payload layout from re-running and overwriting the freshly signed binaries). We also add a release stage with a 1ES releaseJob that publishes a draft GitHub Release on microsoft/VFSForGit attaching the installer asset. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Download the Symbols pipeline artifact alongside the Installer in the release stage, zip it up via ArchiveFiles@2, and attach it to the draft GitHub Release so consumers (and crash-dump triage) can grab the matching .pdb / native debug symbols for the binaries shipped in SetupGVFS.<ver>.exe. Both downloads now land under $(Pipeline.Workspace)/_final so the GitHubRelease@1 'assets' glob picks up SetupGVFS.*.exe and Symbols.zip from the same staging directory. Also enable the auto-generated change log on the release while we're already in the GitHubRelease@1 step. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Add a UseDotNet@2 task with useGlobalJson: true before the build step so the agent uses exactly the SDK version pinned in global.json This matches the behavior of the GitHub Actions build workflow, which already uses actions/setup-dotnet with global-json-file. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Previously Build.bat warned and continued when it could not locate VS MSBuild (via either PATH or vswhere), silently skipping the native C++ projects (GitHooksLoader, GVFS.NativeTests, GVFS.PostIndexChangedHook, GVFS.ReadObjectHook, GVFS.VirtualFileSystemHook). That meant a build on a machine without the C++ workload would happily produce an installer with the native binaries missing. VS MSBuild and the C++ workload are not optional for any GVFS build that's expected to run, so make a missing toolchain a hard error instead. Anyone hitting it should install Visual Studio with the 'Desktop development with C++' workload (or VS Build Tools with VCTools). Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
GVFS sets PublishAot=true in Directory.Build.props for every managed
project, which means publishing GVFS.exe and friends needs the native
C++ toolchain (link.exe, the Windows SDK, etc.) at build time. The
1ES win-x86_64-ado1es image used by the new release pipeline has no VS
installed so the Native AOT publish step fails with:
error : Platform linker not found.
..per the documented NativeAOT prerequisites
(https://aka.ms/nativeaot-prerequisites), which call for VS 2022 with
the 'Desktop development with C++' workload.
Add a PowerShell script that ensures the C++ workload is present
before Build.bat runs, and wire it into release.yml. The script:
* Bootstraps vswhere.exe from its GitHub release if the standard
Program Files location does not have it (so it works on minimal
images too).
* Uses 'vswhere -requires NativeDesktop VCTools -requiresAny' to
decide whether the workload is already installed on any existing
Visual Studio install. If yes, exit 0 and let the build run.
* If a Visual Studio install exists but lacks the workload, run
its setup.exe modify to add the appropriate workload ID
(NativeDesktop for full VS, VCTools for Build Tools).
* If no Visual Studio is installed at all, download the VS Build
Tools bootstrapper from aka.ms and install it with the VCTools
workload from scratch.
* Treat exit codes 0 and 3010 (success, reboot needed) as success
for both modify and fresh install.
* Re-run the same vswhere requirement check post-install to
verify.
Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
VFS for Git's runtime and several of its unit tests P/Invoke into
ProjectedFSLib.dll (e.g. Microsoft.Windows.ProjFS.ProjFSNative
.PrjDoesNameContainWildCards via ActiveEnumeration). That DLL is only
present on disk when the 'Client-ProjFS' Windows optional feature is
enabled.
The 1ES win-x86_64-ado1es image used by the new release pipeline does
not have that feature enabled, so RunUnitTests.bat fails with:
System.DllNotFoundException : Unable to load DLL
'ProjectedFSLib.dll' or one of its dependencies. (0x8007007E)
at Microsoft.Windows.ProjFS.ProjFSNative.PrjDoesNameContainWildCards(...)
at GVFS.Platform.Windows.ActiveEnumeration.SaveFilter(...)
at GVFS.UnitTests.Windows.Virtualization.ActiveEnumerationTests
.CannotSetMoreThanOneFilter()
Add a small PowerShell helper that enables the feature, and run it as
a build prereq. The script is a no-op when the feature is already
enabled, so it's safe to keep across image refreshes.
Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Visual Studio's MSBuild for native C++ projects emits a <binary>.exe.lastcodeanalysissucceeded marker file next to each produced executable to record that PREfast / code analysis ran successfully on it. Those marker files are pure build-time bookkeeping with no runtime purpose, but the GVFS.Payload layout step xcopies the entire native bin\x64\<Config>\ folder for each of GitHooksLoader, GVFS.PostIndexChangedHook, GVFS.ReadObjectHook and GVFS.VirtualFileSystemHook -- so the markers end up in the Payload, get packaged into SetupGVFS.exe, and finally land on end-user machines as e.g. GitHooksLoader.exe.lastcodeanalysissucceeded in 'C:\Program Files\VFS for Git\'. Extend the existing layout.bat cleanup block (which already strips *.runtimeconfig.json, *.deps.json, and orphaned managed PDBs) to also recursively delete *.lastcodeanalysissucceeded from the output directory so they're never shipped. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Refactor the three EsrpCodeSigning@6 invocations in release.yml
(Payload binaries, FastFetch, installer) to use a shared
.azure-pipelines/esrp/sign.yml step template, modeled on the same
template in microsoft/git.
The template:
* Forwards the per-call inputs (displayName, folderPath, pattern,
inlineOperation) to EsrpCodeSigning@6.
* Provides defaults for the ESRP connection parameters that point
at the standard pipeline variables ($(esrpAppConnectionName),
$(esrpClientId), etc.), so callers don't repeat them.
* Runs an inline PowerShell@2 step right after each signing
operation that removes the CodeSignSummary-<guid>.md report
ESRP CLI drops into the signing folder. Without this, those
.md files would otherwise end up packaged into SetupGVFS.exe
(Payload), or uploaded as part of the FastFetch and Installer
pipeline artifacts.
Net effect on release.yml is a small reduction in line count and,
more importantly, cleanup is no longer something a future caller
can forget to wire up.
Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
|
Updated following Copilot's comments.. here's the range-diff: 1: b20b5552 = 1: b20b5552 Build: drop MicroBuild signing wiring
2: 21364aeb = 2: 21364aeb GVFS.Installers: gate CreateInstaller on $(SkipCreateInstaller)
3: f4b46619 ! 3: f3af9cd7 .azure-pipelines: migrate release pipeline from MicroBuild to 1ESPT + ESRP
@@ .azure-pipelines/release.yml
+ - stage: release
+ displayName: 'Release'
+ dependsOn: [build]
-+ condition: and(succeeded(), eq('${{ parameters.github }}', true))
++ # Only publish a draft GitHub release when ESRP signing was enabled in
++ # this run -- otherwise we would risk uploading unsigned installer
++ # binaries to the public release workflow.
++ condition: and(succeeded(), eq('${{ parameters.github }}', true), eq('${{ parameters.esrp }}', true))
+ jobs:
+ - job: github
+ displayName: 'Publish GitHub release'
4: 58460323 = 4: 2fab771a .azure-pipelines: publish debug symbols with GitHub Release
5: 64fc64e0 = 5: 11a41476 .azure-pipelines: install pinned .NET SDK from global.json
6: 56aec454 = 6: d7a11ae7 Build.bat: fail when VS MSBuild is missing
7: b57f278d ! 7: 8721e68c .azure-pipelines: install VS C++ workload before build
@@ .azure-pipelines/release.yml: extends:
## .azure-pipelines/scripts/install-vs-cpp-workload.ps1 (new) ##
@@
+#
-+# Ensure the Visual Studio "Desktop development with C++" workload is
-+# installed on the build agent.
++# Ensure a Visual Studio 2022 (or newer) install with the "Desktop
++# development with C++" workload is present on the build agent.
+#
-+# .NET NativeAOT publishing (used by every managed VFS for Git project via
-+# PublishAot=true in Directory.Build.props) requires the C++ build tools
-+# from this workload at publish time.
++# .NET NativeAOT publishing (used by every product-facing managed VFS for
++# Git project via PublishAot=true in Directory.Build.props) requires the
++# C++ build tools from this workload at publish time. The native VFS
++# projects also build against the v143 toolset, which ships with VS 2022.
+#
+# This script handles three situations:
-+# 1. The C++ workload is already present -> exit early.
-+# 2. Visual Studio (any product) is installed but the C++ workload is
-+# missing -> modify the install to add it.
-+# 3. No Visual Studio at all -> install VS Build Tools 2022 with the
-+# VC tools workload.
++# 1. A VS 2022+ install with the C++ workload is already present
++# -> exit early.
++# 2. A VS 2022+ install (any product) is present but the C++ workload
++# is missing -> modify that install to add it.
++# 3. No VS 2022+ install at all -> install VS Build Tools 2022 with
++# the VC tools workload. (An older VS install, e.g. VS 2019, is
++# ignored here -- we leave it alone and install VS 2022 alongside.)
+#
+# vswhere.exe is bootstrapped from GitHub if not already on disk.
+#
@@ .azure-pipelines/scripts/install-vs-cpp-workload.ps1 (new)
+$vswhereDownloadUrl = 'https://github.com/microsoft/vswhere/releases/latest/download/vswhere.exe'
+$buildToolsDownloadUrl = 'https://aka.ms/vs/17/release/vs_BuildTools.exe'
+
++# The native VFS projects build against the v143 toolset, which ships with
++# Visual Studio 2022 (product line 17.x). VS 2019 (16.x) carries v142 and
++# is not sufficient -- so all vswhere queries below are scoped to 17.0+.
++$minVsVersion = '[17.0,)'
++
+# Either of these workloads provides the C++ build tools we need.
+# Microsoft.VisualStudio.Workload.NativeDesktop = "Desktop development with C++" (Community/Pro/Enterprise).
+# Microsoft.VisualStudio.Workload.VCTools = "C++ build tools" (Build Tools).
@@ .azure-pipelines/scripts/install-vs-cpp-workload.ps1 (new)
+ [Parameter(Mandatory = $true)] [string] $VswhereExe,
+ [string[]] $RequiredWorkloads
+ )
-+ $vswhereArgs = @('-latest', '-prerelease', '-products', '*', '-format', 'json')
++ $vswhereArgs = @('-latest', '-prerelease', '-products', '*', '-version', $script:minVsVersion, '-format', 'json')
+ if ($RequiredWorkloads -and $RequiredWorkloads.Count -gt 0) {
+ $vswhereArgs += '-requires'
+ $vswhereArgs += $RequiredWorkloads
@@ .azure-pipelines/scripts/install-vs-cpp-workload.ps1 (new)
+# --- Find any VS install (regardless of workloads) ---
+$install = Find-VsInstall -VswhereExe $vswhereExe
+
-+# --- If no VS at all, install VS Build Tools with the VC workload ---
++# --- If no VS 2022+ install at all, install VS Build Tools 2022 with the VC workload ---
+if (-not $install) {
-+ Write-Host "No Visual Studio installation found; installing VS Build Tools 2022 with the C++ workload..."
++ Write-Host "No Visual Studio 2022 (or newer) installation found; installing VS Build Tools 2022 with the C++ workload..."
+ $bootstrapper = Join-Path $env:TEMP 'vs_BuildTools.exe'
+ Write-Host "Downloading VS Build Tools bootstrapper from $buildToolsDownloadUrl..."
+ Invoke-WebRequest -Uri $buildToolsDownloadUrl -OutFile $bootstrapper -UseBasicParsing
8: c859c6fc ! 8: 0f30e3db .azure-pipelines: enable Projected File System for unit tests
@@ .azure-pipelines/scripts/enable-projfs.ps1 (new)
+$result = Enable-WindowsOptionalFeature -Online -FeatureName $featureName -NoRestart -ErrorAction Stop
+
+if ($result.RestartNeeded) {
-+ Write-Warning "Windows optional feature '$featureName' was enabled but a restart is required to take effect."
++ # The pipeline runs unit tests immediately after this script which P/Invoke
++ # into ProjectedFSLib.dll. If the OS reports a reboot is required to make
++ # the feature usable, the build agent is in an inconsistent state and the
++ # tests will fail unpredictably -- so fail fast here instead.
++ throw "Windows optional feature '$featureName' was enabled but a restart is required to take effect; failing the build."
+} else {
+ Write-Host "INFO: Windows optional feature '$featureName' is now enabled."
+}
9: 51e58dc9 = 9: c21ed310 GVFS.Payload: drop VS code-analysis marker files from payload
10: fe60e1c3 = 10: 6eaea92f .azure-pipelines: introduce esrp/sign.yml template
|
|
Run with the latest changes: https://dev.azure.com/mseng/1ES/_build/results?buildId=31501969 All green! I specifically ran with GitHub=true and ESRP=false to trigger the new condition we have added on release publishing.. it worked - step was skipped. |
| image: ubuntu-x86_64-ado1es | ||
| os: linux | ||
| image: win-x86_64-ado1es | ||
| os: windows |
There was a problem hiding this comment.
Is this change intentional? Windows runners tend to be slower than Linux runners...
There was a problem hiding this comment.
We need to re-archive our PDB symbols since the artifact that gets uploaded/downloaded between jobs gets the _manifest/ and internal SBOM stuff added. We need to keep release jobs separate since 1ES policies restrict certain job types from being 'build' vs 'release'.
We want to publish as a ZIP and the ArchiveFiles@2 task requires zip, which isn't on the Ubuntu images (nor can we install it mid-pipeline for reasons above).
Microbuild doesn't give us as much control as we'd like over our build images, pool admin, and signing process. Since moving to NativeAOT, the SignFiles target of Microbuild runs at the wrong time.
The fact we're using Microbuild is a holdover from the organisational parentage of the then GVFS team back in 2017/2018. The Git Client team also maintains Git Credential Manager and Microsoft Git, where we use regular 1ES PT builds and ESRP directly for signing. Let's standardise on this model.
Also, whilst we're at it, we can add automatic GitHub release publishing (as draft)! See an example of a run with this branch:
This PR is probably easiest to review commit by commit, and referencing microsoft/git and GCM's build pipelines for prior art.