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
5 changes: 5 additions & 0 deletions docs/source/_static/mermaid.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* Centre a Mermaid diagram narrower than the page; conf.py sizes each to fit its content. */
pre.mermaid {
margin-left: auto;
margin-right: auto;
}
14 changes: 14 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,20 @@

ogp_site_url = "https://pypcapkit.jarryshaw.me/"

# Mermaid diagrams render at their natural size and shrink only to fit the page
# width. The ``sphinxcontrib-mermaid`` defaults -- ``mermaid_height = '500px'`` with
# the SVG stretched to the full page width -- fit every diagram into the same fixed
# box, so a tall class hierarchy shrank until its text could not be read and its
# edges ran together. ``useMaxWidth`` has to be off as well, or Mermaid writes
# ``width="100%"`` on the SVG in place of its own size and ``fit-content`` has
# nothing to size the box from. ``startOnLoad`` repeats the plugin's default, which
# this setting replaces wholesale. ``_static/mermaid.css`` centres a diagram
# narrower than the page, as the full-width box did.
mermaid_width = 'fit-content'
mermaid_height = 'auto'
mermaid_init_config = {'startOnLoad': False, 'flowchart': {'useMaxWidth': False}}
html_css_files = ['mermaid.css']


def maybe_skip_member(app: 'Sphinx', what: str, name: str, # pylint: disable=unused-argument
obj: 'Any', skip: bool, options: 'Dict[str, Any]') -> bool: # pylint: disable=unused-argument
Expand Down
33 changes: 22 additions & 11 deletions docs/source/contributing/conventions/documentation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -107,25 +107,36 @@ would otherwise inflate both counts:
.. code-block:: shell

grep -rn 'mermaid::' docs/source --include='*.rst' \
--exclude='documentation.rst' | wc -l # 12 directives
--exclude='documentation.rst' | wc -l # directives
grep -rl 'mermaid::' docs/source --include='*.rst' \
--exclude='documentation.rst' | wc -l # on 11 pages

All twelve are a bare ``.. mermaid::`` carrying no directive options, and all twelve
are a ``flowchart``: ``TD`` where the subject is a sequence or a decision, ``LR`` for a
type hierarchy. **Quote a node label whose text Mermaid would otherwise try to parse**,
and leave a bare identifier bare. That is what the exemplars do with *node* labels, and
it falls close to the ``TD``/``LR`` line without following it: every node label in the
six ``TD`` graphs is quoted, since every one of them is prose; the ``LR`` graphs quote
only where the text forces it, which is ``h1["HTTP/1.*"]`` and ``h2["HTTP/2"]`` in
--exclude='documentation.rst' | wc -l # pages carrying one

Every one is a bare ``.. mermaid::`` carrying no directive options, and every one is a
``flowchart``: ``LR`` for a type hierarchy, and for a graph whose ``TD`` layout would run
wider than the page or pile its edge labels on top of each other -- a decision with many
outcomes, a trigger map -- and ``TD`` for the other sequences and decisions. **Quote a
node label whose text Mermaid would otherwise try to parse**, and leave a bare identifier
bare. That is what the exemplars do with *node* labels, and it follows the subject rather
than the direction: every node label in the sequence, decision and trigger graphs is
quoted, since every one of them is prose; the type hierarchies quote only where the text
forces it, which is ``h1["HTTP/1.*"]`` and ``h2["HTTP/2"]`` in
:file:`docs/source/pcapkit/protocols/index.rst` and nowhere else. Everything else there
uses the bare ``A{{Meta}}``, ``B(Base)``, ``D([user customisation ...])`` and
``subgraph name [Title]`` forms, and spends its quotes on ``click`` targets instead.
**Write a** ``click`` **target relative to the page it is on** -- ``protocol.html#...``,
``../../ext.html#...`` -- never from the site root: the published site is served under
``/PyPCAPKit/``, so a target starting with ``/`` resolves outside it and 404s.
**Edge labels follow no rule at all.** The exemplars quote some and leave others bare,
including ``|workflow_run: completed|`` and ``|02:00|``, which the reason above would
have quoted and which Mermaid accepts anyway -- so match the graph being edited rather
than this paragraph. Inside a quoted label, ``<br/>`` breaks the text across lines and a
literal ``<`` or ``>`` is written ``&lt;`` or ``&gt;``.
literal ``<`` or ``>`` is written ``&lt;`` or ``&gt;``. Mermaid also reads label text as
Markdown, so an underscore that opens or closes a name is written ``\_`` --
``\_missing\_``, ``pcapkit.\_\_version\_\_`` -- or the name renders as emphasis. Its
own ``#95;`` escape is no substitute: in a label that carries a ``<br/>`` it is decoded
before the Markdown pass, and the emphasis comes back. Where a steep edge runs under
a neighbouring edge label, ``curve: monotoneX`` in the graph's front matter keeps it
clear, as the mint criterion's decision graph does.
:doc:`The release pipeline </contributing/releasing>` is the ``TD`` example and
:doc:`the field hierarchy </pcapkit/corekit/fields/index>` the ``LR`` one.

Expand Down
11 changes: 8 additions & 3 deletions docs/source/contributing/conventions/mint-criterion.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,16 @@ and :file:`pcapkit/vendor/ipx/socket.py`'s ``UNASSIGNED_RANGE_NAMES``.

.. mermaid::

flowchart TD
LOOKUP["cls(value) finds no member"] --> MISSING["_missing_"]
---
config:
flowchart:
curve: monotoneX
---
flowchart LR
LOOKUP["cls(value) finds no member"] --> MISSING["\_missing\_"]
MISSING -->|"out of range, or no block covers it"| RAISE["ValueError"]
MISSING -->|"CGAType only"| MINT["extend_enum<br/>permanent member, registry grows"]
MISSING -->|"flag registries"| FLAG["super()._missing_<br/>pseudo-member cached in the value table only"]
MISSING -->|"flag registries"| FLAG["super().\_missing\_<br/>pseudo-member cached in the value table only"]
MISSING -->|"block label names a party"| SUFFIXED["_unregistered_member<br/>label + _0x&lt;code&gt;"]
MISSING -->|"block label names a procedure"| BARE["_unregistered_member<br/>bare block label"]

Expand Down
9 changes: 3 additions & 6 deletions docs/source/contributing/conventions/registry-protocol.rst
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,11 @@ Which branch a key takes, and what each miss ends in:
.. mermaid::

flowchart TD
GET["get(key, default)"] -->|"isinstance(key, str)"| NAME["_member_map_ lookup"]
GET["get(key, default)"] -->|"isinstance(key, str)"| NAME["\_member\_map\_ lookup"]
GET -->|"otherwise"| VALUE["_validate_value(key)<br/>then cls(key)"]
NAME -->|"hit"| OK["canonical member"]
NAME -->|"miss, key is a registered value"| OK
NAME -->|"miss, default is a registered value"| OK
NAME -->|"miss, no usable default"| KEYERR["EnumKeyError<br/>quiet, derives KeyError"]
VALUE -->|"resolves, or _missing_ answers"| OK
VALUE -->|"ValueError, default is a registered value"| OK
NAME -->|"hit,<br/>or miss, key is a registered value,<br/>or miss, default is a registered value"| OK["canonical member"]
VALUE -->|"resolves, or \_missing\_ answers,<br/>or ValueError, default is a registered value"| OK
VALUE -->|"ValueError, no usable default"| VALERR["EnumValueError<br/>loud, derives ValueError"]

Do not "improve" on the shape by making both misses report identically. Converting
Expand Down
6 changes: 3 additions & 3 deletions docs/source/contributing/releasing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -416,20 +416,20 @@ The Release Pipeline
flowchart TD
T1["push: tags v*<br/>ref_name starts with v"] --> VC
T2["workflow_run: Vendor Update completed<br/>ref_name = main"] --> VC
VC["version_check -- reads pcapkit.__version__<br/>computes each job's own evidence<br/>no gate, read-only"]
VC["version_check -- reads pcapkit.\_\_version\_\_<br/>computes each job's own evidence<br/>no gate, read-only"]
VC --> UT
UT["unit-tests -- Release test gate<br/>skipped when nothing is publishable"] --> GH
GH["github -- GitHub Release<br/>creates/attaches the v* tag<br/>THE ONLY APPROVAL"]
GH --> TG["tag -- Conda Tag<br/>conda-&lt;version&gt;+0 tag at github.sha<br/>then conda/build reset on main"]
GH --> PY["pypi -- build + publish<br/>OIDC trusted publishing"]
TG --> CD["conda -- build + upload<br/>matrix: 2 OS, per-leg evidence"]
GH --> CD
UT --> RS
VC --> RS
UT --> RS
GH --> RS
TG --> RS
PY --> RS
CD --> RS
PY --> RS
RS["release_status -- always runs<br/>reports why nothing released, or that it did"]
classDef gate stroke-dasharray:6 3,stroke-width:2px
class GH gate
Expand Down
115 changes: 66 additions & 49 deletions docs/source/contributing/workflows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,40 +128,39 @@ The Graph
---------

Solid arrows are direct triggers (``push``, ``pull_request``, ``schedule``,
``workflow_dispatch``). Thick arrows are ``workflow_run`` edges -- a
``workflow_dispatch``, ``issues``, ``pull_request_target``), drawn in the first
graph with a node for each schedule day. The second graph is the edges from one
workflow to another. Thick arrows are ``workflow_run`` edges -- a
*separate* run, started only after the upstream workflow's run has fully
completed. Dotted arrows are ``uses:`` edges -- a reusable-workflow call that
runs synchronously *inside* the caller's own run, sharing its run id rather
than starting a new one.

.. mermaid::

flowchart TD
flowchart LR
PUSH["push: main"]
PR["pull_request: main"]
TAG["push: tags v*"]
SCHED["schedule (Saturday, Monday, or daily)"]
SAT["schedule: Saturday"]
MON["schedule: Monday"]
DISPATCH["workflow_dispatch"]
DAILY["schedule: daily"]
ISSUES["issues"]
PRT["pull_request_target"]
DISPATCH["workflow_dispatch"]
TAG["push: tags v*"]

CQ["CodeQL<br/>codeql-analysis.yml"]
UT["Unit Tests<br/>unit-tests.yml"]
EX["Experimental Tests<br/>experimental.yml"]
PC["Python Compatibility<br/>python-compatibility.yml"]
CQ["CodeQL<br/>codeql-analysis.yml"]
GP["GitHub Pages<br/>deploy-pages.yml"]
CU["Conda Update<br/>cron-conda.yml"]
LT["Lint<br/>lint.yml"]
VU["Vendor Update<br/>cron-vendor.yml"]
CU["Conda Update<br/>cron-conda.yml"]
GP["GitHub Pages<br/>deploy-pages.yml"]
CR["Create Release<br/>create-release.yml"]
PS["Project Status<br/>project-status.yml"]
CC["Coverage Comment<br/>coverage-comment.yml"]
CT["CI Timing<br/>ci-timing.yml"]
PV["Python Versions<br/>cron-python-versions.yml"]

PUSH --> CQ
PR --> CQ
SCHED -->|02:00| CQ
CT["CI Timing<br/>ci-timing.yml"]
PS["Project Status<br/>project-status.yml"]
CR["Create Release<br/>create-release.yml"]

PUSH --> UT
PR --> UT
Expand All @@ -171,57 +170,75 @@ than starting a new one.

PUSH --> PC
PR --> PC
SCHED -->|04:00| PC
SAT -->|04:00| PC

PUSH --> CQ
PR --> CQ
SAT -->|02:00| CQ

PR --> GP
SAT -->|02:00| GP

SAT -->|10:00| CU

PR --> LT
SCHED -->|06:00| LT
SAT -->|06:00| LT
DISPATCH --> LT

PR --> GP
SCHED -->|02:00| GP
SAT -->|10:00| VU

SCHED -->|10:00| VU
SCHED -->|10:00| CU
MON -->|05:41| PV
DISPATCH --> PV

TAG --> CR
MON -->|07:23| CT
DISPATCH --> CT

DISPATCH --> PS
DAILY -->|00:37| PS
ISSUES --> PS
PRT --> PS
SCHED -->|daily 00:37| PS
DISPATCH --> PS

SCHED -->|Mon 07:23| CT
DISPATCH --> CT
TAG --> CR

SCHED -->|Mon 05:41| PV
DISPATCH --> PV
classDef trig fill:none,stroke-dasharray:2 2
class PUSH,PR,SAT,MON,DISPATCH,DAILY,ISSUES,PRT,TAG trig

.. mermaid::

flowchart LR
UT["Unit Tests<br/>unit-tests.yml"]
VU["Vendor Update<br/>cron-vendor.yml"]
CU["Conda Update<br/>cron-conda.yml"]
GP["GitHub Pages<br/>deploy-pages.yml"]
CC["Coverage Comment<br/>coverage-comment.yml"]
CR["Create Release<br/>create-release.yml"]

UT ==>|workflow_run: completed| VU
UT ==>|workflow_run: completed| CU
UT ==>|workflow_run: completed| GP
VU ==>|workflow_run: completed| CR
UT ==>|workflow_run: completed| CC

VU -.->|uses: gate-only| UT
CU -.->|uses: gate-only| UT
GP -.->|uses: gate-only| UT
CR -.->|uses: gate-only| UT

classDef trig fill:none,stroke-dasharray:2 2
class PUSH,PR,TAG,SCHED,DISPATCH,ISSUES,PRT trig

The double relationship between **Unit Tests** and the four dependants that
also call it is the
part that reading a single file cannot show, and it is deliberate rather than
redundant: each of the four calls ``unit-tests.yml`` with ``gate-only: true``
itself (dotted, above) **only when its own trigger is not** ``workflow_run``
(their gate jobs' ``if:`` guards this explicitly) -- i.e. only on the
Saturday ``schedule`` path, where no completed Unit Tests run exists yet to
read a verdict from. On the ``workflow_run`` path (thick arrows), each reads
the verdict Unit Tests already reached instead of re-running the gate a second
time. Vendor Update, Conda Update and GitHub Pages act on it only once their
``verdict`` job, described under the edge list below, says it is a verdict on
the commit they build.
VU -.-> UT
CU -.-> UT
GP -.-> UT
CR -.-> UT

The double relationship between **Unit Tests** and the dependants that also
call it is the part that reading a single file cannot show, and it is
deliberate rather than redundant. Vendor Update, Conda Update and GitHub Pages
call ``unit-tests.yml`` with ``gate-only: true`` themselves (dotted, above)
**only when their own trigger is** ``schedule`` -- their gate jobs' ``if:``
skips ``workflow_run``, and GitHub Pages' skips ``pull_request`` as well -- so
only on the Saturday path, where no completed Unit Tests run exists yet to read
a verdict from. On the ``workflow_run`` path (thick arrows), each reads the
verdict Unit Tests already reached instead of re-running the gate a second
time, and acts on it only once its ``verdict`` job, described under the edge
list below, says it is a verdict on the commit it builds. Create Release has
no ``schedule``, and its gate job's ``if:`` reads ``version_check``'s evidence
rather than the event, so its gate runs on both of its triggers: on a ``v*``
tag push, and on a ``workflow_run`` from Vendor Update when some target is not
yet published (:doc:`releasing` gives the conditions).

``workflow_run`` Edges
----------------------
Expand Down
Loading
Loading