Every list of three or more takes the serial comma: the one-liner in
pyproject.toml, meshterm/__init__.py, and --help ("USB, Bluetooth, or TCP",
"Windows, macOS, and Linux"), the README, the changelog, the manuals, the
issue and PR templates, and six strings the app shows (the region-name
rule, a repeater setting's help, the diagnostics tool's description, and
the archive confirms). The Windows-console notice is rewrapped so its
comma keeps it inside 72 columns.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
123 lines
4.5 KiB
YAML
123 lines
4.5 KiB
YAML
# The three commands CONTRIBUTING.md asks a contributor to run, run for them.
|
|
#
|
|
# Split in two on purpose. Style is a property of the source, not of the machine, so
|
|
# `lint` runs once and its failure is unambiguous. Behaviour is a property of the
|
|
# machine, so `test` runs everywhere we claim to work — the description promises
|
|
# Windows, macOS, and Linux, and until this file existed that promise had only ever been
|
|
# checked on one of them.
|
|
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
workflow_dispatch:
|
|
|
|
# Nothing here writes to the repository; say so rather than inherit the default.
|
|
permissions:
|
|
contents: read
|
|
|
|
# A second push while the first is still running makes the first answer obsolete.
|
|
# Cancel it — a run on a stale commit is a queue slot and a wait spent on nothing.
|
|
concurrency:
|
|
group: ci-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
lint:
|
|
name: lint
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.14"
|
|
cache: pip
|
|
cache-dependency-path: pyproject.toml
|
|
|
|
- run: pip install -e ".[dev]"
|
|
|
|
# Both gates, and the second runs even when the first fails: they report
|
|
# different things and seeing only the earlier one wastes a round trip.
|
|
- run: ruff check .
|
|
- if: ${{ !cancelled() }}
|
|
run: ruff format --check .
|
|
|
|
test:
|
|
name: test ${{ matrix.os }} py${{ matrix.python }}
|
|
runs-on: ${{ matrix.os }}
|
|
timeout-minutes: 15
|
|
strategy:
|
|
# Let every leg finish. The point of a run is the whole picture of what broke,
|
|
# and one red leg cancelling its siblings hides the rest until the next push.
|
|
fail-fast: false
|
|
matrix:
|
|
# Linux carries the full Python range and the slower-to-schedule runners
|
|
# cover the ends of it, which is where a version-dependent break actually
|
|
# shows up. It was shaped this way when the repository was private and
|
|
# minutes were metered; on a public one they are not, so this can widen to
|
|
# every OS times every version whenever the wait for a full run is worth it.
|
|
#
|
|
# 3.10 stays on Ubuntu 24.04. It reaches end of life in October 2026, and a
|
|
# newer image that ubuntu-latest moves to may not offer it at all. That would
|
|
# turn the badge red with nothing broken. Drop this leg when the floor rises.
|
|
include:
|
|
- { os: ubuntu-24.04, python: "3.10" }
|
|
- { os: ubuntu-latest, python: "3.11" }
|
|
- { os: ubuntu-latest, python: "3.12" }
|
|
- { os: ubuntu-latest, python: "3.13" }
|
|
- { os: ubuntu-latest, python: "3.14" }
|
|
- { os: windows-latest, python: "3.10" }
|
|
- { os: windows-latest, python: "3.14" }
|
|
- { os: macos-latest, python: "3.14" }
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: ${{ matrix.python }}
|
|
cache: pip
|
|
cache-dependency-path: pyproject.toml
|
|
|
|
- run: pip install -e ".[dev]"
|
|
|
|
# Includes tests/test_gallery.py, the dual-platform readability gate: every
|
|
# screen has to stay readable at 72 columns regular and 53 on the PicoCalc, and
|
|
# every PicoCalc case is a hard failure rather than a warning.
|
|
- run: python -m pytest -q
|
|
|
|
# Builds the distributions and checks them the way PyPI would, publishing nothing.
|
|
# Worth its minute: packaging breaks silently and is otherwise only discovered on
|
|
# the one day you least want to discover it. The wheel is installed into a clean
|
|
# environment and the command run from outside the source tree, because "it built"
|
|
# and "it works when installed" are different claims.
|
|
build:
|
|
name: build
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.14"
|
|
|
|
- run: pip install build twine
|
|
|
|
- run: python -m build
|
|
|
|
- run: twine check dist/*
|
|
|
|
- name: Install the wheel clean and run the command
|
|
run: |
|
|
python -m venv /tmp/fresh
|
|
/tmp/fresh/bin/pip install --quiet dist/*.whl
|
|
cd /tmp
|
|
/tmp/fresh/bin/meshterm --help > /dev/null
|
|
# A tool-provided subcommand, not just --help: the tools are discovered at
|
|
# runtime, so a broken package still answers --help perfectly well.
|
|
/tmp/fresh/bin/meshterm contacts --help > /dev/null
|
|
echo "the wheel installs, the console script runs, and its tools registered"
|