azure-pipelines: migrate installer release pipeline from GitHub Actions#920
Draft
dscho wants to merge 8 commits into
Draft
azure-pipelines: migrate installer release pipeline from GitHub Actions#920dscho wants to merge 8 commits into
dscho wants to merge 8 commits into
Conversation
azure-pipelines: add stub release pipeline for Azure Add a release-pipeline scaffold for microsoft/git on Azure Pipelines, structured around a prereqs stage, a per-platform build stage with placeholder jobs, and a release stage that downloads the build artifacts and publishes them to a draft GitHub release. The per-platform build, signing, and validation logic lands in subsequent commits. The pipeline targets Microsoft-internal 1ES-hosted images across Windows x64, Windows ARM64, macOS, Ubuntu x64, and Ubuntu ARM64. Windows and Linux matrix entries carry a `poolArch` dimension because the 1ES hosted pools select their image from `hostArchitecture`; an arm64 entry on a default x64 pool would silently grab the wrong image. macOS uses the same matrix-parameter shape as Windows and Linux, so future macOS variants drop in the same way an extra Windows toolchain would. The prereqs stage derives the Git version, tag name, and tag SHA via resolve-version.sh and exposes them as pipeline variables for downstream stages to pick up. For that walk to find tags at all, the prereqs checkout uses fetchDepth: 0 / fetchTags: true. setup-git-bash.cmd is the Windows-side prerequisite that prepends Git Bash to PATH, since the bare hosted image does not provide a bash for Bash@3 tasks to find. ESRP signing to be added later. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com> Assisted-by: Claude Opus 4.7 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Bring ESRP code signing into the release pipeline, gated behind an `esrp` boolean parameter that defaults to false until the rest of the signing wiring catches up. Microsoft policy precludes shipping unsigned binaries from this pipeline, so every per-platform build job needs an obvious place to plug signing in. The Windows flow runs through a custom esrpsign.sh script rather than the EsrpCodeSigning ADO task. The custom script gives the later Windows installer commit a CLI-shaped seam it can register as Git for Windows' `git signtool` alias from build-extra's release pipeline, so every binary embedded inside the installer gets signed rather than only the outer .exe wrapper. The canned ADO task does not expose that integration point. The accompanying setup template uses AzureCLI@2 to bind to the WIF service connection by name rather than by GUID, and relies on addSpnToEnvironment to surface the service principal ID, tenant ID, and connection GUID at runtime via ENDPOINT_URL_* env vars. That way esrpsign.sh composes the auth JSON with no hardcoded identifiers leaking into the repository. EsrpClientTool@4 takes care of downloading and caching the ESRP client binary itself. macOS and Linux take the simpler path: the EsrpCodeSigning@6 task via a shared sign.yml template. macOS in particular requires an archive submission (useArchive: true), so centralising the copy/zip/sign/extract cycle in the template keeps each platform job from re-implementing it. The Linux hosted agents do not ship with .NET, which EsrpCodeSigning requires. UseDotNet@2 installs the .NET 8 SDK ahead of the signing template invocation so Linux signing works out of the box without per-platform plumbing. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Smoke tests and dry runs of the pipeline itself need to build microsoft/git from an untagged tip, where resolve-version.sh's tag walk would either fail or pick up an unrelated tag. Add a queue-time `versionOverride` parameter; when it is set to anything other than the empty string or the sentinel `-`, the prereqs stage emits the override verbatim as the build version, labels the tag name as `untagged`, and bypasses resolve-version.sh entirely. A build from an untagged commit must not race a real release upload, so a non-empty override also forces the GitHub publishing job off regardless of the `github` parameter, and the prereqs step logs a warning to make that consequence visible in the run summary. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Add the per-platform Linux job that takes microsoft/git from
source to a signed `microsoft-git_<version>_<arch>.deb` staged
under `$(Build.ArtifactStagingDirectory)/_final/`. The flow
ports `create-linux-unsigned-artifacts` and
`create-linux-signed-artifacts` from the GitHub workflow at
.github/workflows/build-git-installers.yml, keeping the Make
recipe and DEBIAN/control body byte-for-byte identical so a
diff against the workflow's output is empty modulo the
deliberate departures called out below.
The GitHub workflow runs everything inside an ubuntu:20.04 /
ubuntu:22.04 container, both to pin the resulting .deb's glibc
ABI floor and to give apt-get a root-owned filesystem. The 1ES
pool images we run on
(GitClientPME-1ESHostedPool-{intel,arm64}-pc) silently ignore a
job-level `container:` directive, so the build executes on the
bare Ubuntu host VM as the unprivileged agent user. apt-get
therefore runs via `sudo`, and the job logs the Ubuntu version,
kernel, and effective UID up front so an audit can read the
.deb's effective glibc floor back from the build output.
Re-introducing a real container later (whether via 1ES's
container option, a custom container job, or docker invoked
from a step) is a separate question. The workflow's
`DEBIAN_FRONTEND=noninteractive` and `TZ=Etc/UTC` env vars
exist only to keep `tzdata` quiet inside a fresh container; the
bare 1ES image already has tzdata configured, so they are
dropped. The Node.js workaround in the workflow similarly
exists only to satisfy GitHub Actions' Node-based shim and is
not needed under Azure Pipelines.
A few intentional content changes: parallelism switches from
the workflow's hard-coded `-j5` (a runner-specific holdover) to
`-j$(nproc)`, which adapts to whatever the 1ES pool gives us;
the shell prologue changes from `set -ex` to `set -euo
pipefail` so an unbound variable or a failing stage in a pipe
aborts the job rather than silently producing a broken .deb;
`$(git_version)` now comes from the prereqs stage, dropping the
workflow's runtime dpkg-architecture round-trip in favour of
the matrix's explicit `amd64` / `arm64` entries via
`$(deb_arch)`. The `s/-rc/.rc/g` substitution carries over
because Git's GIT-VERSION-GEN spells release-candidate tags
with a dot.
The build drops its output under
`$(Build.ArtifactStagingDirectory)/app/` so the existing ESRP
signing template's `**/*.deb` pattern picks it up. A focused
move of just the signed
`microsoft-git_<version>_<arch>.deb` into
`$(Build.ArtifactStagingDirectory)/_final/` then feeds the
existing `templateContext.outputs.pipelineArtifact` for the
`linux_x64` / `linux_arm64` artifact. Naming the file
precisely turns "ESRP signed something else" into a
missing-file error rather than a silent wrong-artifact upload.
Assisted-by: Claude Opus 4.7
Co-authored-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Add the per-platform macOS job that takes microsoft/git from source to a signed-and-notarized `git-<version>-universal.pkg` plus the corresponding `.dmg`, both staged under `$(Build.ArtifactStagingDirectory)/_final/`. The flow ports `create-macos-artifacts` and `create-macos-signed-artifacts` from the GitHub workflow at .github/workflows/build-git-installers.yml and leans on .github/macos-installer/Makefile for the heavy lifting, but swaps the workflow's productsign + xcrun notarytool path for ESRP signing and ESRP MacAppNotarize. The native Homebrew on the macOS-15-arm64 pool image is arm64 and lives at /opt/homebrew. Producing a universal binary additionally requires the x86_64 build of gettext/libintl, so a separate x86_64 Homebrew gets installed under /usr/local via the upstream installer running under Rosetta and pulls gettext from there as well. The two arch-specific libintl.a copies are then combined with lipo into a universal archive at the workspace root, which the upcoming config.mak's `LDFLAGS = -L"$(pwd)"` resolves. libintl depends on iconv, but the system /usr/lib/libiconv.dylib is already universal and exports the `_iconv*` symbols Homebrew's gettext was built against; Homebrew's own libiconv exports `_libiconv*` and would not link, hence the explicit `USE_HOMEBREW_LIBICONV` / `ICONVDIR` overrides in config.mak. Spotlight indexing on the boot volume is disabled (`mdutil -i off /`) at the start of the job because leaving it on caused intermittent file-locking failures in subsequent steps. config.mak collects the Make flags that turn on the dual-arch compile and route around several macOS quirks: HOST_CPU=universal, dual-arch CFLAGS (the actual universal-binary driver), -DNO_OPENSSL for contrib Makefiles that do not see the main Makefile's NO_OPENSSL handling, the USE_HOMEBREW_LIBICONV / ICONVDIR overrides, gettext include dirs from both Homebrew prefixes, and CURL_LDFLAGS / CURL_CONFIG pinned against the OS-supplied libcurl rather than a Homebrew copy. SKIP_DASHED_BUILT_INS disables the dashed built-ins because on macOS the hard-link optimisation does not kick in for the staging tree and the resulting full copies would bloat the eventual .dmg. `make GIT-VERSION-FILE dist dist-doc` runs in the source tree; `git get-tar-commit-id` recovers the original commit OID from the resulting source tarball (this becomes GIT_BUILT_FROM_COMMIT, which the macos-installer Makefile bakes into `git version --build-options`); the source and manpage tarballs extract into payload/ and manpages/; a copy of config.mak is dropped inside the extracted source so the universal-build flags apply during the real compile; finally `make -C .github/macos-installer payload` produces the universal binary tree. `git get-tar-commit-id` reads only the leading pax header and then closes its stdin, which makes `gunzip -c` exit 141 (SIGPIPE) under the outer `set -o pipefail`; the pipeline is wrapped in a `set +o pipefail` subshell so the SIGPIPE does not abort the build. The macos-installer Makefile produces the install tree at stage/git-universal-<ver>/ but its `pkg` target packages from build-artifacts/, so the tree is copied across after `make payload` completes, mirroring the GitHub workflow. GITHUB_WORKSPACE=$(Build.SourcesDirectory) is exported because the Makefile derives BUILD_DIR from $(GITHUB_WORKSPACE), which is unset under Azure Pipelines. XML_CATALOG_FILES points at the catalogs from the Homebrew docbook installed in the dependencies step. (FUTURE: the duplication exists only because .github/macos-installer/Makefile hardcodes both DESTDIR=stage/... and ARTIFACTDIR=build-artifacts; overriding ARTIFACTDIR on the `make pkg` line below to point at stage/ would let us drop the cp entirely. Worth cleaning up alongside moving the macOS installer Makefiles out of .github/, where they live for historical reasons rather than because they are GitHub-specific.) Signing happens against the install tree at .github/macos-installer/build-artifacts/usr/local/git/, not the source tree under payload/git-<version>/, because the macos-installer Makefile's `pkg` target packages from build-artifacts/; signing the source tree would have no effect on the resulting .pkg. Following the pattern in git-credential-manager/.azure-pipelines/release.yml, the install tree is pre-filtered to just the Mach-O files (using `file --mime` matching `mach`, the same heuristic .github/scripts/codesign.sh uses), copied into a staging directory under $(Build.ArtifactStagingDirectory)/macos-tosign/ preserving relative paths, handed to the existing .azure-pipelines/esrp/sign.yml template (which zips, signs via EsrpCodeSigning@6 with KeyCode CP-401337-Apple + OperationCode MacAppDeveloperSign + Hardening enabled, and extracts back into the staging dir), and finally copied back into the install tree. The pre-filter is necessary because the existing template's CopyFiles@2 step uses minimatch globs and the only reliable way to pick out Mach-O files is by file content; signing the entire install tree would either fail on non-binary files or sign things that should not be signed (shell scripts, perl, manpages, templates, the uninstall.sh). UseDotNet@2 (8.x) installs the .NET SDK that EsrpCodeSigning@6 depends on, since the macOS-15-arm64 pool image does not provide it. The macos-installer Makefile's `pkg` target then produces an unsigned .github/macos-installer/disk-image/git-<version>-universal.pkg from the signed payload tree. APPLE_INSTALLER_IDENTITY is deliberately left undefined so pkgbuild does not try to sign; the next step submits the .pkg back through ESRP for signing (KeyCode CP-401337-Apple covers both Developer ID Application and Developer ID Installer certs in this account, so MacAppDeveloperSign on a .pkg is the productsign equivalent), and then through ESRP for Apple notarization (MacAppNotarize, BundleId com.git.pkg, matching the identifier pkgbuild bakes in via `--identifier com.git.pkg` from the Makefile). The ESRP MacAppNotarize operation handles both submission and ticket stapling, returning the notarized .pkg back into disk-image/ via the same zip-extract template path the previous sign step used; this is what replaces the `xcrun notarytool submit ... --wait` plus `xcrun stapler staple` flow from .github/scripts/notarize.sh. Finally the Makefile's `image` target builds .github/macos-installer/git-<version>-universal.dmg from the contents of disk-image/. The .dmg lands at the macos-installer root, while the signed-and-notarized .pkg lives somewhere under disk-image/: ESRP's MacAppNotarize op repacks its output zip to wrap the notarized .pkg in a UUID-named .zip.unzipped/ subdirectory, so depending on whether notarization ran, the .pkg ends up either directly under disk-image/ or at disk-image/<uuid>.zip.unzipped/git-...pkg. `find` locates it and moves it (along with the globbed .dmg) into $(Build.ArtifactStagingDirectory)/_final/, which the job's templateContext.outputs already publishes as the `macos_universal` pipeline artifact. `set -euo pipefail` means an empty `find` result, or a missing .dmg, fails the mv loudly rather than producing a silent half-empty upload, matching the same defensive choice the Linux stage step makes. Assisted-by: Claude Opus 4.7 Co-authored-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Add the per-platform Windows job that takes microsoft/git from source to ESRP-signed `Git-<version>-<arch>.exe` and `PortableGit-<version>-<arch>.exe`, plus the matching sha-256.txt sidecar, all staged under $(Build.ArtifactStagingDirectory)/_final/. The flow ports `create-windows-artifacts` and `create-windows-signed-artifacts` from the GitHub workflow at .github/workflows/build-git-installers.yml, leaning on git-for-windows/build-extra (please.sh + installer/release.sh) for the build itself. GitHub Actions has the git-for-windows/setup-git-for-windows-sdk@v1 action that drops a full SDK onto the runner; Azure Pipelines has no equivalent task, so the Windows job has to bootstrap the SDK by hand before any of the bash-driven build steps can run. Bootstrap is driven by .azure-pipelines/scripts/windows/setup-git-sdk.sh, which a Bash@3 task invokes via `filePath:` so it runs under the agent's MinGit-provided bash. A `sdk_repo` field on each windows_matrix entry (git-for-windows/git-sdk-64 for x64, git-sdk-arm64 for ARM64) lets the script pick the right upstream; the script does a partial+bare clone of the SDK, clones git-for-windows/build-extra into a sibling directory, then runs `please.sh create-sdk-artifact --sdk=<bare> --out=<sdk> build-installers` to materialise the build-installers flavour of the SDK at the requested output path. Routing through `please.sh create-sdk-artifact` keeps the bytes flowing via plain GitHub HTTPS clones (which 1ES allows) rather than the raw and release-asset CDNs that an earlier download-the-snapshot approach hit. Once the SDK is in place, its own usr/bin (which ships cygpath) and the matching MinGW toolchain bin/ are exposed to subsequent tasks via ##vso[task.prependpath]. The arm64 Windows hosted agents do not have Azure CLI pre-installed, which the AzureCLI@2 task in the ESRP setup step needs. Install the x64 MSI (which runs under x86-64 emulation on arm64 Windows) and prepend it to PATH, gated on a poolArch condition. This is a workaround until the bug preventing us from baking Azure CLI into the hosted pool image is fixed, at which point this step can be dropped. A small helper, .azure-pipelines/scripts/windows/utils.sh, provides `to_windows_path` / `to_unix_path` for scripts running on Windows agents. Both prefer `cygpath` when it is on PATH and fall back to a small pure-shell parser otherwise. The fallback matters because some bash steps run before the SDK is bootstrapped and only have MinGit's bash available, which does not ship cygpath. Both setup-git-sdk.sh and the ESRP-sign script source utils.sh rather than duplicating the conversion logic. Git for Windows' build tooling (please.sh, signtool.sh, the installer .iss templates, and the MINGW-packages helpers) lives in git-for-windows/build-extra rather than in the SDK snapshot. The GitHub workflow's Windows job clones it into /usr/src/build-extra of the SDK before invoking please.sh; this job does the same. A partial clone (--filter=blob:none) plus --single-branch -b main is enough for everything please.sh needs and avoids pulling the full blob history, matching the workflow's invocation byte for byte. The mingw-w64-git package is built via please.sh build-mingw-w64-git from a Bash@3 task using the SDK's bash that the bootstrap put on PATH. Outputs land in $(Build.SourcesDirectory)/artifacts/ so the subsequent installer-build step can pass them to please.sh make_installers_from_mingw_w64_git via --pkg= flags. Three small adaptations from the GitHub workflow's source step are worth flagging. First, the /usr/bin/git trampoline that delegates to the matching MinGW-built git.exe is the same one the workflow writes by hand; makepkg-mingw shells out to plain `git`, and the SDK bash's git candidates would otherwise come from MinGit, not the toolchain we are building against. Second, the user.name / user.email / PACKAGER values are hardcoded to a build-bot identity since Azure Pipelines has no GitHub-actor equivalent. Third, please.sh's --only-<arch> flag takes the bare CPU name (x86_64 or aarch64), not the toolchain triple, so a `cpu_arch` matrix dimension surfaces the right value next to each toolchain entry. The build task detaches stdin via `exec </dev/null` before invoking makepkg-mingw, because pacman's git-extra post_install hook runs `for s in $(grep -l PAT $(find /mingw*/bin/ ...))` and falls back to reading stdin when /mingw*/bin/ is absent and find produces empty output. Bash@3 leaves the task's stdin pipe open with no writer (the GitHub Actions runner closes it for the same reason; see actions/runner ProcessInvoker.cs), so without the detach the build would hang indefinitely waiting for input. Two pieces of the workflow's pkg-build step are intentionally not ported and are noted in a comment for follow-up: the per-tarball GPG signing (replaceable with an ESRP PGP operation analogous to the Linux LinuxSign flow if needed downstream), and the MINGW-packages bundle creation, which depends on a PKGBUILD.<tag_name> snapshot that microsoft/git does not currently ship. A single bash task drives please.sh make_installers_from_mingw_w64_git for both installer and portable variants. The GitHub workflow runs these as separate matrix jobs (one per type/arch combination); keeping both builds in the same Azure Pipelines job means the .pkg.tar.* artifacts produced by the previous step are available without an inter-job artifact passing trip. The PDB archive copy into build-extra/cached-source-packages is the same prerequisite that --include-pdbs needs in the GitHub workflow. The --pkg= filter that strips signatures and the optional archimport / cvs / p4 / gitweb / doc-man pieces matches the workflow's sed exactly so the resulting .exe sizes are comparable. The same `exec </dev/null` stdin detach as the build mingw-w64-git step applies. To make a silent hang inside the nested pacman / makepkg / Inno Setup chain debuggable, BASH_ENV is pointed at a tiny `set -x` fragment so every nested non-interactive bash subshell auto-enables xtrace, and please.sh itself is invoked via `sh -x`. The trace volume is the cost we pay for being able to identify which step a future hang is stuck in; cheap compared to debugging a silent hang. The GitHub Actions workflow applies five `sed` transformations during the Windows build to turn upstream Git for Windows into the microsoft/git distribution. They are spread over five run: blocks and largely opaque without following each sed pattern by hand. Capture them as patches under .azure-pipelines/patches/ instead, grouped by the upstream tree they mutate (build-extra/* for installer customisation, git-sdk/* for the SDK's git-update-git-for-windows helper); the patches themselves carry the explanatory commit-style headers each transformation deserves. A small helper, .azure-pipelines/scripts/apply-patches.sh, applies every *.patch in a directory in lexicographic order via `patch -p1`. patch(1) is used rather than `git apply` because the latter is strict about context whitespace; CRLF/LF mismatches between the patch context (as authored) and the working tree (which may be CRLF on Windows checkouts) trip it up. patch is more forgiving by default, and matches the convention used by msys2/MINGW-packages PKGBUILDs and git-for-windows/build-extra's get-sources.sh. The patches apply against /usr/src/build-extra and against the SDK's /$(mingwprefix), between cloning build-extra and building the mingw-w64-git package, mirroring where the GitHub workflow's sed steps slot in. ESRP signing is wired through build-extra's `signtool` alias hook so that ESRP signs every binary that ends up inside the Windows installer and portable Git, not just the outer .exe wrapper. A naive post-build sign of just `Git-*.exe` and `PortableGit-*.exe` would leave every binary embedded inside the installer (DLLs, helper exes, the mingw-w64-git pkg payload) shipping unsigned. The mechanism, set up by build-extra: please.sh's build_mingw_w64_git checks `git config alias.signtool` and, if set, exports `SIGNTOOL="git ... signtool"` into makepkg-mingw so the PKGBUILD can sign individual binaries during the package build; build-extra's installer/release.sh checks the same alias and passes `//Ssigntool="git signtool $f" //DSIGNTOOL` to Inno Setup's ISCC.exe, which then signs every embedded file via the SignTool=signtool directive in install.iss. The portable Git .exe is a 7z self-extractor that bypasses the Inno Setup signtool path, so it is signed explicitly after `make_installers` returns. The ESRP setup template (which sets ESRP_TOOL and ESRP_AUTH) runs before the build steps; a Bash@3 task registers the signtool alias to invoke .azure-pipelines/esrp/windows/esrpsign.sh; the ESRP env vars are added to both build tasks so esrpsign.sh has ESRP_TOOL, ESRP_AUTH, and SYSTEM_ACCESSTOKEN available when invoked via the alias from inside please.sh, makepkg-mingw, or Inno Setup. MSYSTEM is exported in the build installer task's env block because installer/release.sh requires it to select the architecture branch, and Bash@3 does not source /etc/profile. esrpsign.sh's calling convention (`<file> [file ...]`, sign in place) already matches signtool.sh's, so no further script changes are needed. Stage `Git-*.exe` and `PortableGit-*.exe` alongside a SHA-256 sidecar into $(Build.ArtifactStagingDirectory)/_final/, which the job's templateContext.outputs.pipelineArtifact already publishes as the `windows_x64` / `windows_arm64` artifact. The SHA-256 sidecar is computed here, post-build, rather than in the build step because ESRP signing rewrites the .exe contents; a SHA-256 computed before signing would mismatch the bytes that ship. Assisted-by: Claude Opus 4.7 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
With build, signing, notarization, validation, and draft-release publishing all in place, the Azure Pipeline is ready to take over from the GitHub Actions build-git-installers workflow. Switch `trigger: none` to a tag-only trigger matching the same `v[0-9]*vfs*` pattern the GitHub workflow used (and that resolve-version.sh validates against), and explicitly exclude all branches so the pipeline does not fire on every topic-branch push. Flip the `esrp` and `github` parameter defaults from false to true. The GitHub release job still uses `isDraft: true`, so a maintainer inspects and publishes the release manually; manual runs in the Azure DevOps UI can still uncheck either box for a dry run. Assisted-by: Claude Opus 4.7 Co-authored-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
release: create initial Windows installer build workflow Add a manual-only GitHub Actions workflow for building the Windows installer (x86_64 plus portable Git), driven via `workflow_dispatch:`. The production release path for the official microsoft/git installers lives in .azure-pipelines/release.yml; this workflow is kept around as a fallback so the Windows installer can still be produced on demand for debugging or comparison. The build steps are pinned to `windows-2019` (rather than `windows-latest`) to ensure the correct Visual Studio version is used (verified in the pipeline via `type -p mspdb140.dll`), and the SDK used is the `full` flavor rather than `build-installers` due to a known (but not-yet-fixed) issue downloading the `build-installers` flavor with the `git-for-windows/setup-git-for-windows-sdk` Action. There is no code-signing certificate available to this workflow, so the artifacts it produces are unsigned and must not be published as releases; they are useful only for build-time debugging. Signed-off-by: Victoria Dye <vdye@github.com> Assisted-by: Claude Opus 4.7 Co-authored-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Port #913 and #914 from the
vfs-2.53.0to thevfs-2.54.0branch. While at it, reorganize the changes into a more concise commit history, including twoamend!commits that target the next rebase to a new Git version.At time of writing,
git diff azp vfs-2.53.0 -- .azure-pipelinesshows an empty diff, proving that it bringsvfs-2.54.0up to date with the latest changes that made v2.53.0.vfs.0.11 possible.