Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ simply wrong, the most useful form is the observed value next to the expected on
**System information**
A clear and concise description of your system information.
- OS Version: [e.g. macOS 15.3, Ubuntu 24.04, Windows 11]
- Python Version: [e.g. 3.14, 3.12, 3.10]
- Python Version: [e.g. 3.15, 3.13, 3.11]
- Python Implementation: [e.g. CPython, PyPy]
- `pcapkit` Version: [`python -c "import pcapkit; print(pcapkit.__version__)"`]
- If you are working from a checkout rather than an installed release, please give
Expand Down
102 changes: 89 additions & 13 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ concurrency:
# artefact -- the tag it checks is the one it is about to create -- but ``tag``,
# ``pypi`` and ``conda`` now each read their own evidence instead: ``tag`` checks
# for the ``conda-<version>+0`` tag it creates, ``pypi`` checks PyPI's file count
# for the version against the 8 expected, and ``conda`` both checks Anaconda's
# for the version against the 7 expected, and ``conda`` both checks Anaconda's
# aggregate count as a job-level cost saver and, per leg, whether that leg's own
# platform/Python distribution is already there -- the correctness fix, since
# retrying an upload Anaconda already has fails outright rather than skipping
Expand Down Expand Up @@ -221,12 +221,14 @@ jobs:
with:
tag: "conda-${{ steps.get_version.outputs.PCAPKIT_VERSION }}+0"

# PyPI evidence: compare the file count for this version against the 8
# expected -- 7 matrix legs (``pypi``'s ``strategy.matrix.python-version``)
# PyPI evidence: compare the file count for this version against the 7
# expected -- the 6 supported legs (``pypi``'s ``strategy.matrix.python-version``)
# each producing a wheel, plus the sdist that the "No Source Distribution"
# step keeps only on the 3.14 leg. A release-level 200 only proves *some*
# file landed, not all of them, so presence alone -- ``PCAPKIT_TAG_EXISTS``'s
# mistake, repeated -- is not enough.
# mistake, repeated -- is not enough. Only the supported tier's wheels are
# counted (#1633): a KTLO leg's wheel neither completes the release nor,
# when that leg fails, leaves it incomplete for every later run to retry.
#
# The per-version endpoint's own ``releases`` mapping was empty on every
# request checked while writing this, including against a version that
Expand All @@ -251,7 +253,7 @@ jobs:
run: |
set -euo pipefail

expected=8
expected=7
# `--retry-connrefused` and the `|| status=000` fallback keep a
# transient pypi.org outage from failing this step outright: under
# `set -e` a bare curl failure here would fail `version_check`,
Expand Down Expand Up @@ -280,7 +282,10 @@ jobs:
"import json" \
"with open('/tmp/pypi_release.json') as f:" \
" data = json.load(f)" \
"print(len(data.get('urls', [])))" \
"supported = {'cp311', 'cp312', 'cp313', 'cp314', 'cp315', 'pp311'}" \
"names = [u.get('filename', '') for u in data.get('urls', [])]" \
"wheels = {n.split('-')[2] for n in names if n.endswith('.whl') and n.count('-') == 4}" \
"print(len(wheels & supported) + any(n.endswith('.tar.gz') for n in names))" \
| python3 -)"
fi

Expand Down Expand Up @@ -323,7 +328,7 @@ jobs:
"with open('/tmp/anaconda_release.json') as f:" \
" data = json.load(f)" \
"platforms = {'linux-64', 'osx-64'}" \
"py_tags = {'py310', 'py311', 'py312', 'py313', 'py314'}" \
"py_tags = {'py311', 'py312', 'py313', 'py314', 'py315'}" \
"seen = {" \
" (d.get('attrs', {}).get('subdir'), tag)" \
" for d in data.get('distributions', [])" \
Expand Down Expand Up @@ -547,7 +552,7 @@ jobs:
git push origin "conda-${{ needs.version_check.outputs.PCAPKIT_VERSION }}+0"

pypi:
name: PyPI distribution for Python ${{ matrix.python-version }}
name: PyPI distribution for Python ${{ matrix.python-version }}${{ matrix.ktlo && ' (KTLO)' || '' }}
runs-on: ubuntu-latest
# Named ``pypi`` rather than the upstream snippet's ``release``: see the note
# above ``jobs:`` for why each target gets its own. #641 was that this line
Expand Down Expand Up @@ -576,19 +581,35 @@ jobs:
needs.unit-tests.result == 'success' &&
(needs.github.result == 'success' || needs.github.result == 'skipped') &&
(startsWith(github.ref_name, 'v') || needs.version_check.outputs.PCAPKIT_PYPI_COMPLETE == 'false') }}
# #1633: each KTLO leg's own outcome, under a name only that leg sets --
# matrix job outputs are combined across legs -- so ``release_status`` can
# report it although it never reaches ``needs.pypi.result``.
outputs:
ktlo_310: ${{ steps.ktlo.outputs.ktlo_310 }}
ktlo_pypy310: ${{ steps.ktlo.outputs.ktlo_pypy310 }}
strategy:
## one leg failing must not cancel its siblings: each leg still has its own
## wheel to attach to the GitHub Release, and a cancelled leg never does
fail-fast: false
matrix:
# #1633: the supported tier, whose wheels version_check counts.
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
- "pypy3.10"
- "3.15"
- "pypy3.11"
# The KTLO tier: Python 3.10 language level, end-of-life, still built
# and published but never blocking. Each step that can fail on one
# carries `continue-on-error: ${{ matrix.ktlo == true }}`, the
# step-level form, so ``needs.pypi.result`` cannot see it -- see the
# same choice in unit-tests.yml's ``test`` job.
include:
- python-version: "3.10"
ktlo: true
- python-version: "pypy3.10"
ktlo: true
steps:
- name: Checkout code
uses: actions/checkout@v7
Expand All @@ -597,17 +618,23 @@ jobs:
fetch-depth: 0

- uses: actions/setup-python@v7
id: setup
continue-on-error: ${{ matrix.ktlo == true }}
with:
python-version: ${{ matrix.python-version }}

- name: Install and Setup
id: install
continue-on-error: ${{ matrix.ktlo == true }}
run: |
set -x

python -m pip install -U pip setuptools wheel
python -m pip install -U build

- name: Build Package
id: build
continue-on-error: ${{ matrix.ktlo == true }}
env:
PCAPKIT_VERSION: ${{ needs.version_check.outputs.PCAPKIT_VERSION }}
run: |
Expand All @@ -617,11 +644,15 @@ jobs:
python util/wheel_rename.py dist/*.whl

- name: No Source Distribution for Python ${{ matrix.python-version }}
id: no_sdist
if: ${{ matrix.python-version != '3.14' }}
continue-on-error: ${{ matrix.ktlo == true }}
run: |
rm dist/*.tar.gz

- name: Publish to PyPI
id: publish
continue-on-error: ${{ matrix.ktlo == true }}
uses: pypa/gh-action-pypi-publish@release/v1
with:
# password: ${{ secrets.PYPI_API_TOKEN }}
Expand All @@ -642,6 +673,8 @@ jobs:
attestations: false

- name: Upload package to GitHub Release
id: upload
continue-on-error: ${{ matrix.ktlo == true }}
uses: softprops/action-gh-release@v3.0.2
with:
# allowUpdates: true
Expand All @@ -650,6 +683,25 @@ jobs:
tag_name: "v${{ needs.version_check.outputs.PCAPKIT_VERSION }}"
token: "${{ secrets.GITHUB_TOKEN }}"

# #1633: what this KTLO leg's steps did before ``continue-on-error``,
# for ``release_status`` -- ``steps.*.outcome`` covers the steps with an
# ``id`` above, i.e. every one that can fail here except the TestPyPI
# upload, which no leg treats as a failure.
- name: Record this KTLO leg's outcome
id: ktlo
if: ${{ !cancelled() && matrix.ktlo == true }}
continue-on-error: ${{ matrix.ktlo == true }}
env:
LEG: ${{ matrix.python-version }}
OUTCOMES: ${{ join(steps.*.outcome, ' ') }}
run: |
case " $OUTCOMES " in
*" failure "*) result=failure ;;
*) result=success ;;
esac
echo "ktlo_$(printf '%s' "$LEG" | tr -d '.')=$result" >> "$GITHUB_OUTPUT"
echo "Python $LEG (KTLO, non-blocking): $result (step outcomes: $OUTCOMES)"

conda:
name: Conda deployment (release) on ${{ matrix.os }} with Python ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -685,12 +737,16 @@ jobs:
- macos-latest
- ubuntu-latest
# - windows-latest # skip for now
# #1633: the supported tier only. There is no KTLO 3.10 leg because it
# can only fail: conda/requirements.txt pins soupsieve==3.0.3
# (Requires-Python >=3.11.5), and util/conda-dist.py pins the newest
# release of every dependency, so that floor only rises.
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
- "3.15"
steps:
- uses: actions/checkout@v7
with:
Expand Down Expand Up @@ -800,13 +856,20 @@ jobs:
mkdir -p conda/wheels
python -m pip download -r conda/requirements.txt -d conda/wheels

# With `activate-environment: base`, `python-version` installs that Python
# into base, next to conda itself. conda-forge's conda has no py315 build
# yet, so on 3.15 that fails with "RemoveError: Conda cannot remove
# itself" (#1633, measured with conda 26.7.2). So 3.15 leaves base on its
# own Python: `conda build --python` below builds against 3.15 regardless,
# measured to build and import pypcapkit-*-py315_0. Drop the exception
# once conda publishes a py315 build.
- name: Conda environment creation and activation
uses: conda-incubator/setup-miniconda@v4.0.1
env:
PCAPKIT_VERSION: ${{ needs.version_check.outputs.PCAPKIT_VERSION }}
PCAPKIT_BUILD: "0"
with:
python-version: ${{ matrix.python-version }}
python-version: ${{ matrix.python-version != '3.15' && matrix.python-version || '' }}
activate-environment: base
auto-update-conda: false
auto-activate: true
Expand Down Expand Up @@ -934,9 +997,22 @@ jobs:
PCAPKIT_CONDA_TAG_EXISTS: ${{ needs.version_check.outputs.PCAPKIT_CONDA_TAG_EXISTS }}
PCAPKIT_PYPI_COMPLETE: ${{ needs.version_check.outputs.PCAPKIT_PYPI_COMPLETE }}
PCAPKIT_CONDA_COMPLETE: ${{ needs.version_check.outputs.PCAPKIT_CONDA_COMPLETE }}
PCAPKIT_PYPI_KTLO_310: ${{ needs.pypi.outputs.ktlo_310 }}
PCAPKIT_PYPI_KTLO_PYPY310: ${{ needs.pypi.outputs.ktlo_pypy310 }}
run: |
set -euo pipefail

# #1633: pypi's KTLO legs (3.10, pypy3.10) never reach
# needs.pypi.result -- their steps carry continue-on-error -- so
# nothing below can warn or fail because of one, and their wheels are
# not in PCAPKIT_PYPI_COMPLETE's count. Each leg records its own
# outcome as a job output instead, reported here as a notice.
ktlo_310="${PCAPKIT_PYPI_KTLO_310:-}"
ktlo_pypy310="${PCAPKIT_PYPI_KTLO_PYPY310:-}"
if [ -n "${ktlo_310}${ktlo_pypy310}" ]; then
echo "::notice title=KTLO legs (non-blocking)::pypi 3.10=${ktlo_310:-not run} pypy3.10=${ktlo_pypy310:-not run}. End-of-life Python, built and published but never blocking (#1633); neither result changes this run's verdict."
fi

# Case 1: version_check itself never ran. On a `workflow_run`
# trigger this means the triggering Vendor Update run did not
# conclude `success` -- most commonly because it found nothing to
Expand Down Expand Up @@ -1025,7 +1101,7 @@ jobs:
# Case 2: every target reconciled by skipping, none of them ran.
# Quiet -- this is the steady state most runs are in.
if [ "$all_reconciled" = "true" ] && [ "$all_skipped" = "true" ]; then
echo "::notice title=Nothing to release::pypcapkit ${PCAPKIT_VERSION} is already fully released -- the v* tag, the conda-*+0 tag, all 8 PyPI files and all 10 Anaconda files are present. This run correctly released nothing."
echo "::notice title=Nothing to release::pypcapkit ${PCAPKIT_VERSION} is already fully released -- the v* tag, the conda-*+0 tag, all 7 supported PyPI files and all 10 Anaconda files are present. This run correctly released nothing."
exit 0
fi

Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/cron-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -230,12 +230,14 @@ jobs:
- macos-latest
- ubuntu-latest
# - windows-latest # skip for now
# #1633: the supported tier; create-release.yml's ``conda`` job says why
# there is no KTLO 3.10 leg.
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
- "3.15"
steps:
- uses: actions/checkout@v7
with:
Expand Down Expand Up @@ -276,13 +278,14 @@ jobs:
mkdir -p conda/wheels
python -m pip download -r conda/requirements.txt -d conda/wheels

# See create-release.yml's step of the same name for the 3.15 exception.
- name: Conda environment creation and activation
uses: conda-incubator/setup-miniconda@v4.0.1
env:
PCAPKIT_VERSION: ${{ needs.conda-update.outputs.PCAPKIT_VERSION }}
PCAPKIT_BUILD: ${{ needs.conda-update.outputs.PCAPKIT_BUILD }}
with:
python-version: ${{ matrix.python-version }}
python-version: ${{ matrix.python-version != '3.15' && matrix.python-version || '' }}
activate-environment: base
auto-update-conda: false
auto-activate: true
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,16 +159,16 @@ jobs:
steps:
- uses: actions/checkout@v7

# One interpreter, not a matrix. Unit Tests sweeps 3.10-3.14 and Python
# Compatibility sweeps the same plus a schedule-only 3.15 leg, because
# One interpreter, not a matrix. Unit Tests and Python Compatibility sweep
# 3.11-3.15, plus the non-blocking KTLO legs 3.8-3.10 (#1633), because
# they check that the package *runs* everywhere; these tools read the
# source, and running them several times over would produce several
# copies of the same findings for several times the runner cost.
#
# 3.14 specifically, matching cron-vendor.yml, because it is the newest
# non-experimental version in the test matrix and the version the recorded
# counts above were measured on -- so a count that moves means the code
# moved, not the interpreter.
# 3.14 specifically, matching cron-vendor.yml, because it is the version
# the recorded counts above were measured on -- so a count that moves
# means the code moved, not the interpreter. 3.15 is newer, and supported
# since #1633, but moving to it would re-baseline every count above.
- uses: actions/setup-python@v7
with:
python-version: '3.14'
Expand Down
58 changes: 23 additions & 35 deletions .github/workflows/python-compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,37 @@ jobs:
# exactly with the Unit Tests matrix job of the same name: `gh pr checks`
# showed two rows reading `Python 3.10`, one a 13-second import smoke test
# and one a three-minute test run, with nothing to tell them apart.
name: Compat Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
#
# #1633: two tiers. The supported tier, 3.11-3.15, keeps the plain name,
# which is what ruleset 23497679 requires one context per version of. The
# KTLO tier -- end-of-life versions that still run, 3.8-3.10 -- is suffixed
# ` (KTLO)`, so no required context can ever match one, and carries
# `continue-on-error`, so a failed leg shows red without failing the run.
# 3.6 and 3.7 are left out: neither can install and import pcapkit (see the
# note on `requires-python` in pyproject.toml).
name: Compat Python ${{ matrix.python-version }}${{ matrix.ktlo && ' (KTLO)' || '' }}
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
continue-on-error: ${{ matrix.ktlo == true }}
strategy:
fail-fast: false
matrix:
python-version:
# 3.15 deliberately excluded: ruleset 23497679's required checks
# only cover 3.10-3.14, so it cannot block a merge here either. Its
# replacement is the `compatibility-nightly` job below, which runs
# only on this workflow's existing weekly schedule.
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
- "3.15"
# setup-python publishes 3.8 and 3.9 builds up to Ubuntu 24.04 only, and
# `ubuntu-latest` moves to 26.04 from 2026-10-19, hence the pin.
include:
- python-version: "3.8"
ktlo: true
os: ubuntu-24.04
- python-version: "3.9"
ktlo: true
os: ubuntu-24.04
- python-version: "3.10"
ktlo: true
steps:
- uses: actions/checkout@v7

Expand All @@ -59,31 +75,3 @@ jobs:
run: |
python -m compileall -q pcapkit
python -c 'import pcapkit; print(pcapkit.__version__)'

# 3.15 is not in ruleset 23497679's required-checks list, so running it on
# every push/PR was queue contention with no gating signal (it was already
# `continue-on-error`). This job keeps early warning of a 3.15-only
# regression -- import and compileall only, ~14s -- without paying for it on
# every push: it fires solely on the schedule trigger above (line 9).
compatibility-nightly:
name: Compat Python 3.15 (scheduled)
if: ${{ github.event_name == 'schedule' }}
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v7

- uses: actions/setup-python@v7
with:
python-version: "3.15"
allow-prereleases: true

- name: Install package
run: |
python -m pip install -U pip setuptools wheel
python -m pip install -e .

- name: Verify package
run: |
python -m compileall -q pcapkit
python -c 'import pcapkit; print(pcapkit.__version__)'
Loading
Loading