pm python node: 3.14.7+20260901 (freshest python-build-standalone 3.14 build) for the 6 desktop targets; the bionic row moves from the third-party TUR python3.11 deb to the official termux-main python_3.14.6-1 deb (which lags PBS by one patch — pinned manually, documented). All 7 digests fetched from the live sources (PBS release API + termux-main Packages index). pm/packages.py: main_bin_rel python3.14, deb_package python, bionic fetch constant, latest_versions guards bionic (no PBS build exists). termux lane: PYTHON_ABI cp311->cp314, python3.11->python3.14 paths, libpython3.11.so->3.14, TARGET_ENV 3.11.15->3.14.6 AND sys_platform linux->android (CPython 3.13+ reports 'android', docs-verified) — linux- gated markers no longer admit the termux target. runtime_libs.json needs no change: every python 3.14.6-1 Depends is already staged. CI: python-version/--python 3.11->3.14 across all 11 workflows incl. the uv lockfile-check lane. Installers derive the minor from the lock already; fallbacks bumped. Sandbox images nikolaik/python-nodejs:python3.11-nodejs20 -> python3.14-nodejs22 (tag exists). runtime_repair fall-forward cap now tracks the <3.15 requires-python window. Docs/README python version claims updated.
179 lines
7.9 KiB
YAML
179 lines
7.9 KiB
YAML
name: OS-specific tests
|
|
|
|
# Runs the tests that can only be trusted on their own host OS.
|
|
#
|
|
# The main Python suite (.github/workflows/tests.yml) runs on
|
|
# ubuntu-latest and covers everything that is either platform-agnostic or
|
|
# genuinely Linux-specific. Tests whose subject is macOS- or
|
|
# Windows-specific behaviour carry a marker (see the ``_OS_MARKS`` block
|
|
# comment in tests/conftest.py) and are SKIPPED on Linux, because faking
|
|
# ``sys.platform`` on a Linux runner selects the branch under test without
|
|
# reproducing any of the OS behaviour that branch exists for. This workflow
|
|
# is where those markers actually execute:
|
|
#
|
|
# macos → ``-m platforms`` on macos-latest
|
|
# windows → ``-m platforms`` on windows-latest-32-core
|
|
#
|
|
# (tests/conftest.py's ``pytest_collection_modifyitems`` hook skips
|
|
# foreign-OS ``platforms(...)`` markers on each host, so one shared ``-m
|
|
# platforms`` expression selects "this host's own marked tests" on either
|
|
# lane — the helper narrows WHICH FILES get imported first.)
|
|
#
|
|
# Deliberately NOT sliced. The marked set is small (tens of tests, not
|
|
# thousands), so one plain ``pytest`` process per OS is both faster and far
|
|
# less machinery than the per-file parallel runner the Linux lane uses.
|
|
# Each lane FAILS when it selects zero tests (pytest exit code 5) — a
|
|
# renamed marker or bad selector can never report a green job that ran
|
|
# nothing.
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
desktop_updater:
|
|
description: >-
|
|
Run the Windows desktop-update hand-off integration tests
|
|
(tests/test_desktop_update_windows_*.py). These spawn the real
|
|
scripts/desktop-update/windows.ps1 and poll its loopback server, so
|
|
they carry process-timing noise a shared runner amplifies; the
|
|
caller gates them on the classifier's desktop_updater lane so a PR
|
|
that never touched that surface cannot be failed by it. Push /
|
|
dispatch runs fail open (classifier sets every lane true).
|
|
type: boolean
|
|
required: false
|
|
default: true
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: tests-os-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
os-tests:
|
|
name: ${{ matrix.name }}
|
|
runs-on: ${{ matrix.runner }}
|
|
timeout-minutes: 30
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- name: macOS-only tests
|
|
runner: macos-latest
|
|
marker: macos
|
|
- name: Windows-only tests
|
|
runner: windows-latest-32-core
|
|
marker: windows
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
|
|
- name: Install uv
|
|
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # 8.2.0
|
|
with:
|
|
# Pinned for the same reason as the Linux lane: unpinned, setup-uv
|
|
# resolves "latest" by fetching a manifest on every job and a
|
|
# transient fetch failure fails the whole job.
|
|
version: "0.9.28"
|
|
enable-cache: true
|
|
cache-dependency-glob: |
|
|
pyproject.toml
|
|
uv.lock
|
|
|
|
- name: Set up Python 3.14
|
|
uses: ./.github/actions/retry
|
|
with:
|
|
command: uv python install 3.14
|
|
|
|
- name: Install dependencies
|
|
# Same extras as the Linux test lane so an OS-marked test can import
|
|
# anything its Linux siblings can. ``[all]`` is deliberately
|
|
# Windows/macOS-installable (see the policy comment on the extra in
|
|
# pyproject.toml — matrix/python-olm was removed from it precisely
|
|
# because it could not build here).
|
|
uses: ./.github/actions/retry
|
|
with:
|
|
command: uv sync --locked --python 3.14 --extra all --extra dev --extra anthropic --extra mistral --extra fal --extra modal --extra daytona --extra hindsight --extra parallel-web
|
|
|
|
- name: Minimize uv cache
|
|
run: uv cache prune --ci
|
|
|
|
- name: Run ${{ matrix.marker }} tests
|
|
# scripts/run_tests.sh — the canonical runner, same as every other
|
|
# lane: per-file subprocess isolation (run_tests_parallel.py). It
|
|
# natively tolerates per-file empty collections (a platform-gated
|
|
# file collects nothing after -m filtering, exit 5, treated as a
|
|
# pass for that file) and fails the run itself when NOTHING was
|
|
# collected across all files (exit 2) — the zero-tests guard the
|
|
# bare-pytest variant implemented by hand with an exit-5 branch.
|
|
#
|
|
# Selection still narrows WHICH FILES run:
|
|
# scripts/ci/list_os_marked_tests.py emits the file list (it exits
|
|
# non-zero when the marker matches no file at all); the list rides
|
|
# to the runner via ``--files`` so an unrelated ImportError fails
|
|
# only ITS file, red and visible, without poisoning the lane's
|
|
# other files. ``-m`` stays authoritative for which TESTS run —
|
|
# passed after ``--`` so the runner routes it to every per-file
|
|
# pytest invocation (the flag REPLACES pyproject's addopts, hence
|
|
# repeating ``not integration``).
|
|
#
|
|
# ``--timeout-method`` needs no override: tests/conftest.py's
|
|
# pytest_configure already downgrades the signal-based timer on
|
|
# Windows, which has no SIGALRM.
|
|
shell: bash
|
|
run: |
|
|
set -uo pipefail
|
|
|
|
LIST="${RUNNER_TEMP:-.}/selected-tests.txt"
|
|
|
|
# Process substitution would hide the helper's exit status, so write
|
|
# to a file and check it explicitly.
|
|
if ! uv run --no-sync python scripts/ci/list_os_marked_tests.py \
|
|
"${{ matrix.marker }}" > "$LIST"; then
|
|
echo "::error::could not enumerate ${{ matrix.marker }} test files"
|
|
exit 1
|
|
fi
|
|
if [ ! -s "$LIST" ]; then
|
|
echo "::error::empty ${{ matrix.marker }} file list"
|
|
exit 1
|
|
fi
|
|
|
|
echo "selected file(s) for ${{ matrix.marker }}:"
|
|
cat "$LIST"
|
|
|
|
# Desktop-update hand-off integration tests spawn the real
|
|
# windows.ps1; deselect them unless the PR touched that surface
|
|
# (see the workflow_call input). ``--ignore-glob`` keeps the file
|
|
# list above intact, so a renamed test file still trips the
|
|
# zero-tests guard rather than silently vanishing.
|
|
# (bash 3.2 on the macOS runner: an empty array under ``set -u`` is
|
|
# an unbound-variable error, hence the ``${arr[@]+...}`` idiom.)
|
|
EXTRA_ARGS=()
|
|
if [ "${{ inputs.desktop_updater }}" != "true" ]; then
|
|
echo "desktop_updater lane off: skipping tests/test_desktop_update_windows_*.py"
|
|
EXTRA_ARGS+=(--ignore-glob='*test_desktop_update_windows_*.py')
|
|
fi
|
|
|
|
# ``tr -d '\r'``: on Windows the helper's redirected stdout gains
|
|
# CRLF line endings; a stray \r would corrupt the path. The list
|
|
# uses the native path-list separator consumed by --files.
|
|
#
|
|
# Any non-zero exit propagates red: real test failures, or the
|
|
# runner's own zero-run guard (every file filtered to empty by
|
|
# ``-m`` — "must never pass without running its OS's tests").
|
|
SEPARATOR="$(uv run --no-sync python -c 'import os, sys; sys.stdout.write(os.pathsep)')"
|
|
FILES="$(tr -d '\r' < "$LIST" | paste -sd "$SEPARATOR" -)"
|
|
scripts/run_tests.sh --files "$FILES" -- \
|
|
${EXTRA_ARGS[@]+"${EXTRA_ARGS[@]}"} \
|
|
-m "platforms and not integration" \
|
|
-v --tb=short
|
|
env:
|
|
# These files spawn nested PowerShell trees. CPU-count parallelism
|
|
# starves cold child startup before the fixture's idle deadline.
|
|
HERMES_TEST_WORKERS: ${{ matrix.marker == 'windows' && '8' || '' }}
|
|
# Belt-and-suspenders with tests/conftest.py's env blanking: no
|
|
# test may reach a real provider API.
|
|
OPENROUTER_API_KEY: ""
|
|
OPENAI_API_KEY: ""
|
|
NOUS_API_KEY: ""
|