netprotocols 1.3.0__tar.gz → 2.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. netprotocols-2.2.0/.github/workflows/ci.yml +114 -0
  2. netprotocols-2.2.0/.github/workflows/fuzz.yml +64 -0
  3. netprotocols-2.2.0/.github/workflows/release.yml +61 -0
  4. {netprotocols-1.3.0 → netprotocols-2.2.0}/.gitignore +8 -0
  5. netprotocols-2.2.0/ARCHITECTURE.md +476 -0
  6. netprotocols-2.2.0/CHANGELOG.md +952 -0
  7. netprotocols-2.2.0/PKG-INFO +446 -0
  8. netprotocols-2.2.0/README.md +421 -0
  9. netprotocols-2.2.0/benchmarks/baseline.json +13 -0
  10. netprotocols-2.2.0/docs/CLAIMS.md +809 -0
  11. {netprotocols-1.3.0 → netprotocols-2.2.0}/pyproject.toml +10 -1
  12. netprotocols-2.2.0/scripts/benchmark.py +449 -0
  13. netprotocols-2.2.0/scripts/benchmark_encode.py +107 -0
  14. netprotocols-2.2.0/scripts/benchmark_import.py +63 -0
  15. netprotocols-2.2.0/scripts/pyodide/check_in_pyodide.py +137 -0
  16. netprotocols-2.2.0/scripts/pyodide/package-lock.json +53 -0
  17. netprotocols-2.2.0/scripts/pyodide/package.json +9 -0
  18. netprotocols-2.2.0/scripts/pyodide/run_in_pyodide.mjs +44 -0
  19. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/__init__.py +54 -3
  20. netprotocols-2.2.0/src/netprotocols/_base.py +241 -0
  21. netprotocols-2.2.0/src/netprotocols/_defaults.py +105 -0
  22. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/checksum.py +9 -3
  23. netprotocols-2.2.0/src/netprotocols/flow.py +96 -0
  24. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/layer2/arp.py +68 -18
  25. netprotocols-2.2.0/src/netprotocols/layer2/ethernet.py +108 -0
  26. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/layer2/vlan.py +37 -10
  27. netprotocols-2.2.0/src/netprotocols/layer3/__init__.py +28 -0
  28. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/layer3/gre.py +23 -7
  29. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/layer3/icmp.py +69 -5
  30. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/layer3/igmp.py +28 -5
  31. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/layer3/ip.py +222 -77
  32. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/layer3/ipv6_ext.py +153 -25
  33. netprotocols-2.2.0/src/netprotocols/layer4/__init__.py +4 -0
  34. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/layer4/_ports.py +30 -15
  35. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/layer4/tcp.py +51 -11
  36. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/layer4/udp.py +6 -4
  37. netprotocols-2.2.0/src/netprotocols/layer7/__init__.py +20 -0
  38. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/layer7/dhcp.py +154 -8
  39. netprotocols-2.2.0/src/netprotocols/layer7/dns.py +736 -0
  40. netprotocols-2.2.0/src/netprotocols/packet.py +174 -0
  41. netprotocols-2.2.0/src/netprotocols/pcap.py +504 -0
  42. netprotocols-2.2.0/src/netprotocols/registry.py +385 -0
  43. netprotocols-2.2.0/src/netprotocols/utils/exceptions.py +136 -0
  44. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/utils/ipv4.py +19 -3
  45. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/utils/mac.py +22 -4
  46. netprotocols-2.2.0/src/netprotocols/walk.py +173 -0
  47. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/conftest.py +13 -21
  48. netprotocols-2.2.0/tests/strategies.py +274 -0
  49. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_arp.py +16 -2
  50. netprotocols-2.2.0/tests/test_contract.py +217 -0
  51. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_corpus.py +18 -18
  52. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_dhcp.py +117 -3
  53. netprotocols-2.2.0/tests/test_diagnostics.py +552 -0
  54. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_dns.py +149 -2
  55. netprotocols-2.2.0/tests/test_docs.py +115 -0
  56. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_ethernet.py +3 -1
  57. netprotocols-2.2.0/tests/test_exports.py +82 -0
  58. netprotocols-2.2.0/tests/test_flow.py +174 -0
  59. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_fuzz.py +141 -4
  60. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_gre.py +9 -6
  61. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_icmp.py +48 -0
  62. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_igmp.py +2 -2
  63. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_ip.py +163 -2
  64. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_ipv6_ext.py +175 -3
  65. netprotocols-2.2.0/tests/test_packet.py +103 -0
  66. netprotocols-2.2.0/tests/test_pattern_matching.py +341 -0
  67. netprotocols-2.2.0/tests/test_pcap.py +367 -0
  68. netprotocols-2.2.0/tests/test_registry.py +296 -0
  69. netprotocols-2.2.0/tests/test_version.py +54 -0
  70. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_vlan.py +3 -0
  71. netprotocols-2.2.0/tests/test_walk.py +414 -0
  72. netprotocols-2.2.0/tests/test_workflows.py +118 -0
  73. {netprotocols-1.3.0 → netprotocols-2.2.0}/uv.lock +27 -1
  74. netprotocols-1.3.0/.github/workflows/ci.yml +0 -47
  75. netprotocols-1.3.0/.github/workflows/release.yml +0 -18
  76. netprotocols-1.3.0/ARCHITECTURE.md +0 -295
  77. netprotocols-1.3.0/CHANGELOG.md +0 -296
  78. netprotocols-1.3.0/PKG-INFO +0 -198
  79. netprotocols-1.3.0/README.md +0 -173
  80. netprotocols-1.3.0/src/netprotocols/_base.py +0 -128
  81. netprotocols-1.3.0/src/netprotocols/layer2/ethernet.py +0 -90
  82. netprotocols-1.3.0/src/netprotocols/layer3/__init__.py +0 -21
  83. netprotocols-1.3.0/src/netprotocols/layer4/__init__.py +0 -4
  84. netprotocols-1.3.0/src/netprotocols/layer7/__init__.py +0 -3
  85. netprotocols-1.3.0/src/netprotocols/layer7/dns.py +0 -436
  86. netprotocols-1.3.0/src/netprotocols/packet.py +0 -88
  87. netprotocols-1.3.0/src/netprotocols/utils/exceptions.py +0 -48
  88. netprotocols-1.3.0/tests/test_contract.py +0 -108
  89. netprotocols-1.3.0/tests/test_packet.py +0 -37
  90. {netprotocols-1.3.0 → netprotocols-2.2.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  91. {netprotocols-1.3.0 → netprotocols-2.2.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  92. {netprotocols-1.3.0 → netprotocols-2.2.0}/.github/ISSUE_TEMPLATE/protocol_request.yml +0 -0
  93. {netprotocols-1.3.0 → netprotocols-2.2.0}/.github/pull_request_template.md +0 -0
  94. {netprotocols-1.3.0 → netprotocols-2.2.0}/CONTRIBUTING.md +0 -0
  95. {netprotocols-1.3.0 → netprotocols-2.2.0}/LICENSE +0 -0
  96. {netprotocols-1.3.0 → netprotocols-2.2.0}/scripts/capture_fixtures.sh +0 -0
  97. {netprotocols-1.3.0 → netprotocols-2.2.0}/scripts/capture_fixtures_dhcp.sh +0 -0
  98. {netprotocols-1.3.0 → netprotocols-2.2.0}/scripts/capture_fixtures_dns_tcp.sh +0 -0
  99. {netprotocols-1.3.0 → netprotocols-2.2.0}/scripts/capture_fixtures_gre.sh +0 -0
  100. {netprotocols-1.3.0 → netprotocols-2.2.0}/scripts/capture_fixtures_igmp.sh +0 -0
  101. {netprotocols-1.3.0 → netprotocols-2.2.0}/scripts/capture_fixtures_supplement.sh +0 -0
  102. {netprotocols-1.3.0 → netprotocols-2.2.0}/scripts/capture_fixtures_vlan.sh +0 -0
  103. {netprotocols-1.3.0 → netprotocols-2.2.0}/scripts/check_fixtures.py +0 -0
  104. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/_enums.py +0 -0
  105. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/layer2/__init__.py +0 -0
  106. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/py.typed +0 -0
  107. {netprotocols-1.3.0 → netprotocols-2.2.0}/src/netprotocols/utils/__init__.py +0 -0
  108. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/MANIFEST.md +0 -0
  109. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/arp_exchange.pcap +0 -0
  110. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/dhcp.pcap +0 -0
  111. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/dns_tcp.pcap +0 -0
  112. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/gre.pcap +0 -0
  113. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/icmpv4_echo_lo.pcap +0 -0
  114. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/icmpv4_external.pcap +0 -0
  115. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/icmpv4_ttl_exceeded.pcap +0 -0
  116. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/icmpv6_echo_lo.pcap +0 -0
  117. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/igmp.pcap +0 -0
  118. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/ipv4_fragments.pcap +0 -0
  119. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/ipv6_fragments.pcap +0 -0
  120. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/ipv6_mld.pcap +0 -0
  121. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/ipv6_ndp_mld.pcap +0 -0
  122. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/arp_exchange.pcap +0 -0
  123. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/dhcp.pcap +0 -0
  124. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/dns_tcp.pcap +0 -0
  125. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/gre.pcap +0 -0
  126. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/icmpv4_echo_lo.pcap +0 -0
  127. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/icmpv4_external.pcap +0 -0
  128. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/icmpv4_ttl_exceeded.pcap +0 -0
  129. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/icmpv6_echo_lo.pcap +0 -0
  130. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/igmp.pcap +0 -0
  131. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/ipv4_fragments.pcap +0 -0
  132. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/ipv6_fragments.pcap +0 -0
  133. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/ipv6_mld.pcap +0 -0
  134. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/ipv6_ndp_mld.pcap +0 -0
  135. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/tcp_http.pcap +0 -0
  136. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/tcp_https.pcap +0 -0
  137. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/udp_dns.pcap +0 -0
  138. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/staging/vlan_icmp.pcap +0 -0
  139. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/tcp_http.pcap +0 -0
  140. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/tcp_https.pcap +0 -0
  141. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/udp_dns.pcap +0 -0
  142. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/fixtures/vlan_icmp.pcap +0 -0
  143. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_checksum.py +0 -0
  144. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_tcp.py +0 -0
  145. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_udp.py +0 -0
  146. {netprotocols-1.3.0 → netprotocols-2.2.0}/tests/test_utils.py +0 -0
@@ -0,0 +1,114 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+ # Callable so the release workflow can run this exact ladder against a
9
+ # tagged commit before publishing (see release.yml). Keeping one
10
+ # definition means the release gate cannot drift from the PR gate.
11
+ workflow_call:
12
+
13
+ jobs:
14
+ lint:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v5
18
+ - uses: astral-sh/setup-uv@v6
19
+ with:
20
+ python-version: "3.12"
21
+ - run: uv run ruff check
22
+ - run: uv run ruff format --check
23
+
24
+ typecheck:
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - uses: actions/checkout@v5
28
+ - uses: astral-sh/setup-uv@v6
29
+ with:
30
+ python-version: "3.12"
31
+ - run: uv run mypy
32
+
33
+ test:
34
+ runs-on: ubuntu-latest
35
+ strategy:
36
+ matrix:
37
+ python-version: ["3.12", "3.13", "3.14"]
38
+ steps:
39
+ - uses: actions/checkout@v5
40
+ - uses: astral-sh/setup-uv@v6
41
+ with:
42
+ python-version: ${{ matrix.python-version }}
43
+ - run: uv run --frozen pytest --cov=netprotocols --cov-report=term
44
+
45
+ build:
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@v5
49
+ - uses: astral-sh/setup-uv@v6
50
+ - run: uv build
51
+ - run: uv run --isolated --no-project --with dist/*.whl python -c "import netprotocols; print(netprotocols.__version__)"
52
+
53
+ # Proves the README's browser/WASM claim (#99) against the real
54
+ # thing, not a simulation of it: boots an actual Pyodide runtime
55
+ # (scripts/pyodide/run_in_pyodide.mjs, via Node — no browser needed,
56
+ # same WebAssembly build either way) and decodes the whole
57
+ # real-capture corpus with the wheel this job just built. See
58
+ # scripts/pyodide/check_in_pyodide.py for what it checks and why.
59
+ pyodide:
60
+ runs-on: ubuntu-latest
61
+ steps:
62
+ - uses: actions/checkout@v5
63
+ - uses: astral-sh/setup-uv@v6
64
+ with:
65
+ python-version: "3.12"
66
+ - run: uv build
67
+ - uses: actions/setup-node@v4
68
+ with:
69
+ node-version: "22"
70
+ - run: npm ci
71
+ working-directory: scripts/pyodide
72
+ - run: >-
73
+ node scripts/pyodide/run_in_pyodide.mjs
74
+ "$GITHUB_WORKSPACE" scripts/pyodide/check_in_pyodide.py
75
+
76
+ # Blocking, at 15% below the committed baseline. The threshold has
77
+ # runner data behind it: this job's first run measured 3.7% away from
78
+ # a baseline recorded on entirely different hardware, and run-to-run
79
+ # spread on one machine is ~2%. 15% is therefore roughly four times
80
+ # the noise it has to tolerate, which leaves real regressions (Tier 1
81
+ # moved this number by 30-90% a step) nowhere to hide.
82
+ #
83
+ # A failure re-measures once before failing, because a shared runner
84
+ # can lose a slice of CPU to a neighbour: a genuine regression fails
85
+ # both attempts, a noisy one usually does not.
86
+ #
87
+ # Pinned to the baseline's interpreter on purpose: normalization
88
+ # cancels how fast the machine is, not which CPython it runs (see the
89
+ # module docstring in scripts/benchmark.py). If a change is a
90
+ # deliberate trade, re-record with --update-baseline and say why.
91
+ benchmark:
92
+ runs-on: ubuntu-latest
93
+ steps:
94
+ - uses: actions/checkout@v5
95
+ - uses: astral-sh/setup-uv@v6
96
+ with:
97
+ python-version: "3.12"
98
+ - name: Corpus decode throughput
99
+ shell: bash
100
+ run: |
101
+ # Keep the benchmark's own exit status: piping the whole thing
102
+ # into tee would report tee's, and wrapping it in a { } group
103
+ # would report the trailing echo's -- either way the gate
104
+ # would pass while the check failed.
105
+ status=0
106
+ uv run --frozen --group bench python scripts/benchmark.py \
107
+ --check --threshold 15 --compare >benchmark.out 2>&1 || status=$?
108
+ {
109
+ echo '```'
110
+ cat benchmark.out
111
+ echo '```'
112
+ } >>"$GITHUB_STEP_SUMMARY"
113
+ cat benchmark.out
114
+ exit "$status"
@@ -0,0 +1,64 @@
1
+ name: Nightly fuzz
2
+
3
+ # Separate from ci.yml on purpose: ci.yml is reused via workflow_call
4
+ # by release.yml specifically so the release gate and the PR gate never
5
+ # drift apart (see the comment at the top of ci.yml). Folding a
6
+ # schedule trigger into it would fire that whole ladder on a cron too,
7
+ # not just this exploratory job.
8
+ on:
9
+ schedule:
10
+ # Arbitrary; 03:00 UTC lands after most contributors' working day
11
+ # across common timezones, clear of any push/PR traffic.
12
+ - cron: "0 3 * * *"
13
+ workflow_dispatch:
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ # tests/test_fuzz.py's own "netprotocols" profile is deliberately
20
+ # derandomize=True at 200 examples, so CI has run the *same* 200
21
+ # inputs on every push since it was written. This job runs the whole
22
+ # suite under the "nightly" profile instead (10,000 examples, a real
23
+ # random seed each time — see tests/test_fuzz.py's module docstring),
24
+ # so it explores new ground every run rather than replaying the same
25
+ # counterexample search forever. Pinned to Python 3.12 only, unlike
26
+ # the "test" job's full matrix: this is an exploratory job, not a
27
+ # compatibility gate, and 3x the interpreters would 3x its cost for
28
+ # no extra coverage of what it is actually looking for.
29
+ fuzz:
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v5
33
+ - uses: astral-sh/setup-uv@v6
34
+ with:
35
+ python-version: "3.12"
36
+ # Keyed by run id with a prefix restore-key: a fixed key would
37
+ # restore forever but (actions/cache never overwrites an existing
38
+ # key) never actually save a new one, so nothing would
39
+ # accumulate. This restores the most recent run's cache and saves
40
+ # today's as a new entry, so Hypothesis's example database grows
41
+ # night over night instead of starting cold every time.
42
+ - name: Restore accumulated Hypothesis examples
43
+ uses: actions/cache@v4
44
+ with:
45
+ path: .hypothesis
46
+ key: hypothesis-nightly-${{ github.run_id }}
47
+ restore-keys: |
48
+ hypothesis-nightly-
49
+ - name: Run the full suite under the nightly fuzz profile
50
+ env:
51
+ HYPOTHESIS_PROFILE: nightly
52
+ run: uv run --frozen pytest
53
+ # A failed scheduled run already notifies repo watchers by
54
+ # default (GitHub's standard behavior) — that red run *is* the
55
+ # report; nothing here files an issue on top of it. This just
56
+ # makes the minimal counterexample recoverable without needing
57
+ # repo write access to re-run the job locally.
58
+ - name: Upload the failing example database
59
+ if: failure()
60
+ uses: actions/upload-artifact@v4
61
+ with:
62
+ name: hypothesis-nightly-failure-${{ github.run_id }}
63
+ path: .hypothesis
64
+ retention-days: 30
@@ -0,0 +1,61 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ # Run the full QA ladder against the tagged commit before anything is
12
+ # uploaded. This is not redundant with the CI workflow's own triggers:
13
+ # those fire on pushes and pull requests targeting master, and a tag
14
+ # push matches neither, so without this job a release ran no checks at
15
+ # all. A tag can also point at a commit that never landed on master.
16
+ # A published version can only be yanked, never replaced.
17
+ ci:
18
+ uses: ./.github/workflows/ci.yml
19
+
20
+ publish:
21
+ needs: ci
22
+ runs-on: ubuntu-latest
23
+ environment: pypi
24
+ permissions:
25
+ id-token: write
26
+ contents: read
27
+ steps:
28
+ - uses: actions/checkout@v5
29
+ - uses: astral-sh/setup-uv@v6
30
+ - run: uv build
31
+ # The QA ladder cannot catch a mistagged release: every check
32
+ # passes because the code is fine and only the tag is wrong. Read
33
+ # the version back out of the artifact that is about to be
34
+ # uploaded, rather than re-parsing pyproject.toml, so this
35
+ # compares against what PyPI will actually receive.
36
+ - name: Verify the tag matches the version being published
37
+ run: |
38
+ set -euo pipefail
39
+ # Only the "v*" trigger keeps a bare "1.3.0" tag from reaching
40
+ # this step, where it would compare equal and pass. Check the
41
+ # prefix here too, so the contract survives a wider trigger.
42
+ case "${GITHUB_REF_NAME}" in
43
+ v*) tag="${GITHUB_REF_NAME#v}" ;;
44
+ *)
45
+ echo "::error::Tag ${GITHUB_REF_NAME} does not start with 'v'."
46
+ echo "Release tags must be of the form v<version>." >&2
47
+ exit 1
48
+ ;;
49
+ esac
50
+ built=$(uv run --isolated --no-project --with dist/*.whl \
51
+ python -c \
52
+ "from importlib.metadata import version; print(version('netprotocols'))")
53
+ if [ "$tag" != "$built" ]; then
54
+ echo "::error::Tag ${GITHUB_REF_NAME} would publish version ${built}."
55
+ echo "The tag and the packaged version must agree." >&2
56
+ echo "Either retag as v${built}, or set the version in" >&2
57
+ echo "pyproject.toml (and __version__) to ${tag} and retag." >&2
58
+ exit 1
59
+ fi
60
+ echo "Tag ${GITHUB_REF_NAME} matches packaged version ${built}."
61
+ - run: uv publish --trusted-publishing always
@@ -147,3 +147,11 @@ amass.log
147
147
 
148
148
  # Application-specific
149
149
  .logs/tests/fixtures/staging/
150
+
151
+ # Written by the benchmark CI step (scripts/benchmark.py output)
152
+ benchmark.out
153
+
154
+ # npm dependency (the pyodide package itself) for scripts/pyodide/ —
155
+ # CI installs it fresh from package-lock.json, same as any other
156
+ # lockfile-pinned dependency.
157
+ scripts/pyodide/node_modules/