build: deterministic builds use Python 3.10
What changed, and why it matters
This commit updates the project's build system to use Python 3.10 instead of Python 3.9 for creating release binaries and packages. It also changes the source of the Windows Python installer from official python.org MSI files to a NuGet package, because python.org no longer provides MSI installers for newer Python 3.10 versions. The changes are routine build-maintenance and do not appear to fix or introduce a security vulnerability on their own. The main thing to watch is that the new NuGet package is correctly verified by its SHA-256 hash, which the commit does include.
Verify that the new NuGet package hash and URL are trustworthy and that the build environment still produces bit-for-bit reproducible outputs. Review Python 3.10 release notes for any behavior changes that could affect HWI's runtime or build reproducibility. No immediate security patch or incident response is indicated by this commit alone.
Security signals we found
Build toolchain version bump (Python 3.9 -> 3.10)
Windows Python installer source changed from python.org MSI with GPG verification to NuGet package with SHA-256 verification
Removal of GPG key import and MSI signature verification in build_wine.sh
Deterministic build timestamps updated to 2026-01-01
No application or cryptographic code changes
Evidence from the diff
The diff upgrades deterministic build scripts and dependency metadata from Python 3.9.19/3.9.13 to Python 3.10.20/3.10.11. Files touched include Docker build environment, Linux/macOS binary build script, source distribution build script, Windows Wine build script, release documentation, poetry.lock, pyproject.toml, and setup.py. For Windows, the previous GPG-verified python.org MSI installation is replaced by a SHA-256-verified NuGet package download. Timestamps used for reproducible builds are moved from 2019-01-01 to 2026-01-01. PySide2 constraints are widened from python < 3.10 to python < 3.11. No runtime application code is modified.
Changed components
contrib/build.Dockerfilecontrib/build_bin.shcontrib/build_dist.shcontrib/build_wine.shdocs/development/release-process.rstpoetry.lockpyproject.tomlsetup.pyInspect captured patch +39 / −34
diff --git a/contrib/build.Dockerfile b/contrib/build.Dockerfile
index 766d5b0..d1d2386 100644
--- a/contrib/build.Dockerfile
+++ b/contrib/build.Dockerfile
@@ -34,9 +34,9 @@ ENV PATH="$PYENV_ROOT/bin:$PATH"
COPY contrib/reproducible-python.diff /opt/reproducible-python.diff
ENV PYTHON_CONFIGURE_OPTS="--enable-shared"
-ENV BUILD_DATE="Jan 1 2019"
+ENV BUILD_DATE="Jan 1 2026"
ENV BUILD_TIME="00:00:00"
-RUN /bin/bash -c 'eval "$(pyenv init --path)" && eval "$(pyenv virtualenv-init -)" && cat /opt/reproducible-python.diff | pyenv install -kp 3.9.19'
+RUN /bin/bash -c 'eval "$(pyenv init --path)" && eval "$(pyenv virtualenv-init -)" && cat /opt/reproducible-python.diff | pyenv install -kp 3.10.20'
ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8
diff --git a/contrib/build_bin.sh b/contrib/build_bin.sh
index 2db94f4..48f9a6d 100755
--- a/contrib/build_bin.sh
+++ b/contrib/build_bin.sh
@@ -6,8 +6,11 @@ set -ex
ARCH=$(uname -m | tr '[:upper:]' '[:lower:]')
+PYTHON_VERSION=3.10.20
+
eval "$(pyenv init --path)"
eval "$(pyenv virtualenv-init -)"
+export PYENV_VERSION="$PYTHON_VERSION"
pip install -U pip
pip install poetry
@@ -21,8 +24,8 @@ else
fi
# We also need to change the timestamps of all of the base library files
-lib_dir=$(pyenv prefix)/lib/python3.9
-TZ=UTC find ${lib_dir} -name '*.py' -type f -execdir touch -t "201901010000.00" '{}' \;
+lib_dir=$(pyenv prefix)/lib/python3.10
+TZ=UTC find ${lib_dir} -name '*.py' -type f -execdir touch -t "202601010000.00" '{}' \;
# Make the standalone binary
export PYTHONHASHSEED=42
diff --git a/contrib/build_dist.sh b/contrib/build_dist.sh
index 8b0b50c..104d37f 100755
--- a/contrib/build_dist.sh
+++ b/contrib/build_dist.sh
@@ -4,8 +4,11 @@
set -ex
+PYTHON_VERSION=3.10.20
+
eval "$(pyenv init --path)"
eval "$(pyenv virtualenv-init -)"
+export PYENV_VERSION="$PYTHON_VERSION"
pip install -U pip
pip install poetry
@@ -21,5 +24,5 @@ fi
# Make the distribution archives for pypi
poetry build -f wheel
-# faketime is needed to make sdist detereministic
-TZ=UTC faketime -f "2019-01-01 00:00:00" poetry build -f sdist
+# faketime is needed to make sdist deterministic
+TZ=UTC faketime -f "2026-01-01 00:00:00" poetry build -f sdist
diff --git a/contrib/build_wine.sh b/contrib/build_wine.sh
index 64d211f..30efca2 100755
--- a/contrib/build_wine.sh
+++ b/contrib/build_wine.sh
@@ -3,7 +3,11 @@
set -ex
-PYTHON_VERSION=3.9.13
+# Note: Python MSIs/EXEs are no longer hosted for 3.10.x on python.org.
+# The NuGet python package currently only goes up to 3.10.11, so Windows builds use that.
+PYTHON_VERSION=3.10.11
+PYTHON_NUGET_URL="https://api.nuget.org/v3-flatcontainer/python/${PYTHON_VERSION}/python.${PYTHON_VERSION}.nupkg"
+PYTHON_NUGET_HASH="7c6f99b160a36a7e09492dfcff2b0a3a60bb5229ca44cdcc3ecb32871a6144d0"
PYTHON_FOLDER="python3"
PYHOME="c:/$PYTHON_FOLDER"
@@ -19,20 +23,15 @@ WINDOWS_SDK_VERSION=10.0.17763.0
wine 'wineboot'
-# Install Python
-# Get the PGP keys
-wget -O pubkeys.txt -N -c "https://keybase.io/stevedower/pgp_keys.asc?fingerprint=7ed10b6531d7c8e1bc296021fc624643487034e5"
-gpg --import pubkeys.txt
-rm pubkeys.txt
-
-# Install python components
-for msifile in core dev exe lib pip tools; do
- wget -N -c "https://www.python.org/ftp/python/$PYTHON_VERSION/amd64/${msifile}.msi"
- wget -N -c "https://www.python.org/ftp/python/$PYTHON_VERSION/amd64/${msifile}.msi.asc"
- gpg --verify "${msifile}.msi.asc" "${msifile}.msi"
- wine msiexec /i "${msifile}.msi" /qb TARGETDIR=$PYHOME
- rm $msifile.msi*
-done
+# Install Python from NuGet package
+wget -O python.nupkg -N -c "$PYTHON_NUGET_URL"
+echo "$PYTHON_NUGET_HASH python.nupkg" | sha256sum -c
+rm -rf python-nupkg
+7z x python.nupkg -opython-nupkg >/dev/null
+rm -rf ~/.wine/drive_c/python3
+mkdir -p ~/.wine/drive_c/python3
+cp -a python-nupkg/tools/* ~/.wine/drive_c/python3/
+rm -rf python.nupkg python-nupkg
# Get and build libusb
wget -N -c -O libusb.tar.bz2 "$LIBUSB_URL"
@@ -40,7 +39,7 @@ echo "$LIBUSB_HASH libusb.tar.bz2" | sha256sum -c
tar -xf libusb.tar.bz2
pushd "libusb-$LIBUSB_VERSION"
./configure --host=x86_64-w64-mingw32
-faketime -f "2019-01-01 00:00:00" make
+faketime -f "2026-01-01 00:00:00" make
cp libusb/.libs/libusb-1.0.dll ~/.wine/drive_c/python3/
popd
rm -r libusb*
@@ -62,7 +61,7 @@ $PYTHON -m pip install poetry
# We also need to change the timestamps of all of the base library files
lib_dir=~/.wine/drive_c/python3/Lib
-TZ=UTC find ${lib_dir} -name '*.py' -type f -execdir touch -t "201901010000.00" '{}' \;
+TZ=UTC find ${lib_dir} -name '*.py' -type f -execdir touch -t "202601010000.00" '{}' \;
# Install python dependencies
POETRY="wine $PYHOME/Scripts/poetry.exe"
diff --git a/docs/development/release-process.rst b/docs/development/release-process.rst
index fb739ff..16cf860 100644
--- a/docs/development/release-process.rst
+++ b/docs/development/release-process.rst
@@ -4,7 +4,7 @@ Release Process
1. Bump version number in ``pyproject.toml`` and ``hwilib/__init__.py``, generate the setup.py file, and git tag release
2. Build distribution archives for PyPi with ``contrib/build_dist.sh``
3. For MacOS and Linux, use ``contrib/build_bin.sh``. This needs to be run on a macOS machine for the macOS binary and on a Linux machine for the linux one.
-4. For Windows, use ``contrib/build_wine.sh`` to build the Windows binary using wine
+4. For Windows, use ``contrib/build_wine.sh`` to build the Windows binary using wine. Note that this uses Python 3.10.11 via the NuGet package because python.org no longer hosts the MSIs for newer 3.10.x releases.
5. Make ``SHA256SUMS.txt`` using ``contrib/make_shasums.sh``.
6. Make ``SHA256SUMS.txt.asc`` using ``gpg --clearsign SHA256SUMS.txt``
7. Upload distribution archives to PyPi
@@ -26,7 +26,7 @@ Build everything::
docker run -it --name hwi-builder -v $PWD:/opt/hwi --rm --workdir /opt/hwi hwi-builder /bin/bash -c "contrib/build_bin.sh && contrib/build_dist.sh"
docker run -it --name hwi-wine-builder -v $PWD:/opt/hwi --rm --workdir /opt/hwi hwi-wine-builder /bin/bash -c "contrib/build_wine.sh"
- docker run --platform linux/arm64 -it --rm --name hwi-builder-arm64 -v $PWD:/opt/hwi --workdir /opt/hwi hwi-builder-arm64 /bin/bash -c "contrib/build_bin.sh --without-gui && contrib/build_dist.sh --without-gui"
+ docker run --platform linux/arm64 -it --rm --name hwi-builder-arm64 -v $PWD:/opt/hwi --workdir /opt/hwi hwi-builder-arm64 /bin/bash -c "contrib/build_bin.sh --without-gui && contrib/build_dist.sh --without-gui"
Building macOS binary
=====================
@@ -35,14 +35,14 @@ Note that the macOS build is non-deterministic.
First install `pyenv <https://github.com/pyenv/pyenv>`_ using whichever method you prefer.
-Then a deterministic build of Python 3.9.19 needs to be installed. This can be done with the patch in ``contrib/reproducible-python.diff``. First ``cd`` into HWI's source tree. Then use::
+Then a deterministic build of Python 3.10.20 needs to be installed. This can be done with the patch in ``contrib/reproducible-python.diff``. First ``cd`` into HWI's source tree. Then use::
- cat contrib/reproducible-python.diff | PYTHON_CONFIGURE_OPTS="--enable-framework" BUILD_DATE="Jan 1 2019" BUILD_TIME="00:00:00" pyenv install -kp 3.9.19
+ cat contrib/reproducible-python.diff | PYTHON_CONFIGURE_OPTS="--enable-framework" BUILD_DATE="Jan 1 2026" BUILD_TIME="00:00:00" pyenv install -kp 3.10.20
-Make sure that python 3.9.19 is active::
+Make sure that python 3.10.20 is active::
$ python --version
- Python 3.9.19
+ Python 3.10.20
Now install `Poetry <https://github.com/sdispater/poetry>`_ with ``pip install poetry``
diff --git a/poetry.lock b/poetry.lock
index f7f47dc..d0f4916 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -933,7 +933,7 @@ description = "Python bindings for the Qt cross-platform application and UI fram
optional = true
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <3.11"
groups = ["main"]
-markers = "python_version == \"3.9\" and extra == \"qt\""
+markers = "python_version < \"3.11\" and extra == \"qt\""
files = [
{file = "PySide2-5.15.2.1-5.15.2-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:b5e1d92f26b0bbaefff67727ccbb2e1b577f2c0164b349b3d6e80febb4c5bde2"},
{file = "PySide2-5.15.2.1-5.15.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:235240b6ec8206d9fdf0232472c6ef3241783d480425e5b54796f06e39ed23da"},
@@ -1017,7 +1017,7 @@ description = "Python / C++ bindings helper module"
optional = true
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <3.11"
groups = ["main"]
-markers = "python_version == \"3.9\" and extra == \"qt\""
+markers = "python_version < \"3.11\" and extra == \"qt\""
files = [
{file = "shiboken2-5.15.2.1-5.15.2-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:f890f5611ab8f48b88cfecb716da2ac55aef99e2923198cefcf781842888ea65"},
{file = "shiboken2-5.15.2.1-5.15.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:87079c07587859a525b9800d60b1be971338ce9b371d6ead81f15ee5a46d448b"},
@@ -1311,4 +1311,4 @@ qt = ["pyside2"]
[metadata]
lock-version = "2.1"
python-versions = "^3.9,<3.13"
-content-hash = "cfdbc60bf068f88587fe801325c19dfada1e868dbb0cdd65c5b7a7353dc0ca6a"
+content-hash = "f1695383c9bf2a946924a19c7aaeda082e6160a43932173e989129ad6794be95"
diff --git a/pyproject.toml b/pyproject.toml
index 3137310..eaf34ca 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -23,7 +23,7 @@ pyaes = "^1.6"
mnemonic = "~0"
typing-extensions = "^4.4"
libusb1 = ">=1.7,<4"
-pyside2 = { version = "^5.14.0", optional = true, python = "<3.10" }
+pyside2 = { version = "^5.15.2.1", optional = true, python = "<3.11" }
cbor2 = ">=5.4.6,<5.8"
pyserial = "^3.5"
semver = "^3.0.1"
diff --git a/setup.py b/setup.py
index d6255f6..79c1cd5 100644
--- a/setup.py
+++ b/setup.py
@@ -39,7 +39,7 @@ install_requires = \
'typing-extensions>=4.4,<5.0']
extras_require = \
-{'qt:python_version < "3.10"': ['pyside2>=5.14.0,<6.0.0']}
+{'qt:python_version < "3.11"': ['pyside2>=5.15.2.1,<6.0.0.0']}
entry_points = \
{'console_scripts': ['hwi = hwilib._cli:main', 'hwi-qt = hwilib._gui:main']}
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.