diff --git a/contrib/packaging/build-windows-wheels.bat b/contrib/packaging/build-windows-wheels.bat
new file mode 100644
--- /dev/null
+++ b/contrib/packaging/build-windows-wheels.bat
@@ -0,0 +1,37 @@
+@echo off
+
+REM - This is a convenience script to build all of the wheels outside of the CI
+REM - system.  It requires the cibuildwheel package to be installed, and the
+REM - executable on PATH, as well as `msgfmt.exe` from gettext and the x86,
+REM - amd64, and arm64 compilers from VS BuildTools.  These can be obtained by
+REM - running `contrib/install-windows-dependencies.ps1`.
+
+REM - None of the variable set here live past this script exiting.
+setlocal
+
+REM - Build translations; requires msgfmt.exe on PATH.
+set MERCURIAL_SETUP_FORCE_TRANSLATIONS=1
+
+REM - Prevent building pypy wheels, which is broken.
+set CIBW_SKIP=pp*
+
+REM - Disable warning about not being able to test without an arm64 runner.
+set CIBW_TEST_SKIP=*-win_arm64
+
+
+REM - arm64 support starts with py39, but the first arm64 installer wasn't
+REM - available until py311, so skip arm64 on the older, EOL versions.
+set CIBW_ARCHS=x86 AMD64
+set CIBW_BUILD=cp38-* cp39-* cp310-*
+
+cibuildwheel --output-dir dist/wheels
+
+if %errorlevel% neq 0 exit /b %errorlevel%
+
+
+set CIBW_ARCHS=x86 AMD64 ARM64
+set CIBW_BUILD=cp311-* cp312-* cp313-*
+
+cibuildwheel --output-dir dist/wheels
+
+if %errorlevel% neq 0 exit /b %errorlevel%
diff --git a/tests/test-check-code.t b/tests/test-check-code.t
--- a/tests/test-check-code.t
+++ b/tests/test-check-code.t
@@ -93,5 +93,6 @@ Keep python3 tests sorted:
 Keep Windows line endings in check
 
   $ testrepohg files 'set:eol(dos)'
+  contrib/packaging/build-windows-wheels.bat
   contrib/win32/hg.bat
   contrib/win32/mercurial.ini