The standalone <!-- {Locked="..."} --> XML comments above each <string>
are silently ignored by the POMXML parser used by TouchdownBuildTask, so
locked tokens were getting translated in every locale (NetworkingMode
values "Mirrored"/"None"/"NAT", ".wslconfig" key names, etc.).
Move the directives inside each <string> using the inline form the parser
actually honors:
<string id="X"><!-- _locComment='{Locked="Foo"}' -->...Foo...</string>
<string id="NetworkingModeMirrored"><!-- _locComment="{Locked}" -->Mirrored</string>
Update validate-localization.py to extract tokens from the inline form so
the baseline-token-presence check still catches authoring mistakes.
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Touchdown `.adml` parser does not honor `<!-- {Locked=...} -->` XML
comments the way the `.resw` parser honors `<comment>{Locked=...}</comment>`,
so every nightly localization PR comes back with locked tokens translated
(`Mirrored` -> `镜像`, `None` -> `Нет`, etc.) and fails CI on the
per-locale token check we added in #40515.
Keep the baseline en-US locked-token check and all ID/presentation parity
checks as errors. Downgrade the per-locale locked-token check to a warning
so loc PRs can land while we figure out the right LocVer placement for
.adml with the Touchdown team.
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add the WSL ADMX policy strings to the nightly Touchdown localization
pipeline so translated .adml files are produced for every locale already
covered by Resources.resw. Translated files land at
intune/<locale>/WSL.adml, matching the Group Policy on-disk layout (the
language-neutral WSL.admx stays at intune/WSL.admx).
Changes:
* intune/en-US/WSL.adml: new baseline ADML file. Each translatable
string is annotated with {Locked="..."} XML comments so translators
preserve product names (Windows Subsystem for Linux, WSL, WSL1, WSL2,
Store WSL), .wslconfig keys (wsl2.kernel, wsl2.systemDistro,
wsl2.kernelCommandLine, wsl2.nestedVirtualization,
wsl2.kernelDebugPort, wsl2.networkingmode, wsl2.firewall), command
names (wsl.exe, debug-shell, mount), networking-mode enum values
(None, NAT, Mirrored, VirtioProxy), and the policy state literal
Disabled.
* .pipelines/wsl-build-nightly-localization.yml: add intune/en-US/WSL.adml
to the trigger paths and to the TouchdownBuildTask resourceFilePath
block. The combined ';O:intune\' mapping puts translated files at
intune/<locale>/WSL.adml.
* tools/devops/validate-localization.py: add validate_adml() which
enforces the same locked-token invariant as .resw (every {Locked="X"}
token must appear verbatim in its target string value), validates
that translated locales have the same string ids as the en-US
baseline, and is parameterized by the ADML folder name. The argv
shortcut used in CI to avoid a pip install dependency on click now
accepts both 3 args (back-compat) and 4 args (with explicit ADML
folder).
* .pipelines/build-stage.yml: pass 'intune' explicitly to
validate-localization.py so the validation invocation reads
'localization/strings en-US intune' -- both folders visible at the
call site, consistent with the rest of the script's parameterization.
* tools/devops/create-change.py: include untracked files in the
'Changed files:' report and stage them with 'git add -A' (via
repo.git.add(A=True)) before commit. Without this, Touchdown's
first-time ADML outputs land as untracked files in newly created
intune/<locale>/ directories and are silently dropped by 'git commit
-a', which only stages modifications and deletions of already-tracked
files. Confirmed against ADO build 146781281: Touchdown returned
WSL.adml translations for 20 locales and saved them on disk, but the
resulting auto-PR (#40511) contained only the resw modifications.
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes Dependabot alerts #22 and #23. GitPython <= 3.1.49 has a newline injection vulnerability in config_writer() section parameter that bypasses the CVE-2026-42215 patch and enables RCE via core.hooksPath.
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Verify WinGet config exists before invoking winget configure
If the resolved `` does not exist, `winget configure` fails
with a cryptic error rather than telling the user that the config file
is missing. Add an explicit `Test-Path` guard that reports the missing
file path and the expected location, and update the existing failure
message to print the full `` instead of just ``
so it matches the path actually passed to `winget configure`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Retrigger CI
---------
Co-authored-by: benhillis <17727402+benhillis@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`te.exe` is invoked as a native command, so its failure should be
detected via `0` rather than `!True` (which can be
unreliable for native processes under `Set-StrictMode`). Also
propagate the actual exit code instead of substituting a hardcoded `1`
so callers (CI, wrapper scripts) can distinguish between te.exe failure
modes.
Also fix the `-Package` parameter doc: the actual default is
`.\installer.msix`, not `.\wsl.msix`.
Co-authored-by: benhillis <17727402+benhillis@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses CVEs in GitPython <= 3.1.48:
- Path traversal in reference APIs (patched in 3.1.48)
- Newline injection in config_writer().set_value() enabling RCE via core.hooksPath (patched in 3.1.49)
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add setup-dev-env.ps1 and WinGet configuration for dev prerequisites
Add a one-command developer environment setup that uses WinGet
Configuration (DSC) to install all build prerequisites: Developer Mode,
CMake, Visual Studio 2022, and the required VS workloads/components.
New files:
- tools/setup-dev-env.ps1: Detects any existing VS 2022 edition
(Community/Professional/Enterprise) and runs the matching WinGet
configuration. Defaults to Community when no VS 2022 is found.
- .vsconfig: Declares the required VS components (MSVC, ATL, Clang,
Windows SDK 26100, UWP tools, .NET desktop/WinUI workloads) so
VS Installer can import them directly.
- .config/configuration.winget: WinGet DSC config for VS Community.
- .config/configuration.vsProfessional.winget: Same for Professional.
- .config/configuration.vsEnterprise.winget: Same for Enterprise.
Updated doc/docs/dev-loop.md to document the automated setup and
reference the new .vsconfig and winget configuration files, while
keeping manual install instructions in a collapsed details block.
* doc: add winget configure --enable to manual setup instructions
Address review feedback: document that 'winget configure --enable' is
required before running winget configure manually, since the feature
may not be enabled by default.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wait-Process -PassThru is only available in PowerShell 7+. Since
test.bat invokes powershell.exe (Windows PowerShell 5.1), this caused
a NamedParameterNotFound error when running tests.
Replace with Wait-Process followed by reading ExitCode from the
process object already returned by Start-Process -PassThru.
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cherry-pick WSL1/WSL2 test changes from 9c4dba91 (feature/wsl-for-apps).
Replace runtime WSL1_TEST_ONLY()/WSL2_TEST_ONLY() skip macros with
WSL1_TEST_METHOD()/WSL2_TEST_METHOD() TAEF metadata macros. This moves
version filtering to the test runner level via /select: queries, so
inapplicable tests are excluded entirely instead of appearing as skipped.
Updated files:
- test/windows/Common.h: New macros + removed old skip macros
- test/windows/*.cpp: Converted all test methods
- tools/test/run-tests.ps1: Auto-add /select: when no user filter
- cloudtest/TestGroup.xml.in: Add version filter to TAEF args
- test/README.md: Document new macros
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Replace pre-commit hook with CMake-generated clang-format check
Replace the old pre-commit hook that shelled out to PowerShell and
never blocked commits (-NoFail) with a CMake-generated hook that
calls clang-format directly on staged C/C++ files.
- Add tools/hooks/pre-commit.in as a CMake template
- CMake resolves the clang-format path at configure time via
LLVM_INSTALL_DIR, matching the existing FormatSource.ps1.in pattern
- Hook blocks commits on formatting errors, skips gracefully if
clang-format is not available (cmake not yet run)
- ~5x faster than the old PowerShell approach (~0.5s vs ~2.6s)
* Make pre-commit hook behavior configurable via WSL_PRE_COMMIT_MODE
Add WSL_PRE_COMMIT_MODE CMake cache variable with three modes:
- warn (default): report formatting issues without blocking commit
- error: block commit when formatting issues are found
- fix: auto-format files and re-stage them
Also addresses PR feedback:
- Generate hook into build tree, copy to source tree for out-of-source builds
- Use repo-local tools/clang-format.exe instead of LLVM_INSTALL_DIR path
- Use @ONLY in configure_file to avoid shell variable substitution issues
- Document modes in dev-loop.md and UserConfig.cmake.sample
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add /attachdebugger option to automatically launch WinDbgX for test debugging
When /attachdebugger is passed to test.bat, run-tests.ps1 now:
- Starts te.exe with /waitfordebugger in the background
- Polls for the TE.ProcessHost.exe child process via WMI
- Launches WinDbgX attached directly to the test host PID
- With /inproc, attaches to TE.exe itself instead
This replaces the manual workflow of running /waitfordebugger, reading
the PID from the output, and launching WinDbgX separately.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* run-tests: use /inproc with /attachdebugger, simplify exit
Per review feedback from @OneBlue:
- Add /inproc when /attachdebugger is set so WinDbgX attaches
directly to TE.exe instead of polling for TE.ProcessHost.exe
- Simplify exit to pass through TE.exe exit code directly
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: update /attachdebugger to reflect /inproc behavior
The script now always adds /inproc, so update the README to match:
WinDbgX attaches directly to TE.exe, no ProcessHost polling.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix UnicodeEncodeError in create-release.py on cp1252 consoles
Reconfigure stdout/stderr with errors='backslashreplace' so commit
messages containing characters outside the console code-page (e.g.
U+2225) are escaped instead of crashing the script. Also redirect the
'failed to extract PR number' warning to stderr for consistency.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix return type annotation for get_github_pr_message()
Update the return annotation from str to tuple[str | None, str | None]
to match the actual return values (pr_body, pr_number).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: Add arm64 test distro support
* update unit test baseline
* more test baseline updates
---------
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
* Ship initrd.img in MSI using build-time generation via tar.exe
Replace the install-time CreateInitrd/RemoveInitrd custom actions with a
build-time step that generates initrd.img using the Windows built-in
tar.exe (libarchive/bsdtar) and ships it directly in the MSI.
The install-time approach had a race condition: wsl.exe could launch
before the CreateInitrd custom action completed, causing
ERROR_FILE_NOT_FOUND for initrd.img.
Changes:
- Add CMake custom command to generate initrd.img via tar.exe --format=newc
- Add initrd.img as a regular file in the MSI tools component
- Remove CreateInitrd/RemoveInitrd custom actions from WiX, DllMain,
and wslinstall.def
- Remove CreateCpioInitrd helper and its tests (no longer needed)
- Update pipeline build targets to build initramfs instead of init
* pr feedback
* more pr feedback
* switch to using a powershell script instead of tar.exe
* powershell script feedback
* hopefully final pr feedback
---------
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
* Move from shipping the initrd to generating during package install.
* pr feedback
* working
* adjust custom action conditions
* update initrd test to cover more cases
* Update msipackage/package.wix.in
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* use stack buffer
* move initrd helper to filesystem.cpp and add unit test
---------
Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Configure defender process exceptions when running tests
* Use explicit %ls
* Save state
* Save state
* Add exceptions before the package is installed
* Remove extra call
This patch closes a logic gap in [tools/devops/create-release.py](cci:7://file:///c:/Users/T2430514/Downloads/WSL/tools/devops/create-release.py:0:0-0:0):
* **Problem** – The script’s `--publish` mode was intended to require at least one asset, but Click passes an empty tuple if no assets are provided. The original guard (`if assets is None`) never triggered, allowing empty releases to be drafted.
* **Solution** – Replace the check with `if not assets:` which catches both `None` and empty collections, preventing accidental empty releases.
* **Impact** – Ensures CI and manual runs cannot publish a release without payload, aligning behavior with the CLI contract and avoiding junk tags that require manual cleanup.
Co-authored-by: Odio Marcelino <odiomarcelino@gmail.com>
Many Microsoft employees have contributed to the Windows Subsystem for Linux, this commit is the result of their work since 2016.
The entire history of the Windows Subsystem for Linux can't be shared here, but here's an overview of WSL's history after it moved to it own repository in 2021:
Number of commits on the main branch: 2930
Number of contributors: 31
Head over https://github.com/microsoft/WSL/releases for a more detailed history of the features added to WSL since 2021.