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
6 changes: 3 additions & 3 deletions docs/source/contributing/releasing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Editing ``__version__`` by hand without also moving :file:`CITATION.cff`'s
asserts the two agree, and ``create-release.yml``'s ``unit-tests`` job calls
``unit-tests.yml`` with
``gate-only: true``, which runs the **full** suite rather than the tiered
subset an ordinary push runs (its ``gate`` job, ``unit-tests.yml:1798-1873``).
subset an ordinary push runs (its ``gate`` job).
That gate runs whenever a release will -- a moved ``__version__`` is exactly
what makes ``version_check``'s evidence read "not yet published" -- and every
publishing job's ``needs:`` requires it to have succeeded, so a
Expand All @@ -62,8 +62,8 @@ Either run the script, or move both fields by hand in the same commit.
This is the **only** edit a person makes to get a release started -- the tag,
the Release, and every upload are the workflow's job from here. Other
checks run unconditionally on the same path, though none is a step in *this*
process: the ``changelog`` job (``unit-tests.yml:1641-1667``) fails outright if
``CHANGELOG.md`` has drifted from its source entry under
process: the ``changelog`` job's "Check CHANGELOG.md is in step with its source
entry" step fails outright if ``CHANGELOG.md`` has drifted from its source entry under
:file:`docs/source/changelog/`, and ``github``'s "Select release body" step
warns, without failing, if that entry's heading still reads "unreleased". Each
enforces what the changelog convention
Expand Down
37 changes: 18 additions & 19 deletions docs/source/contributing/workflows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,8 @@ the same commit several times over. It also skips ``changes``,
`Project Pins`_ below), and ``required-checks`` (see `Required Status
Checks`_ below), which are gated out by their own ``if:`` rather than by having
already run. ``changelog`` carries no
``if:`` at all and runs on every path regardless -- deliberately, per its own
comment (``unit-tests.yml:1629-1635``): ``create-release.yml`` feeds
``if:`` at all and runs on every path regardless -- deliberately, per the comment
above the job: ``create-release.yml`` feeds
``CHANGELOG.md`` to the GitHub Release body, so the release path is exactly
where a drifted file must not go unchecked. Confirmed on run `36210743295
<https://github.com/JarryShaw/PyPCAPKit/actions/runs/36210743295>`__ (a
Expand Down Expand Up @@ -688,7 +688,7 @@ grepping every workflow file for its name:
- Where
* - ``Required checks passed``
- job ``required-checks``
- ``unit-tests.yml:1964``
- ``unit-tests.yml``
* - ``Compat Python 3.11``
- job ``compatibility``, matrix leg ``3.11``
- ``python-compatibility.yml``, ``.github/python-versions.toml:20``
Expand All @@ -709,23 +709,22 @@ That is the list after `#1633 <https://github.com/JarryShaw/PyPCAPKit/issues/163
which added ``Compat Python 3.15`` and removed ``Compat Python 3.10``. Moving a version
between the tiers below means editing the ruleset in the same change.

``Required checks passed`` is defined by exactly one job
(``unit-tests.yml:1964``). ``Compat Python`` is defined by one, the
``name:`` of the ``compatibility`` job in ``python-compatibility.yml``, whose
``Required checks passed`` is defined by exactly one, the ``name:`` of the
``required-checks`` job in ``unit-tests.yml``. ``Compat Python`` is defined by
one, the ``name:`` of the ``compatibility`` job in ``python-compatibility.yml``, whose
``matrix`` comes from :file:`.github/python-versions.toml`: the required legs
are its supported tier (``python-versions.toml:20-24``), and its KTLO legs
(``python-versions.toml:16-19``) have names ending in ``(KTLO)``, so no required
context matches one. A comment at
``unit-tests.yml:1876`` mentions the string without defining it.
context matches one. The comment above ``required-checks`` mentions the string
without defining it.

``Required checks passed`` is an aggregate, not a single check run. The ruleset
requires it and the ``Compat Python 3.11``-``3.15`` legs, nothing else, as
``unit-tests.yml``'s own comment (``unit-tests.yml:1875-1885``) records. It
``needs:`` the gating jobs of ``unit-tests.yml`` -- ``test``, ``integration``,
``engine-tests`` and ``pypcap-parity``, plus ``project-tests``, the one run of
the project pins, and ``changes`` for the docs-only path -- and checks each
result explicitly
(``unit-tests.yml:1963-2019``). Rulesets match check names literally, with no
that comment records. It ``needs:`` the gating jobs of ``unit-tests.yml`` --
``test``, ``integration``, ``engine-tests`` and ``pypcap-parity``, plus
``project-tests``, the one run of the project pins, and ``changes`` for the
docs-only path -- and checks each result explicitly, in its "Check every
required job succeeded" step. Rulesets match check names literally, with no
wildcard, so requiring the matrix cells one by one would mean editing the
ruleset whenever a matrix changes; the aggregate keeps the required list fixed.
It accepts the gating jobs as ``skipped`` only on a docs-only pull request, where
Expand All @@ -734,8 +733,8 @@ It accepts the gating jobs as ``skipped`` only on a docs-only pull request, wher
``python-compatibility.yml``, and a job cannot ``needs:`` a job in another
workflow file. This job runs on Unit Tests' own ``push``/``pull_request``
triggers; the ``gate-only: true`` reusable calls documented above skip it via
``if: ${{ always() && inputs.gate-only != true }}`` (``unit-tests.yml:1966``),
so it is never produced -- and never expected -- on those paths.
its ``if: ${{ always() && inputs.gate-only != true }}``, so it is never
produced -- and never expected -- on those paths.

.. _ktlo-tier:

Expand All @@ -758,9 +757,9 @@ and keeps its legs' check names, ``Python 3.10 (KTLO)`` and so on. In
with supported legs. Every job holding KTLO legs works the same way:

* Every step that can fail on a KTLO leg has an ``id`` and
``continue-on-error: ${{ matrix.ktlo == true }}`` (``unit-tests.yml:394-411``),
unless it is masked on every leg, as the timings uploads are. Its ``outcome`` is
then ``failure`` but its ``conclusion`` ``success``, so the job, its check run
``continue-on-error: ${{ matrix.ktlo == true }}`` (see the comment above
``test-ktlo``), unless it is masked on every leg, as the timings uploads are.
Its ``outcome`` is then ``failure`` but its ``conclusion`` ``success``, so the job, its check run
and ``required-checks`` read ``success``. A step that needs an earlier one's work
waits on that step's ``outcome``: a leg that cannot install stops there, and a
``pypi`` leg stops at its first failure.
Expand Down
50 changes: 2 additions & 48 deletions tests/project/test_unit_tests_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -605,30 +605,10 @@ def test_the_scan_is_not_blind(self) -> None:
self.assertIn('tests.corekit.test_sentinel_exports_unit', docs_readers())


#: Every ``unit-tests.yml:N`` or ``unit-tests.yml:N-M`` citation in these documents,
#: as (text the cited first line must contain, text the last line must contain).
#: A citation the table does not account for fails, and so does a table entry no
#: citation matches -- so moving a job without renumbering the prose fails here.
CITATIONS: 'dict[str, list[tuple[str, Optional[str]]]]' = {
'docs/source/contributing/releasing.rst': [
(' gate:', 'run: python util/cap_file_size.py python -m pytest -q -n auto --dist load'),
(' changelog:', 'fi'),
],
'docs/source/contributing/workflows.rst': [
('# Deliberately *not* gated on ``gate-only``', '# this is neither expensive'),
('name: Required checks passed', None),
('and a `Compat Python` one per supported version', None),
("# Ruleset 23497679's required_status_checks", '# them.'),
(' required-checks:', 'echo "All required jobs reported an accepted result."'),
('if: ${{ always() && inputs.gate-only != true }}', None),
('# The KTLO tier (#1633)', '`ubuntu-latest` moves to 26.04 from 2026-10-19.'),
],
}


#: The workflows the prose cites by job and step name, never by line number:
#: trimming a comment in one moves every line below it (#1967).
BY_NAME = ('create-release.yml', 'python-compatibility.yml', 'coverage-comment.yml')
BY_NAME = ('unit-tests.yml', 'create-release.yml', 'python-compatibility.yml',
'coverage-comment.yml')

#: A document that names one of :data:`BY_NAME` may still cite another file bare,
#: as ``:N``; each such citation is listed here with the file it does cite, and
Expand Down Expand Up @@ -676,31 +656,5 @@ def test_no_document_cites_them_by_line_number(self) -> None:
self.assertEqual(elsewhere - bare, set(), f'{document} no longer cites these bare')


class TestDocCitationsResolve(unittest.TestCase):
"""The prose's ``unit-tests.yml`` line numbers land where it says."""

def test_every_citation_lands_on_its_anchor(self) -> None:
"""Each ``unit-tests.yml`` citation's range starts and ends on the line its anchor names."""
lines = WORKFLOW.read_text(encoding='utf-8').splitlines()
for document in documents():
text = (ROOT / document).read_text(encoding='utf-8')
cited = [(int(m.group(1)), int(m.group(2) or m.group(1)))
for m in re.finditer(r'unit-tests\.yml:(\d+)(?:-(\d+))?', text)]
anchors = CITATIONS.get(document, [])
unmatched = list(anchors)
for first, last in cited:
with self.subTest(document=document, citation=f'{first}-{last}'):
self.assertLessEqual(last, len(lines))
hit = next((a for a in anchors if a[0] in lines[first - 1]
and (a[1] is None or a[1] in lines[last - 1])), None)
self.assertIsNotNone(
hit, f'{document} cites unit-tests.yml:{first}-{last}, which is '
f'{lines[first - 1].strip()!r} .. {lines[last - 1].strip()!r}')
if hit in unmatched:
unmatched.remove(hit)
with self.subTest(document=document):
self.assertFalse(unmatched, f'{document}: no citation lands on {unmatched}')


if __name__ == '__main__':
unittest.main()
Loading