Repository navigation
chore(deps): bump the actions group across 1 directory with 6 updates #683
Workflow file for this run
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
| # This workflow will install Python dependencies, run tests and lint with a variety of Python versions | |
| # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions | |
| name: Python Package | |
| on: | |
| # `push` restricted to `main` so feature branches only run via `pull_request`. | |
| # Otherwise every push to a branch with an open PR would trigger the workflow | |
| # twice (once for `push`, once for `pull_request`) — doubling CI cost and | |
| # latency for no benefit. | |
| push: | |
| branches: [main] | |
| pull_request: | |
| # Allow re-running the workflow without an empty push (handy after the | |
| # workflow gets `disabled_inactivity` after 60 days idle). | |
| workflow_dispatch: | |
| schedule: | |
| - cron: '0 0 */7 * *' | |
| # Cancel an in-flight run when a newer commit lands on the same ref. Keeps the | |
| # queue lean and stops stale runs from blocking the merge button. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Install lint deps | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install flake8 | |
| - name: Lint | |
| run: | | |
| flake8 PyMemoryEditor tests | |
| type-check: | |
| needs: lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Install dev deps | |
| # Include the `speed` extra (NumPy) so mypy type-checks the NumPy fast | |
| # path against real stubs. Without it, `import numpy` resolves to `Any` | |
| # and the `# type: ignore` in scan_numpy.py reads as unused. | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[dev,speed]" | |
| - name: Run mypy | |
| run: | | |
| mypy PyMemoryEditor | |
| build: | |
| needs: lint | |
| runs-on: ${{ matrix.os }} | |
| # Hard ceiling, same purpose as build-macos'. A cell takes ~4 minutes | |
| # (measured 2026-10-08: 2m30s to 4m25s); without this the default is 6h. | |
| timeout-minutes: 20 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ['3.10', '3.11', '3.12', '3.13'] | |
| os: | |
| - ubuntu-latest | |
| - windows-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install Qt system libraries (Linux) | |
| # PySide6 links against libEGL/libGL/libxkbcommon/libfontconfig and the | |
| # XCB stack at import time, even when running under the `offscreen` | |
| # platform plugin. The Ubuntu runner ships without them, so pytest-qt's | |
| # `import QtGui` crashes with `libEGL.so.1: cannot open shared object`. | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| libegl1 \ | |
| libgl1 \ | |
| libxkbcommon0 \ | |
| libfontconfig1 \ | |
| libdbus-1-3 \ | |
| libxcb-cursor0 \ | |
| libxcb-icccm4 \ | |
| libxcb-image0 \ | |
| libxcb-keysyms1 \ | |
| libxcb-randr0 \ | |
| libxcb-render-util0 \ | |
| libxcb-shape0 \ | |
| libxcb-sync1 \ | |
| libxcb-xfixes0 \ | |
| libxcb-xinerama0 \ | |
| libxcb-xkb1 \ | |
| libxkbcommon-x11-0 | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[dev]" | |
| - name: Test with pytest | |
| env: | |
| QT_QPA_PLATFORM: offscreen | |
| # `--ignore=tests/mcp_server`: the MCP server has its own job | |
| # (`build-mcp`) so this matrix keeps measuring the library and the app, | |
| # at the wall clock it always had. Its tests walk a live process's whole | |
| # address space, which on macOS is ~390 GiB (see build-macos below) -- | |
| # running them in every cell of a 4x2 matrix plus macOS plus speed paid | |
| # that cost eight times over, and it pushed build-macos past its | |
| # 20-minute budget. | |
| # | |
| # --maxfail=10 rather than -x: one broken test should not hide the next | |
| # nine, but a wholesale breakage (a backend that fails to import) still | |
| # stops early instead of printing 600 tracebacks. | |
| # | |
| # --cov-fail-under is the real, in-repo coverage gate (deterministic and | |
| # independent of any external service), and `--cov-config` is what keeps | |
| # it meaningful now that the MCP suite lives in `build-mcp`. | |
| # | |
| # Without it this job measures PyMemoryEditor/mcp -- 918 statements it | |
| # never runs -- as uncovered, which is a measurement of absence, not of | |
| # quality. It also varies by platform in a way the gate cannot absorb: | |
| # measured that way, Linux came out at 46% and macOS at 59%, so a floor | |
| # picked from macOS alone failed all eight matrix cells. Scoped to the | |
| # library, the numbers are 87% on macOS and 66% on Linux. The floor is | |
| # 62: four points under the lowest cell (Linux), so it trips on a real | |
| # regression rather than on platform spread. (It was 60 from before the | |
| # MCP server existed, when nothing had been measured.) | |
| # | |
| # (coverage.py only counts the host backend + shared code -- the two | |
| # foreign backends aren't imported, which is most of that platform | |
| # spread.) Ratchet up once the numbers settle on Codecov. | |
| run: | | |
| pytest tests --ignore=tests/mcp_server -v -s --maxfail=10 --cov=PyMemoryEditor --cov-config=.coveragerc-lib --cov-report=term --cov-report=xml --cov-fail-under=62 | |
| - name: Upload coverage to Codecov | |
| # Upload from every matrix cell so Codecov merges each OS's backend | |
| # coverage into one combined view (each platform exercises a different | |
| # win32/linux/macos backend). Informational only — see codecov.yml — so a | |
| # flaky upload never blocks the merge; the hard gate is --cov-fail-under | |
| # above. Runs even when tests fail so partial coverage stays visible. | |
| if: always() | |
| uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1 | |
| with: | |
| files: ./coverage.xml | |
| flags: ${{ runner.os }}-py${{ matrix.python-version }} | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| fail_ci_if_error: false | |
| # macOS gets a single cell instead of a row in the matrix above. Its only | |
| # purpose is to measure the `macos` backend so the merged Codecov report | |
| # covers all three platforms; the Python-version sweep stays on Linux and | |
| # Windows, where it costs a fraction of the wall clock. | |
| # | |
| # Unlike the matrix, this job installs `.[dev,speed]` (NumPy). That is not a | |
| # preference — it is what makes a macOS cell viable at all. A macOS process | |
| # exposes ~390 GiB of sparse address space across ~145 regions, two orders of | |
| # magnitude more than a Linux one, and the scan tests walk all of it. Measured | |
| # on an M-series Mac, this exact command takes ~82s with NumPy and 681s | |
| # without: an 8x gap that a 3-core runner only widens, which is what made | |
| # macOS look like it hung here before (it was removed in 041bfc0). | |
| # The pure-Python scan path loses nothing — all eight cells of the matrix | |
| # above still exercise it. | |
| build-macos: | |
| needs: lint | |
| runs-on: macos-latest | |
| # Hard ceiling so a pathological scan can never sit at the 6h default. | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| # No Qt system-library step here: the PySide6 macOS wheel bundles its own | |
| # Qt frameworks, unlike the Linux one. | |
| - name: Install dependencies (with NumPy speed extra) | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[dev,speed]" | |
| - name: Test with pytest | |
| env: | |
| QT_QPA_PLATFORM: offscreen | |
| # Same conservative coverage gate as the main matrix (see that step's note). | |
| run: | | |
| pytest tests --ignore=tests/mcp_server -v -s --maxfail=10 --cov=PyMemoryEditor --cov-config=.coveragerc-lib --cov-report=term --cov-report=xml --cov-fail-under=62 | |
| - name: Upload coverage to Codecov | |
| # Flag mirrors the matrix format (`runner.os` is "macOS" here) so Codecov | |
| # groups this cell alongside the Linux and Windows uploads. | |
| if: always() | |
| uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1 | |
| with: | |
| files: ./coverage.xml | |
| flags: macOS-py3.12 | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| fail_ci_if_error: false | |
| # Isolated job for the optional NumPy-accelerated scan fast path (the | |
| # `[speed]` extra). The matrix above deliberately runs WITHOUT NumPy so the | |
| # pure-Python scan path stays covered; this single Linux job installs | |
| # `.[speed]` on top of the dev deps to exercise the vectorized path. | |
| build-speed: | |
| needs: lint | |
| runs-on: ubuntu-latest | |
| # ~3 minutes measured; see the `build` note. | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Install Qt system libraries (Linux) | |
| # PySide6 links against libEGL/libGL/libxkbcommon/libfontconfig and the | |
| # XCB stack at import time, even when running under the `offscreen` | |
| # platform plugin. The Ubuntu runner ships without them, so pytest-qt's | |
| # `import QtGui` crashes with `libEGL.so.1: cannot open shared object`. | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| libegl1 \ | |
| libgl1 \ | |
| libxkbcommon0 \ | |
| libfontconfig1 \ | |
| libdbus-1-3 \ | |
| libxcb-cursor0 \ | |
| libxcb-icccm4 \ | |
| libxcb-image0 \ | |
| libxcb-keysyms1 \ | |
| libxcb-randr0 \ | |
| libxcb-render-util0 \ | |
| libxcb-shape0 \ | |
| libxcb-sync1 \ | |
| libxcb-xfixes0 \ | |
| libxcb-xinerama0 \ | |
| libxcb-xkb1 \ | |
| libxkbcommon-x11-0 | |
| - name: Install dependencies (with NumPy speed extra) | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[dev,speed]" | |
| - name: Test with pytest (NumPy fast path) | |
| env: | |
| QT_QPA_PLATFORM: offscreen | |
| # Same conservative coverage gate as the main matrix (see that step's note). | |
| run: | | |
| pytest tests --ignore=tests/mcp_server -v -s --maxfail=10 --cov=PyMemoryEditor --cov-config=.coveragerc-lib --cov-report=term --cov-report=xml --cov-fail-under=62 | |
| - name: Upload coverage to Codecov | |
| if: always() | |
| uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1 | |
| with: | |
| files: ./coverage.xml | |
| flags: speed-Linux-py3.12 | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| fail_ci_if_error: false | |
| # The MCP server gets its own job rather than a row in the matrix above. | |
| # | |
| # Its tests are the expensive ones: `test_live.py` and half of | |
| # `test_fake_fidelity.py` attach to the running test process and scan its | |
| # entire address space, which is exactly the workload the build-macos note | |
| # below measures at ~390 GiB. Running them in all ten cells of the other | |
| # jobs multiplied that by ten and timed macOS out; running them once per OS | |
| # keeps the platform coverage that actually matters and costs a fraction. | |
| # | |
| # One Python version, three operating systems -- the opposite shape from the | |
| # matrix above, and deliberately so. Nothing in this package is | |
| # version-sensitive (it is plain 3.10+ typing over the library's own API), | |
| # but plenty of it is platform-sensitive: the Windows-only read-only handle, | |
| # macOS `task_for_pid`, and the per-platform process enumeration and denylist. | |
| # The one Windows-only bug this suite has caught so far was a test reading a | |
| # UTF-8 file with the locale's encoding, which only cp1252 could fail on. | |
| # | |
| # NumPy is installed for the same reason build-macos installs it: without the | |
| # vectorized scan path, walking a macOS address space takes ~8x longer. | |
| build-mcp: | |
| needs: lint | |
| runs-on: ${{ matrix.os }} | |
| # Hard ceiling, same purpose as build-macos'. The suite itself takes | |
| # ~15s of pytest on all three runners; this is generous on purpose. | |
| # | |
| # It was briefly 45 while this job took 20+ minutes everywhere. The cause | |
| # was not scan cost: one test parametrized a 200 000-character value, so | |
| # its pytest id was 400 KB, and the Actions log uploader spent ~150s per | |
| # such line. Five lines, twenty minutes. Fixed at the test. | |
| timeout-minutes: 15 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| # `scan_heavy` -- the tests that scan a *live* process's whole address | |
| # space -- runs on Linux only. The cost is wildly platform-dependent: a | |
| # macOS process exposes ~390 GiB across ~145 regions and Windows is not | |
| # much better, so on a runner each of those scans is minutes. Measured | |
| # on the previous attempt: this job took 22m21s on macOS and hit its | |
| # timeout at 30m on Windows, while the same suite is 18s locally. | |
| # | |
| # Nothing is lost by pinning them to Linux. What the other two OSes are | |
| # here for is the platform-specific code -- the Windows-only read-only | |
| # handle, macOS task_for_pid, per-platform enumeration and the | |
| # truncated-name denylist -- none of which are scans. The library's own | |
| # live-scan coverage on those platforms comes from `build` and | |
| # `build-macos`. | |
| # | |
| # Set through PYTEST_ADDOPTS rather than the run: line because the | |
| # Windows runner's default shell is pwsh, and `-m "not scan_heavy"` | |
| # would need different quoting there than on bash. | |
| include: | |
| - os: ubuntu-latest | |
| addopts: '' | |
| - os: windows-latest | |
| addopts: '-m "not scan_heavy"' | |
| - os: macos-latest | |
| addopts: '-m "not scan_heavy"' | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Install dependencies (MCP SDK + NumPy speed extra) | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[dev,speed]" | |
| - name: Test the MCP server | |
| # Coverage is scoped to the package under test, via both --cov and | |
| # .coveragerc-mcp. Measured against the whole package instead, this gate | |
| # came out at 71% on macOS but 60.4% on Linux -- four tenths of a point | |
| # above a floor of 60, on a number mostly about code this job does not | |
| # target. Scoped, the MCP package is 91% covered by its own suite on | |
| # macOS and 90.4% on Linux, so 85 leaves five points of headroom. | |
| env: | |
| PYTEST_ADDOPTS: ${{ matrix.addopts }} | |
| # `-q --durations=25` rather than the `-v -s` the library jobs use. This | |
| # job's timing is the open question -- 20 minutes on a runner against | |
| # 12.5s in a container, and cutting the process-table walks 14x changed | |
| # nothing -- and `-v -s` emitted 977 log lines in the first 8 seconds, | |
| # so GitHub truncated the log long before the part that would say which | |
| # tests were slow. Quiet output keeps the log small enough that the | |
| # duration table at the end survives. | |
| # | |
| # -p no:pytest-qt: the dev extra brings pytest-qt, whose plugin imports | |
| # QtGui at startup. The name is the entry point's, hyphen included -- | |
| # `-p no:pytestqt` silently does nothing, which is how this failed twice. | |
| # It dies with `ImportError: libEGL.so.1` on a bare Linux runner; the | |
| # library jobs apt-install a pile of X libraries for it. This job runs no | |
| # Qt tests, so it disables the plugin instead of paying for them. | |
| run: | | |
| pytest tests/mcp_server -p no:pytest-qt -q --durations=25 --maxfail=10 --cov=PyMemoryEditor/mcp --cov-config=.coveragerc-mcp --cov-report=term --cov-report=xml --cov-fail-under=85 | |
| - name: Upload coverage to Codecov | |
| # Its own flag, so the merged report shows MCP coverage alongside the | |
| # library's instead of one overwriting the other. | |
| if: always() | |
| uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1 | |
| with: | |
| files: ./coverage.xml | |
| flags: mcp-${{ runner.os }}-py3.12 | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| fail_ci_if_error: false | |
| # Build the Sphinx docs with warnings as errors, so a broken cross-reference, | |
| # a missing toctree entry or an autodoc import failure fails the PR instead | |
| # of surfacing later on Read the Docs. Read the Docs keeps | |
| # `fail_on_warning: false` (.readthedocs.yaml) so a warning that only its | |
| # environment produces never stops the published docs from updating; this | |
| # job is the gate. | |
| docs: | |
| needs: lint | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.11' | |
| - name: Install docs dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r docs/requirements.txt | |
| pip install -e . | |
| - name: Build HTML docs (warnings are errors) | |
| run: | | |
| python -m sphinx -W --keep-going -b html docs docs/_build/html | |
| # Dependency audit. The core library has no dependencies; this checks the | |
| # extras (Qt app, MCP SDK, NumPy) and their transitive closure against the | |
| # PyPI advisory database. Advisory, not a gate: `continue-on-error` keeps a | |
| # new upstream CVE from blocking an unrelated PR, and the weekly `schedule` | |
| # run surfaces it on main regardless. Dependabot security PRs remain the | |
| # fix path; this job is the visibility the disabled version-update PRs | |
| # don't provide (see .github/dependabot.yml). | |
| audit: | |
| needs: lint | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Install the package with every runtime extra | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install ".[app,mcp,speed]" pip-audit | |
| - name: pip-audit | |
| run: | | |
| pip-audit --progress-spinner off |