ci: fix issue with psutil
not being available in msys2/mingw32 anymore
#1495
This file contains 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
name: CI | |
# Controls when the action will run. Triggers the workflow on push or pull request | |
# events for develop and v4 branche. | |
on: | |
push: | |
branches: | |
- 'develop' | |
- 'v4' | |
pull_request: | |
branches: | |
- '**' | |
- '!master' | |
- '!pyup/**' | |
env: | |
# Colored pytest output on CI despite not having a tty | |
FORCE_COLOR: 1 | |
# Enable strict unpack mode to catch file duplication problems in onefile builds (at executable run-time). | |
PYINSTALLER_STRICT_UNPACK_MODE: 1 | |
# Enable strict collect mode to catch file duplication problems in PKG/Carchive (onefile builds) or COLLECT | |
# (onedir builds) at build time. | |
PYINSTALLER_STRICT_COLLECT_MODE: 1 | |
# Enable strict handling of codesign errors for macOS bundles. | |
PYINSTALLER_STRICT_BUNDLE_CODESIGN_ERROR: 1 | |
# Enable strict verification of macOS bundles w.r.t. the code-signing requirements. | |
PYINSTALLER_VERIFY_BUNDLE_SIGNATURE: 1 | |
# Enable PEP 597 EncodingWarnings | |
PYTHONWARNDEFAULTENCODING: true | |
permissions: | |
contents: read # to fetch code (actions/checkout) | |
jobs: | |
tests: | |
runs-on: ${{ matrix.os }} | |
strategy: | |
matrix: | |
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] | |
os: ['windows-latest', 'ubuntu-22.04', 'macos-13', 'macos-14'] | |
# Split macOS workflows between macos-13 (x86_64) and macos-14 (arm64) | |
# runners to cover both architectures without running all combinations. | |
exclude: | |
- python-version: '3.8' | |
os: 'macos-14' | |
- python-version: '3.10' | |
os: 'macos-14' | |
- python-version: '3.12' | |
os: 'macos-14' | |
- python-version: '3.9' | |
os: 'macos-13' | |
- python-version: '3.11' | |
os: 'macos-13' | |
- python-version: '3.13' | |
os: 'macos-13' | |
fail-fast: false | |
steps: | |
- name: Checkout PyInstaller code | |
uses: actions/checkout@v4 | |
- name: Set up Python ${{ matrix.python-version }} | |
uses: actions/setup-python@v5 | |
with: | |
python-version: ${{ matrix.python-version }} | |
- name: Install apt packages | |
if: startsWith(matrix.os, 'ubuntu') | |
run: | | |
sudo apt-get update -qq | |
sudo apt-get upgrade -qq | |
sudo apt-get install -qq --no-install-recommends \ | |
libcmocka-dev \ | |
libxml2-dev libxslt1-dev gfortran libatlas-base-dev \ | |
libespeak1 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 \ | |
libxkbcommon-x11-0 libxcb-icccm4 libxcb1 openssl \ | |
libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev \ | |
libxcb-shape0-dev libxcb-xkb-dev xvfb \ | |
libopengl0 libegl1 \ | |
libpulse0 libpulse-mainloop-glib0 \ | |
gstreamer1.0-plugins-base libgstreamer-gl1.0-0 \ | |
gir1.2-gtk-3.0 libgirepository1.0-dev \ | |
libfuse2 | |
# The following locales are required by test_basic::test_user_preferred_locale | |
# - en_US.UTF-8 | |
# - en_US.ISO8859-1 | |
# - sl_SI.UTF-8 | |
# - sl_SI.ISO8859-2 | |
# | |
# The following locale is used by test_basic::test_time_module_localized | |
# - cs_CZ.UTF-8 | |
- name: Install and enable additional locales | |
if: startsWith(matrix.os, 'ubuntu') | |
run: | | |
sudo apt-get update -qq | |
sudo apt-get install -qq --no-install-recommends locales | |
sudo locale-gen \ | |
en_US.UTF-8 \ | |
en_US \ | |
sl_SI.UTF-8 \ | |
sl_SI \ | |
cs_CZ.UTF-8 | |
locale -a | |
- name: Download AppImage tool | |
if: startsWith(matrix.os, 'ubuntu') | |
run: | | |
wget \ | |
https://github.com/AppImage/AppImageKit/releases/download/12/appimagetool-x86_64.AppImage \ | |
-O $HOME/appimagetool-x86_64.AppImage | |
chmod a+x $HOME/appimagetool-x86_64.AppImage | |
- name: Set cache dir | |
run: echo "pip_cache_dir=$(pip cache dir)" >> $GITHUB_ENV | |
shell: bash | |
- name: Fetch cache | |
id: cache-target | |
uses: actions/cache@v4 | |
with: | |
path: ${{ env.pip_cache_dir }} | |
key: ${{ runner.os }}-${{ matrix.python-version }} | |
- name: Check if bootloader code conforms to gnu90 C standard | |
if: startsWith(matrix.os, 'ubuntu') | |
run: | | |
# Compile bootloader | |
cd bootloader | |
CC="gcc -std=gnu90" python waf --tests all | |
- name: Check if bootloader code conforms to c99 ISO C standard (pedantic mode) | |
if: startsWith(matrix.os, 'ubuntu') | |
run: | | |
# Compile bootloader | |
cd bootloader | |
CC="gcc -std=c99 -pedantic" python waf --tests all | |
- name: Check if bootloader is buildable with --static-zlib option | |
if: startsWith(matrix.os, 'ubuntu') | |
run: cd bootloader && python waf --static-zlib --tests all | |
- name: Check if bootloader is buildable for Windows ARM | |
if: startsWith(matrix.os, 'windows') | |
run: | | |
cd bootloader | |
python waf --tests --target-arch=64bit-arm all | |
ls ../PyInstaller/bootloader/Windows-64bit-arm | |
- name: Update pip | |
run: python -m pip install --upgrade pip setuptools wheel build | |
# On (macos-14, python 3.10), (macos-13, python 3.11) and (macos-14, python 3.11) | |
# combinations the initial `setuptools` installation seems to contain `.opt-1.pyc` | |
# files in `__pycache__` directories. During the upgrade, `pip` fails to remove | |
# those directories (https://github.com/pypa/pip/issues/11835). | |
# | |
# `setuptools` 75.4 removed its vendored copy of `importlib_resources`, but due to | |
# the above issue, the effectively empty directory remains, turning `importlib_resources` | |
# into defunct namespace package, which causes some of our tests to fail. | |
- name: Work around potentially broken setuptools upgrade | |
shell: python | |
run: | | |
import sys | |
import pathlib | |
import shutil | |
import importlib.util | |
spec = importlib.util.find_spec('setuptools._vendor.importlib_resources') | |
if spec is None: | |
print("Did not find setuptools-vendored copy of importlib_resources.") | |
sys.exit(0) | |
elif spec.loader is not None: | |
print("Found a valid setuptools-vendored copy of importlib_resources.") | |
sys.exit(0) | |
print("Found a defunct setuptools-vendored copy of importlib_resources!") | |
# List the contents of importlib_resources package directory for debug purposes | |
def list_directory(path, pad=""): | |
for child in path.iterdir(): | |
if child.is_dir(): | |
print(f"{pad} + {child.name}") | |
list_directory(child, pad + " ") | |
else: | |
print(f"{pad} - {child.name} ({child.stat().st_size} bytes)") | |
for path in spec.submodule_search_locations: | |
print(f"Listing contents of {path}") | |
list_directory(pathlib.Path(path)) | |
# Remove | |
for path in spec.submodule_search_locations: | |
print(f"Removing {path}...") | |
shutil.rmtree(path) | |
- name: Compile bootloader | |
run: cd bootloader && python waf --tests all | |
- name: Download dependencies | |
shell: bash | |
run: python -m pip download --dest=dist .[completion] && rm -f dist/pyinstaller-*.whl | |
- name: Build wheels | |
run: sh release/build-wheels | |
- name: Install PyInstaller | |
run: python -m pip install --no-index --find-links=dist pyinstaller[completion] | |
- name: Check pyinstaller --help | |
run: python -m PyInstaller -h | |
- name: Install test dependencies (base tools) | |
run: | | |
python -m pip install --progress-bar=off --upgrade --requirement tests/requirements-base.txt | |
- name: Install test dependencies (tools and libraries) | |
if: ${{ !endsWith(matrix.python-version, '-dev') }} | |
run: | | |
python -m pip install --progress-bar=off --upgrade --requirement tests/requirements-libraries.txt | |
- name: Set up extra environment variables (non-Windows) | |
if: ${{ !startsWith(matrix.os, 'windows') }} | |
run: | | |
# Run Qt-based tests with offscreen backend. This seems to break QtWebEngine tests on Windows, so | |
# apply it only here, in non-Windows path. | |
echo "QT_QPA_PLATFORM=offscreen" >> $GITHUB_ENV | |
- name: Start display server | |
if: startsWith(matrix.os, 'ubuntu') | |
run: | | |
Xvfb :99 & | |
echo "DISPLAY=:99" >> $GITHUB_ENV | |
# Required on macOS >= 11 by tests that register custom URL schema. This could also be achieved by passing | |
# --basetemp to pytest, but using environment variable allows us to have a unified "Run test" step for | |
# all OSes. | |
# | |
# We now relocate the temporary directory to a fixed location on all OSes, in order to be able to generate | |
# artifacts out of failed tests. | |
- name: Relocate temporary dir | |
shell: bash | |
run: | | |
echo "PYTEST_DEBUG_TEMPROOT=$RUNNER_TEMP" >> $GITHUB_ENV | |
- name: Run tests | |
id: run-tests | |
run: > | |
pytest | |
-n 5 --maxfail 3 --durations 10 tests/unit tests/functional | |
# On all platforms, create a tarball to ensure that symlinks are preserved. Avoid using compression here, | |
# as the tarball will end up collected into artifact zip archive. | |
# To simplify this across platform, run this step in python and use python's tarfile module. | |
- name: Archive failed tests | |
if: ${{ failure() && steps.run-tests.outcome == 'failure' }} | |
shell: python | |
run: | | |
import os | |
import sys | |
import tarfile | |
try: | |
import getpass | |
user = getpass.getuser() or "unknown" | |
except Exception: | |
user = "unknown" | |
temproot = os.environ['PYTEST_DEBUG_TEMPROOT'] | |
pytest_name = f'pytest-of-{user}' | |
pytest_fullpath = os.path.join(temproot, pytest_name) | |
print(f"Input directory: {pytest_fullpath}!", file=sys.stderr) | |
output_file = os.path.join(temproot, 'archived-failed-tests.tar') | |
print(f"Output file: {output_file}!", file=sys.stderr) | |
assert os.path.isdir(pytest_fullpath) | |
assert not os.path.exists(output_file) | |
with tarfile.open(output_file, "w") as tf: | |
tf.add(pytest_fullpath, arcname=pytest_name, recursive=True) | |
print(f"Created {output_file}!", file=sys.stderr) | |
- name: Create artifact out of archived failed tests | |
if: ${{ failure() && steps.run-tests.outcome == 'failure' }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: failed-tests-${{ matrix.os }}-python-${{ matrix.python-version }} | |
path: '${{ env.PYTEST_DEBUG_TEMPROOT }}/archived-failed-tests.tar' | |
# Install and test PyInstaller Hook Sample, to ensure that tests declared in | |
# entry-points are discovered. | |
- name: Install hooksample | |
run: python -m pip install "https://github.com/pyinstaller/hooksample/archive/v4.0rc1.zip" | |
# Augment _pyinstaller_hooks_contrib with bogus hooks that conflict with the hooks provided by hooksample. | |
# Due to (implicit) hook priority, the 3rd-party hooks should be chosen over _pyinstaller_hooks_contrib | |
# ones, and so the bogus hooks should never be ran. This applies to standard module hooks, as well as | |
# the pre-find-module-path and pre-safe-import-module hooks. | |
- name: Inject bogus hooks | |
shell: python | |
run: | | |
import os | |
from _pyinstaller_hooks_contrib import ( | |
stdhooks, | |
pre_safe_import_module, | |
pre_find_module_path, | |
) | |
with open(os.path.join(stdhooks.__path__[0], "hook-pyi_hooksample.py"), "w", encoding="utf-8") as f: | |
f.write('raise Exception("Wrong hook! Use the pyi_hooksample copy instead!")\n') | |
with open(os.path.join(pre_safe_import_module.__path__[0], "hook-pyi_hooksample.py"), "w", encoding="utf-8") as f: | |
f.write('raise Exception("Wrong hook! Use the pyi_hooksample copy instead!")\n') | |
with open(os.path.join(pre_find_module_path.__path__[0], "hook-pyi_hooksample.py"), "w", encoding="utf-8") as f: | |
f.write('raise Exception("Wrong hook! Use the pyi_hooksample copy instead!")\n') | |
- name: Run hooksample tests | |
run: | | |
# The ``run_tests`` script is invoked with the ``-c`` option to | |
# specify a ``pytest.ini``, rather than allowing pytest to find | |
# something unexpected in the filesystem (it searches from the root | |
# dir all the way to the top of the filesystem per | |
# https://docs.pytest.org/en/latest/customize.html). | |
python -m PyInstaller.utils.run_tests -c PyInstaller/utils/pytest.ini --include_only=pyi_hooksample. | |
test-alpine: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout PyInstaller code | |
uses: actions/checkout@v4 | |
- name: Build container | |
run: docker build -f alpine.dockerfile -t foo . | |
- name: Run tests in container | |
run: > | |
docker run | |
foo | |
pytest | |
-n 5 --maxfail 3 --durations 10 tests/unit tests/functional | |
# Verify that installing from sdist works. This is mostly just a verification that the MANIFEST.in contains | |
# all the extras needed to compile the bootloader. | |
- name: Setup Python for creating sdist | |
uses: actions/setup-python@v5 | |
with: | |
python-version: 3.x | |
- name: Create sdist | |
run: | | |
git clean -xfdq . | |
python -m pip install build | |
python -m build --sdist --outdir dist | |
docker build --target=wheel-factory -t bar -f alpine.dockerfile . | |
- name: Install and test PyInstaller installed from sdist | |
run: | | |
sdist="$(ls dist)" | |
docker run -v "$PWD/dist:/io" bar ash -c " | |
python -m pip install /io/$sdist | |
echo 'print(1 + 1)' > test.py | |
pyinstaller test.py | |
./dist/test/test | |
" | |
test-msys2: | |
runs-on: windows-latest | |
strategy: | |
matrix: | |
include: | |
- { sys: mingw64, env: x86_64 } | |
- { sys: mingw32, env: i686 } | |
- { sys: ucrt64, env: ucrt-x86_64 } | |
fail-fast: false | |
defaults: | |
run: | |
shell: msys2 {0} | |
steps: | |
- name: Setup msys2 environment | |
uses: msys2/setup-msys2@v2 | |
with: | |
update: true | |
msystem: ${{matrix.sys}} | |
install: >- | |
mingw-w64-${{matrix.env}}-gcc | |
mingw-w64-${{matrix.env}}-python | |
mingw-w64-${{matrix.env}}-python-pip | |
mingw-w64-${{matrix.env}}-python-wheel | |
mingw-w64-${{matrix.env}}-python-setuptools | |
mingw-w64-${{matrix.env}}-python-pywin32-ctypes | |
mingw-w64-${{matrix.env}}-python-packaging | |
mingw-w64-${{matrix.env}}-python-pytest | |
mingw-w64-${{matrix.env}}-python-pytest-xdist | |
mingw-w64-${{matrix.env}}-python-pytest-timeout | |
mingw-w64-${{matrix.env}}-python-pywin32 | |
mingw-w64-${{matrix.env}}-python-pillow | |
# Some msys2 python packages are not available for i686 anymore: | |
# - numpy | |
# - pefile | |
# - psutil (removed with update to psutil 6.1.1; see https://github.com/msys2/MINGW-packages/commit/7f1c75b33a5aebb89899f99f5fa656c623eeb9ed) | |
# If running in 64-bit environment, install the said packages here. | |
# Otherwise, we will run tests without these packages (note that | |
# if not installed here, `pefile` will end up installed via `pip` | |
# as part of PyInstaller's dependencies). | |
- name: Install extra msys2 packages (64-bit only) | |
if: matrix.env != 'i686' | |
run: > | |
pacman -S --noconfirm | |
mingw-w64-${{matrix.env}}-python-numpy | |
mingw-w64-${{matrix.env}}-python-pefile | |
mingw-w64-${{matrix.env}}-python-psutil | |
- name: Checkout PyInstaller code | |
uses: actions/checkout@v4 | |
- name: Show system and python information | |
run: | | |
uname -a | |
python --version | |
- name: Install PyInstaller | |
run: | | |
# Compile bootloader | |
cd bootloader | |
python waf --gcc --tests all | |
cd .. | |
# Install PyInstaller. | |
python -m pip install --progress-bar=off .[completion] | |
# Make sure the help options print. | |
python -m PyInstaller -h | |
- name: Run tests | |
run: > | |
pytest | |
-n 5 --maxfail 3 --durations 10 tests/unit tests/functional | |
test-cygwin: | |
runs-on: windows-latest | |
defaults: | |
run: | |
shell: bash -o igncr -eo pipefail '{0}' | |
steps: | |
- name: Setup Cygwin | |
uses: cygwin/cygwin-install-action@master | |
with: | |
platform: x86_64 | |
# NOTE: some of the provided packages (python39-wheel, | |
# python39-setuptools, python39-packaging, python39-pytest) are | |
# out-of-date, and we instead install PyPI wheels. | |
# | |
# NOTE: for some reason, python39-pip depends on python39-sphinx, | |
# which is an old sphinx v4.4.0 package. This version contains | |
# an issue caused by open() without specified encoding, which | |
# was fixed in sphinx >= v5.0. Therefore, we install an up-to-date | |
# version of sphinx from PyPI in the "Update pip and base test tools" | |
# step. | |
packages: >- | |
gcc-core | |
zlib | |
zlib-devel | |
xorg-server-extra | |
python39 | |
python39-devel | |
python39-pip | |
python39-tkinter | |
- name: Disable CRLF line endings in git checkout | |
run: git config --global core.autocrlf input | |
- name: Checkout PyInstaller code | |
uses: actions/checkout@v4 | |
- name: Show system and python information | |
run: | | |
uname -a | |
python --version | |
- name: Update pip and base test tools | |
run: python -m pip install --progress-bar=off --upgrade pip setuptools wheel build --requirement tests/requirements-base.txt sphinx | |
- name: Install PyInstaller | |
run: | | |
# Compile bootloader | |
cd bootloader | |
python waf --gcc --tests all | |
cd .. | |
# Install PyInstaller. | |
python -m pip install --progress-bar=off .[completion] | |
# Make sure the help options print. | |
python -m PyInstaller -h | |
- name: Start display server | |
run: | | |
Xvfb :99 & | |
echo "DISPLAY=:99" >> $GITHUB_ENV | |
- name: Run tests | |
# Keep the number of pytest workers below the number of available | |
# CPU cores to avoid CPU contention scenarios, which seem to trigger | |
# a race condition in Cygwin `tkinter` (seemingly in both unfrozen | |
# and frozen programs). | |
run: > | |
python -m pytest | |
-n 3 --maxfail 3 --durations 10 tests/unit tests/functional |