tigrcorn-certification 0.3.16.dev13__tar.gz → 0.3.17.dev1__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.
- {tigrcorn_certification-0.3.16.dev13 → tigrcorn_certification-0.3.17.dev1}/PKG-INFO +3 -3
- {tigrcorn_certification-0.3.16.dev13 → tigrcorn_certification-0.3.17.dev1}/pyproject.toml +3 -3
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/__init__.py +203 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/aioquic_preflight/__init__.py +9 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/aioquic_preflight/bundle.py +80 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/aioquic_preflight/core.py +115 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/aioquic_preflight/helpers.py +87 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/aioquic_preflight/imports.py +24 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/aioquic_preflight/records.py +54 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/aioquic_preflight/status_docs.py +109 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/artifacts.py +337 -0
- {tigrcorn_certification-0.3.16.dev13 → tigrcorn_certification-0.3.17.dev1}/src/tigrcorn_certification/certification_env.py +4 -52
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/__init__.py +17 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/adapters.py +259 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/assertions.py +61 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/core.py +23 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/environment.py +211 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/helpers.py +179 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/imports.py +57 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/matrix.py +148 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/models.py +144 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/ports.py +69 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/process.py +46 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/proxies.py +211 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/qlog.py +142 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/runner.py +116 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/interop_runner/scenario.py +395 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/perf_runner/__init__.py +12 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/perf_runner/artifacts.py +158 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/perf_runner/environment.py +45 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/perf_runner/imports.py +16 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/perf_runner/matrix.py +38 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/perf_runner/metrics.py +231 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/perf_runner/models.py +62 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/perf_runner/runner.py +126 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/perf_runner/stats.py +43 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/perf_runner/validation.py +56 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/release_gates/__init__.py +14 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/release_gates/artifact_gate.py +40 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/release_gates/contract_registry.py +340 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/release_gates/core.py +161 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/release_gates/docs.py +139 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/release_gates/imports.py +27 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/release_gates/independent.py +302 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/release_gates/loaders.py +55 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/release_gates/models.py +65 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/release_gates/promotion.py +111 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/release_gates/promotion_sections.py +373 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/release_gates/supply_chain_gate.py +47 -0
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification/supply_chain.py +373 -0
- {tigrcorn_certification-0.3.16.dev13 → tigrcorn_certification-0.3.17.dev1}/src/tigrcorn_certification.egg-info/PKG-INFO +3 -3
- tigrcorn_certification-0.3.17.dev1/src/tigrcorn_certification.egg-info/SOURCES.txt +59 -0
- {tigrcorn_certification-0.3.16.dev13 → tigrcorn_certification-0.3.17.dev1}/src/tigrcorn_certification.egg-info/requires.txt +2 -2
- tigrcorn_certification-0.3.16.dev13/src/tigrcorn_certification/__init__.py +0 -55
- tigrcorn_certification-0.3.16.dev13/src/tigrcorn_certification/aioquic_preflight.py +0 -449
- tigrcorn_certification-0.3.16.dev13/src/tigrcorn_certification/interop_runner.py +0 -2017
- tigrcorn_certification-0.3.16.dev13/src/tigrcorn_certification/perf_runner.py +0 -725
- tigrcorn_certification-0.3.16.dev13/src/tigrcorn_certification/release_gates.py +0 -1351
- tigrcorn_certification-0.3.16.dev13/src/tigrcorn_certification.egg-info/SOURCES.txt +0 -17
- {tigrcorn_certification-0.3.16.dev13 → tigrcorn_certification-0.3.17.dev1}/LICENSE +0 -0
- {tigrcorn_certification-0.3.16.dev13 → tigrcorn_certification-0.3.17.dev1}/README.md +0 -0
- {tigrcorn_certification-0.3.16.dev13 → tigrcorn_certification-0.3.17.dev1}/setup.cfg +0 -0
- {tigrcorn_certification-0.3.16.dev13 → tigrcorn_certification-0.3.17.dev1}/src/tigrcorn_certification/conformance.py +0 -0
- {tigrcorn_certification-0.3.16.dev13 → tigrcorn_certification-0.3.17.dev1}/src/tigrcorn_certification/explicit_surfaces.py +0 -0
- {tigrcorn_certification-0.3.16.dev13 → tigrcorn_certification-0.3.17.dev1}/src/tigrcorn_certification/py.typed +0 -0
- {tigrcorn_certification-0.3.16.dev13 → tigrcorn_certification-0.3.17.dev1}/src/tigrcorn_certification.egg-info/dependency_links.txt +0 -0
- {tigrcorn_certification-0.3.16.dev13 → tigrcorn_certification-0.3.17.dev1}/src/tigrcorn_certification.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tigrcorn-certification
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.17.dev1
|
|
4
4
|
Summary: Certification and release-gate tooling for Tigrcorn HTTP/3, QUIC, WebSocket, WebTransport, and ASGI conformance evidence.
|
|
5
5
|
Author-email: Jacob Stewart <jacob@swarmauri.com>
|
|
6
6
|
License: Apache License
|
|
@@ -187,8 +187,8 @@ Classifier: Typing :: Typed
|
|
|
187
187
|
Requires-Python: <3.15,>=3.10
|
|
188
188
|
Description-Content-Type: text/markdown
|
|
189
189
|
License-File: LICENSE
|
|
190
|
-
Requires-Dist: tigrcorn-compat==0.3.
|
|
191
|
-
Requires-Dist: tigrcorn-runtime==0.3.
|
|
190
|
+
Requires-Dist: tigrcorn-compat==0.3.17.dev1
|
|
191
|
+
Requires-Dist: tigrcorn-runtime==0.3.17.dev1
|
|
192
192
|
Requires-Dist: tomli>=2.0.0; python_version < "3.11"
|
|
193
193
|
Requires-Dist: cryptography>=46.0.0
|
|
194
194
|
Requires-Dist: aioquic>=1.3.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tigrcorn-certification"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.17.dev1"
|
|
8
8
|
description = "Certification and release-gate tooling for Tigrcorn HTTP/3, QUIC, WebSocket, WebTransport, and ASGI conformance evidence."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10,<3.15"
|
|
@@ -30,8 +30,8 @@ classifiers = [
|
|
|
30
30
|
"Typing :: Typed",
|
|
31
31
|
]
|
|
32
32
|
dependencies = [
|
|
33
|
-
"tigrcorn-compat==0.3.
|
|
34
|
-
"tigrcorn-runtime==0.3.
|
|
33
|
+
"tigrcorn-compat==0.3.17.dev1",
|
|
34
|
+
"tigrcorn-runtime==0.3.17.dev1",
|
|
35
35
|
"tomli>=2.0.0; python_version < '3.11'",
|
|
36
36
|
"cryptography>=46.0.0",
|
|
37
37
|
"aioquic>=1.3.0",
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
PACKAGE_BOUNDARY = "certification"
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"PACKAGE_BOUNDARY",
|
|
7
|
+
"evaluate_release_gates",
|
|
8
|
+
"assert_release_ready",
|
|
9
|
+
"ReleaseGateError",
|
|
10
|
+
"ReleaseGateReport",
|
|
11
|
+
"certification_explicit_surface_catalog",
|
|
12
|
+
"certification_explicit_surface_ids",
|
|
13
|
+
"validate_explicit_surface_manifest",
|
|
14
|
+
"evaluate_contract_registry_release_gate",
|
|
15
|
+
"evaluate_certification_artifact_release_gate",
|
|
16
|
+
"evaluate_supply_chain_release_gate",
|
|
17
|
+
"CertificationArtifact",
|
|
18
|
+
"CertificationArtifactError",
|
|
19
|
+
"CertificationBundle",
|
|
20
|
+
"build_artifact",
|
|
21
|
+
"build_bundle",
|
|
22
|
+
"build_manifest",
|
|
23
|
+
"canonical_json",
|
|
24
|
+
"output_directory_contract",
|
|
25
|
+
"reject_nondeterministic_fields",
|
|
26
|
+
"replay_diff",
|
|
27
|
+
"sign_manifest",
|
|
28
|
+
"verify_release_evidence",
|
|
29
|
+
"verify_output_tree",
|
|
30
|
+
"write_bundle",
|
|
31
|
+
"write_certification_artifacts",
|
|
32
|
+
"PackageRecord",
|
|
33
|
+
"SupplyChainEvidenceError",
|
|
34
|
+
"dependency_drift_report",
|
|
35
|
+
"generated_certification_artifact_links",
|
|
36
|
+
"has_slsa_provenance",
|
|
37
|
+
"has_spdx_or_cyclonedx",
|
|
38
|
+
"package_list",
|
|
39
|
+
"package_record",
|
|
40
|
+
"release_train_evidence",
|
|
41
|
+
"supply_chain_manifest",
|
|
42
|
+
"validate_release_bundle",
|
|
43
|
+
"validate_certification_artifact_linkage",
|
|
44
|
+
"validate_package_manifest_integrity",
|
|
45
|
+
"validate_release_evidence",
|
|
46
|
+
"write_supply_chain_release_bundle",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def __getattr__(name: str):
|
|
51
|
+
if name in {
|
|
52
|
+
"evaluate_release_gates",
|
|
53
|
+
"assert_release_ready",
|
|
54
|
+
"ReleaseGateError",
|
|
55
|
+
"ReleaseGateReport",
|
|
56
|
+
"evaluate_contract_registry_release_gate",
|
|
57
|
+
"evaluate_certification_artifact_release_gate",
|
|
58
|
+
"evaluate_supply_chain_release_gate",
|
|
59
|
+
}:
|
|
60
|
+
from .release_gates import (
|
|
61
|
+
ReleaseGateError,
|
|
62
|
+
ReleaseGateReport,
|
|
63
|
+
assert_release_ready,
|
|
64
|
+
evaluate_certification_artifact_release_gate,
|
|
65
|
+
evaluate_contract_registry_release_gate,
|
|
66
|
+
evaluate_release_gates,
|
|
67
|
+
evaluate_supply_chain_release_gate,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
mapping = {
|
|
71
|
+
"evaluate_release_gates": evaluate_release_gates,
|
|
72
|
+
"evaluate_certification_artifact_release_gate": evaluate_certification_artifact_release_gate,
|
|
73
|
+
"evaluate_contract_registry_release_gate": evaluate_contract_registry_release_gate,
|
|
74
|
+
"evaluate_supply_chain_release_gate": evaluate_supply_chain_release_gate,
|
|
75
|
+
"assert_release_ready": assert_release_ready,
|
|
76
|
+
"ReleaseGateError": ReleaseGateError,
|
|
77
|
+
"ReleaseGateReport": ReleaseGateReport,
|
|
78
|
+
}
|
|
79
|
+
return mapping[name]
|
|
80
|
+
if name in {
|
|
81
|
+
"certification_explicit_surface_catalog",
|
|
82
|
+
"certification_explicit_surface_ids",
|
|
83
|
+
"validate_explicit_surface_manifest",
|
|
84
|
+
}:
|
|
85
|
+
from .explicit_surfaces import (
|
|
86
|
+
certification_explicit_surface_catalog,
|
|
87
|
+
certification_explicit_surface_ids,
|
|
88
|
+
validate_explicit_surface_manifest,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
mapping = {
|
|
92
|
+
"certification_explicit_surface_catalog": certification_explicit_surface_catalog,
|
|
93
|
+
"certification_explicit_surface_ids": certification_explicit_surface_ids,
|
|
94
|
+
"validate_explicit_surface_manifest": validate_explicit_surface_manifest,
|
|
95
|
+
}
|
|
96
|
+
return mapping[name]
|
|
97
|
+
if name in {
|
|
98
|
+
"CertificationArtifact",
|
|
99
|
+
"CertificationArtifactError",
|
|
100
|
+
"CertificationBundle",
|
|
101
|
+
"build_artifact",
|
|
102
|
+
"build_bundle",
|
|
103
|
+
"build_manifest",
|
|
104
|
+
"canonical_json",
|
|
105
|
+
"output_directory_contract",
|
|
106
|
+
"reject_nondeterministic_fields",
|
|
107
|
+
"replay_diff",
|
|
108
|
+
"sign_manifest",
|
|
109
|
+
"verify_release_evidence",
|
|
110
|
+
"verify_output_tree",
|
|
111
|
+
"write_bundle",
|
|
112
|
+
"write_certification_artifacts",
|
|
113
|
+
}:
|
|
114
|
+
from .artifacts import (
|
|
115
|
+
CertificationArtifact,
|
|
116
|
+
CertificationArtifactError,
|
|
117
|
+
CertificationBundle,
|
|
118
|
+
build_artifact,
|
|
119
|
+
build_bundle,
|
|
120
|
+
build_manifest,
|
|
121
|
+
canonical_json,
|
|
122
|
+
output_directory_contract,
|
|
123
|
+
reject_nondeterministic_fields,
|
|
124
|
+
replay_diff,
|
|
125
|
+
sign_manifest,
|
|
126
|
+
verify_release_evidence,
|
|
127
|
+
verify_output_tree,
|
|
128
|
+
write_bundle,
|
|
129
|
+
write_certification_artifacts,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
mapping = {
|
|
133
|
+
"CertificationArtifact": CertificationArtifact,
|
|
134
|
+
"CertificationArtifactError": CertificationArtifactError,
|
|
135
|
+
"CertificationBundle": CertificationBundle,
|
|
136
|
+
"build_artifact": build_artifact,
|
|
137
|
+
"build_bundle": build_bundle,
|
|
138
|
+
"build_manifest": build_manifest,
|
|
139
|
+
"canonical_json": canonical_json,
|
|
140
|
+
"output_directory_contract": output_directory_contract,
|
|
141
|
+
"reject_nondeterministic_fields": reject_nondeterministic_fields,
|
|
142
|
+
"replay_diff": replay_diff,
|
|
143
|
+
"sign_manifest": sign_manifest,
|
|
144
|
+
"verify_release_evidence": verify_release_evidence,
|
|
145
|
+
"verify_output_tree": verify_output_tree,
|
|
146
|
+
"write_bundle": write_bundle,
|
|
147
|
+
"write_certification_artifacts": write_certification_artifacts,
|
|
148
|
+
}
|
|
149
|
+
return mapping[name]
|
|
150
|
+
if name in {
|
|
151
|
+
"PackageRecord",
|
|
152
|
+
"SupplyChainEvidenceError",
|
|
153
|
+
"dependency_drift_report",
|
|
154
|
+
"generated_certification_artifact_links",
|
|
155
|
+
"has_slsa_provenance",
|
|
156
|
+
"has_spdx_or_cyclonedx",
|
|
157
|
+
"package_list",
|
|
158
|
+
"package_record",
|
|
159
|
+
"release_train_evidence",
|
|
160
|
+
"supply_chain_manifest",
|
|
161
|
+
"validate_certification_artifact_linkage",
|
|
162
|
+
"validate_package_manifest_integrity",
|
|
163
|
+
"validate_release_bundle",
|
|
164
|
+
"validate_release_evidence",
|
|
165
|
+
"write_supply_chain_release_bundle",
|
|
166
|
+
}:
|
|
167
|
+
from .supply_chain import (
|
|
168
|
+
PackageRecord,
|
|
169
|
+
SupplyChainEvidenceError,
|
|
170
|
+
dependency_drift_report,
|
|
171
|
+
generated_certification_artifact_links,
|
|
172
|
+
has_slsa_provenance,
|
|
173
|
+
has_spdx_or_cyclonedx,
|
|
174
|
+
package_list,
|
|
175
|
+
package_record,
|
|
176
|
+
release_train_evidence,
|
|
177
|
+
supply_chain_manifest,
|
|
178
|
+
validate_certification_artifact_linkage,
|
|
179
|
+
validate_package_manifest_integrity,
|
|
180
|
+
validate_release_bundle,
|
|
181
|
+
validate_release_evidence,
|
|
182
|
+
write_supply_chain_release_bundle,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
mapping = {
|
|
186
|
+
"PackageRecord": PackageRecord,
|
|
187
|
+
"SupplyChainEvidenceError": SupplyChainEvidenceError,
|
|
188
|
+
"dependency_drift_report": dependency_drift_report,
|
|
189
|
+
"generated_certification_artifact_links": generated_certification_artifact_links,
|
|
190
|
+
"has_slsa_provenance": has_slsa_provenance,
|
|
191
|
+
"has_spdx_or_cyclonedx": has_spdx_or_cyclonedx,
|
|
192
|
+
"package_list": package_list,
|
|
193
|
+
"package_record": package_record,
|
|
194
|
+
"release_train_evidence": release_train_evidence,
|
|
195
|
+
"supply_chain_manifest": supply_chain_manifest,
|
|
196
|
+
"validate_certification_artifact_linkage": validate_certification_artifact_linkage,
|
|
197
|
+
"validate_package_manifest_integrity": validate_package_manifest_integrity,
|
|
198
|
+
"validate_release_bundle": validate_release_bundle,
|
|
199
|
+
"validate_release_evidence": validate_release_evidence,
|
|
200
|
+
"write_supply_chain_release_bundle": write_supply_chain_release_bundle,
|
|
201
|
+
}
|
|
202
|
+
return mapping[name]
|
|
203
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .imports import *
|
|
4
|
+
from .helpers import *
|
|
5
|
+
from .helpers import _now
|
|
6
|
+
|
|
7
|
+
def _bundle_manifest(*, artifact_root: str, matrix_path: str, scenario_ids: Sequence[str]) -> dict[str, Any]:
|
|
8
|
+
return {
|
|
9
|
+
'bundle_kind': 'aioquic_adapter_preflight_bundle',
|
|
10
|
+
'generated_at': _now(),
|
|
11
|
+
'release_gate_eligible': False,
|
|
12
|
+
'artifact_root': artifact_root,
|
|
13
|
+
'matrix_path': matrix_path,
|
|
14
|
+
'scenario_ids': list(scenario_ids),
|
|
15
|
+
'note': 'This bundle proves the third-party aioquic HTTP/3 adapters can execute cleanly before strict-target checkpoint promotion work continues.',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _bundle_index(*, artifact_root: str, matrix_path: str, scenario_records: Sequence[Mapping[str, Any]], environment: Mapping[str, Any], gate_status: Mapping[str, Any]) -> dict[str, Any]:
|
|
20
|
+
return {
|
|
21
|
+
'artifact_root': artifact_root,
|
|
22
|
+
'bundle_kind': 'aioquic_adapter_preflight_bundle',
|
|
23
|
+
'generated_at': _now(),
|
|
24
|
+
'matrix_path': matrix_path,
|
|
25
|
+
'scenario_count': len(scenario_records),
|
|
26
|
+
'scenario_ids': [str(item['scenario_id']) for item in scenario_records],
|
|
27
|
+
'all_adapters_passed': all(bool(item['passed']) for item in scenario_records),
|
|
28
|
+
'no_peer_exit_code_2': all(int(item['peer_exit_code']) != 2 for item in scenario_records),
|
|
29
|
+
'negotiation_metadata_emitted': all(bool(item['negotiation_metadata_emitted']) for item in scenario_records),
|
|
30
|
+
'transcript_metadata_emitted': all(bool(item['transcript_emitted']) for item in scenario_records),
|
|
31
|
+
'all_protocols_h3': all(item.get('protocol') == 'h3' for item in scenario_records),
|
|
32
|
+
'all_handshakes_complete': all(bool(item['handshake_complete']) for item in scenario_records),
|
|
33
|
+
'certificate_inputs_ready': all(bool(item['certificate_inputs_ready']) for item in scenario_records),
|
|
34
|
+
'packet_traces_emitted': all(bool(item['packet_trace_exists']) for item in scenario_records),
|
|
35
|
+
'qlogs_emitted': all(bool(item['qlog_exists']) for item in scenario_records),
|
|
36
|
+
'environment': dict(environment),
|
|
37
|
+
'gate_status_after_preflight': dict(gate_status),
|
|
38
|
+
'release_gate_eligible': False,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _bundle_summary(index: Mapping[str, Any]) -> dict[str, Any]:
|
|
43
|
+
return {
|
|
44
|
+
'artifact_root': index['artifact_root'],
|
|
45
|
+
'bundle_kind': index['bundle_kind'],
|
|
46
|
+
'generated_at': index['generated_at'],
|
|
47
|
+
'scenario_count': index['scenario_count'],
|
|
48
|
+
'all_adapters_passed': index['all_adapters_passed'],
|
|
49
|
+
'no_peer_exit_code_2': index['no_peer_exit_code_2'],
|
|
50
|
+
'all_protocols_h3': index['all_protocols_h3'],
|
|
51
|
+
'all_handshakes_complete': index['all_handshakes_complete'],
|
|
52
|
+
'certificate_inputs_ready': index['certificate_inputs_ready'],
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _bundle_readme(index: Mapping[str, Any], scenario_records: Sequence[Mapping[str, Any]]) -> str:
|
|
57
|
+
lines = [
|
|
58
|
+
'# aioquic adapter preflight bundle',
|
|
59
|
+
'',
|
|
60
|
+
'This bundle preserves the direct third-party aioquic HTTP/3 adapter preflight runs used before strict-target certification checkpoints.',
|
|
61
|
+
'',
|
|
62
|
+
'## Exit-criteria status',
|
|
63
|
+
'',
|
|
64
|
+
f"- all adapters passed: `{index['all_adapters_passed']}`",
|
|
65
|
+
f"- no peer exit code 2: `{index['no_peer_exit_code_2']}`",
|
|
66
|
+
f"- negotiation metadata emitted: `{index['negotiation_metadata_emitted']}`",
|
|
67
|
+
f"- transcript metadata emitted: `{index['transcript_metadata_emitted']}`",
|
|
68
|
+
f"- ALPN h3 observed for every run: `{index['all_protocols_h3']}`",
|
|
69
|
+
f"- QUIC handshakes complete: `{index['all_handshakes_complete']}`",
|
|
70
|
+
f"- certificate inputs ready: `{index['certificate_inputs_ready']}`",
|
|
71
|
+
'',
|
|
72
|
+
'## Scenarios',
|
|
73
|
+
'',
|
|
74
|
+
]
|
|
75
|
+
for item in scenario_records:
|
|
76
|
+
lines.extend([
|
|
77
|
+
f"- `{item['scenario_id']}` → passed=`{item['passed']}`, peer_exit=`{item['peer_exit_code']}`, protocol=`{item['protocol']}`, handshake_complete=`{item['handshake_complete']}`",
|
|
78
|
+
])
|
|
79
|
+
lines.append('')
|
|
80
|
+
return '\n'.join(lines) + '\n'
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .imports import *
|
|
4
|
+
from .helpers import *
|
|
5
|
+
from .helpers import _dump_json, _module_version, _now
|
|
6
|
+
from .records import *
|
|
7
|
+
from .records import _extract_scenario_record
|
|
8
|
+
from .bundle import *
|
|
9
|
+
from .bundle import _bundle_index, _bundle_manifest, _bundle_readme, _bundle_summary
|
|
10
|
+
from .status_docs import *
|
|
11
|
+
|
|
12
|
+
def run_aioquic_adapter_preflight(
|
|
13
|
+
root: str | Path,
|
|
14
|
+
*,
|
|
15
|
+
release_root: str = DEFAULT_RELEASE_ROOT,
|
|
16
|
+
bundle_name: str = DEFAULT_BUNDLE_NAME,
|
|
17
|
+
matrix_path: str = DEFAULT_MATRIX_PATH,
|
|
18
|
+
scenario_ids: Sequence[str] = DEFAULT_PRELIGHT_SCENARIOS,
|
|
19
|
+
bundle_root: str | Path | None = None,
|
|
20
|
+
require_pass: bool = False,
|
|
21
|
+
) -> dict[str, Any]:
|
|
22
|
+
repo_root = Path(root)
|
|
23
|
+
resolved_release_root = repo_root / release_root
|
|
24
|
+
target_bundle_root = Path(bundle_root) if bundle_root is not None else resolved_release_root / bundle_name
|
|
25
|
+
if target_bundle_root.exists():
|
|
26
|
+
shutil.rmtree(target_bundle_root)
|
|
27
|
+
target_bundle_root.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
|
|
29
|
+
with tempfile.TemporaryDirectory(prefix='aioquic-preflight-') as tmpdir:
|
|
30
|
+
summary = run_external_matrix(
|
|
31
|
+
repo_root / matrix_path,
|
|
32
|
+
artifact_root=tmpdir,
|
|
33
|
+
source_root=repo_root,
|
|
34
|
+
scenario_ids=list(scenario_ids),
|
|
35
|
+
strict=False,
|
|
36
|
+
)
|
|
37
|
+
generated_root = Path(summary.artifact_root)
|
|
38
|
+
for source_name, target_name in (
|
|
39
|
+
('manifest.json', 'generated_matrix_manifest.json'),
|
|
40
|
+
('index.json', 'generated_matrix_index.json'),
|
|
41
|
+
('summary.json', 'generated_matrix_summary.json'),
|
|
42
|
+
):
|
|
43
|
+
shutil.copy2(generated_root / source_name, target_bundle_root / target_name)
|
|
44
|
+
for scenario_id in scenario_ids:
|
|
45
|
+
shutil.copytree(generated_root / scenario_id, target_bundle_root / scenario_id)
|
|
46
|
+
|
|
47
|
+
environment = {
|
|
48
|
+
'python_version': sys.version,
|
|
49
|
+
'python_minor_version': f'{sys.version_info.major}.{sys.version_info.minor}',
|
|
50
|
+
'aioquic_version': _module_version('aioquic'),
|
|
51
|
+
'wsproto_version': _module_version('wsproto'),
|
|
52
|
+
'h2_version': _module_version('h2'),
|
|
53
|
+
'websockets_version': _module_version('websockets'),
|
|
54
|
+
}
|
|
55
|
+
gate_status = {
|
|
56
|
+
'authoritative_boundary_passed': evaluate_release_gates(repo_root).passed,
|
|
57
|
+
'strict_target_boundary_passed': evaluate_release_gates(repo_root, boundary_path='docs/review/conformance/certification_boundary.strict_target.json').passed,
|
|
58
|
+
'promotion_target_passed': evaluate_promotion_target(repo_root).passed,
|
|
59
|
+
}
|
|
60
|
+
scenario_records = [_extract_scenario_record(repo_root, target_bundle_root, scenario_id) for scenario_id in scenario_ids]
|
|
61
|
+
index = _bundle_index(
|
|
62
|
+
artifact_root=str(target_bundle_root.relative_to(repo_root)) if target_bundle_root.is_relative_to(repo_root) else str(target_bundle_root),
|
|
63
|
+
matrix_path=matrix_path,
|
|
64
|
+
scenario_records=scenario_records,
|
|
65
|
+
environment=environment,
|
|
66
|
+
gate_status=gate_status,
|
|
67
|
+
)
|
|
68
|
+
manifest = _bundle_manifest(
|
|
69
|
+
artifact_root=index['artifact_root'],
|
|
70
|
+
matrix_path=matrix_path,
|
|
71
|
+
scenario_ids=scenario_ids,
|
|
72
|
+
)
|
|
73
|
+
summary = _bundle_summary(index)
|
|
74
|
+
_dump_json(target_bundle_root / 'manifest.json', manifest)
|
|
75
|
+
_dump_json(target_bundle_root / 'index.json', index)
|
|
76
|
+
_dump_json(target_bundle_root / 'summary.json', summary)
|
|
77
|
+
_dump_json(target_bundle_root / 'preflight.json', {
|
|
78
|
+
'generated_at': _now(),
|
|
79
|
+
'environment': environment,
|
|
80
|
+
'gate_status_after_preflight': gate_status,
|
|
81
|
+
'scenario_records': scenario_records,
|
|
82
|
+
})
|
|
83
|
+
(target_bundle_root / 'README.md').write_text(_bundle_readme(index, scenario_records), encoding='utf-8')
|
|
84
|
+
|
|
85
|
+
snapshot = {
|
|
86
|
+
'checkpoint': 'aioquic_adapter_preflight',
|
|
87
|
+
'status': 'aioquic_adapter_preflight_passed' if summary['all_adapters_passed'] else 'aioquic_adapter_preflight_failed',
|
|
88
|
+
'current_state': {
|
|
89
|
+
'release_root': release_root,
|
|
90
|
+
'bundle_root': index['artifact_root'],
|
|
91
|
+
'matrix_path': matrix_path,
|
|
92
|
+
'scenario_ids': list(scenario_ids),
|
|
93
|
+
'scenario_records': scenario_records,
|
|
94
|
+
'environment': environment,
|
|
95
|
+
'all_adapters_passed': index['all_adapters_passed'],
|
|
96
|
+
'no_peer_exit_code_2': index['no_peer_exit_code_2'],
|
|
97
|
+
'negotiation_metadata_emitted': index['negotiation_metadata_emitted'],
|
|
98
|
+
'transcript_metadata_emitted': index['transcript_metadata_emitted'],
|
|
99
|
+
'all_protocols_h3': index['all_protocols_h3'],
|
|
100
|
+
'all_handshakes_complete': index['all_handshakes_complete'],
|
|
101
|
+
'certificate_inputs_ready': index['certificate_inputs_ready'],
|
|
102
|
+
'packet_traces_emitted': index['packet_traces_emitted'],
|
|
103
|
+
'qlogs_emitted': index['qlogs_emitted'],
|
|
104
|
+
'gate_status_after_preflight': gate_status,
|
|
105
|
+
},
|
|
106
|
+
'remaining_strict_target_blockers': [
|
|
107
|
+
'websocket-http3-server-aioquic-client-permessage-deflate',
|
|
108
|
+
'http3-connect-relay-aioquic-client',
|
|
109
|
+
'http3-trailer-fields-aioquic-client',
|
|
110
|
+
'http3-content-coding-aioquic-client',
|
|
111
|
+
],
|
|
112
|
+
}
|
|
113
|
+
if require_pass and not summary['all_adapters_passed']:
|
|
114
|
+
raise AioquicAdapterPreflightError('one or more aioquic adapter preflight scenarios failed')
|
|
115
|
+
return snapshot
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .imports import *
|
|
4
|
+
|
|
5
|
+
class AioquicAdapterPreflightError(RuntimeError):
|
|
6
|
+
"""Raised when the aioquic adapter preflight fails and strict pass mode is enabled."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _now() -> str:
|
|
10
|
+
return datetime.now(timezone.utc).isoformat()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _load_json(path: Path) -> dict[str, Any]:
|
|
14
|
+
return json.loads(path.read_text(encoding='utf-8'))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _dump_json(path: Path, payload: Any) -> None:
|
|
18
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
19
|
+
path.write_text(json.dumps(payload, indent=2, sort_keys=True) + '\n', encoding='utf-8')
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _module_version(name: str) -> str | None:
|
|
23
|
+
try:
|
|
24
|
+
return importlib.metadata.version(name)
|
|
25
|
+
except importlib.metadata.PackageNotFoundError:
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _command_option(command: Sequence[str], option: str) -> str | None:
|
|
30
|
+
try:
|
|
31
|
+
index = list(command).index(option)
|
|
32
|
+
except ValueError:
|
|
33
|
+
return None
|
|
34
|
+
next_index = index + 1
|
|
35
|
+
if next_index >= len(command):
|
|
36
|
+
return None
|
|
37
|
+
return str(command[next_index])
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _module_name(command: Sequence[str]) -> str | None:
|
|
41
|
+
try:
|
|
42
|
+
index = list(command).index('-m')
|
|
43
|
+
except ValueError:
|
|
44
|
+
return None
|
|
45
|
+
next_index = index + 1
|
|
46
|
+
if next_index >= len(command):
|
|
47
|
+
return None
|
|
48
|
+
return str(command[next_index])
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _path_ready(entry: Mapping[str, Any] | None) -> bool:
|
|
52
|
+
if not isinstance(entry, Mapping):
|
|
53
|
+
return False
|
|
54
|
+
return bool(entry.get('exists')) and bool(entry.get('is_file'))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _default_certificate_inputs(repo_root: Path, peer_command: Sequence[str]) -> dict[str, Any]:
|
|
58
|
+
def _entry(option: str) -> dict[str, Any]:
|
|
59
|
+
value = _command_option(peer_command, option)
|
|
60
|
+
if value is None:
|
|
61
|
+
return {'path': None, 'exists': False, 'is_file': False}
|
|
62
|
+
candidate = repo_root / value
|
|
63
|
+
return {
|
|
64
|
+
'path': value,
|
|
65
|
+
'exists': candidate.exists(),
|
|
66
|
+
'is_file': candidate.is_file(),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
ca = _entry('--cacert')
|
|
70
|
+
cert = _entry('--client-cert')
|
|
71
|
+
key = _entry('--client-key')
|
|
72
|
+
client_material_requested = bool(cert['path'] or key['path'])
|
|
73
|
+
client_material_ready = (not client_material_requested) or (bool(cert['exists']) and bool(key['exists']))
|
|
74
|
+
return {
|
|
75
|
+
'ca_cert': ca,
|
|
76
|
+
'client_cert': cert,
|
|
77
|
+
'client_key': key,
|
|
78
|
+
'client_material_requested': client_material_requested,
|
|
79
|
+
'client_material_ready': client_material_ready,
|
|
80
|
+
'ready': bool(ca['exists']) and client_material_ready,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _scenario_kind(scenario_id: str) -> str:
|
|
85
|
+
if 'websocket' in scenario_id:
|
|
86
|
+
return 'http3_websocket_adapter'
|
|
87
|
+
return 'http3_client_adapter'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib.metadata
|
|
4
|
+
import json
|
|
5
|
+
import shutil
|
|
6
|
+
import sys
|
|
7
|
+
import tempfile
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Iterable, Mapping, Sequence
|
|
11
|
+
|
|
12
|
+
from ..interop_runner import run_external_matrix
|
|
13
|
+
from ..release_gates import evaluate_promotion_target, evaluate_release_gates
|
|
14
|
+
|
|
15
|
+
DEFAULT_PRELIGHT_SCENARIOS: tuple[str, ...] = (
|
|
16
|
+
'http3-server-aioquic-client-post',
|
|
17
|
+
'websocket-http3-server-aioquic-client',
|
|
18
|
+
)
|
|
19
|
+
DEFAULT_BUNDLE_NAME = 'tigrcorn-aioquic-adapter-preflight-bundle'
|
|
20
|
+
DEFAULT_STATUS_DOC = 'docs/review/conformance/AIOQUIC_ADAPTER_PREFLIGHT.md'
|
|
21
|
+
DEFAULT_STATUS_JSON = 'docs/review/conformance/aioquic_adapter_preflight.current.json'
|
|
22
|
+
DEFAULT_DELIVERY_NOTES = 'docs/review/conformance/delivery/DELIVERY_NOTES_AIOQUIC_ADAPTER_PREFLIGHT.md'
|
|
23
|
+
DEFAULT_MATRIX_PATH = 'docs/review/conformance/external_matrix.release.json'
|
|
24
|
+
DEFAULT_RELEASE_ROOT = 'docs/review/conformance/releases/0.3.9/release-0.3.9'
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .imports import *
|
|
4
|
+
from .helpers import *
|
|
5
|
+
from .helpers import _default_certificate_inputs, _load_json, _module_name, _path_ready, _scenario_kind
|
|
6
|
+
|
|
7
|
+
def _extract_scenario_record(repo_root: Path, bundle_root: Path, scenario_id: str) -> dict[str, Any]:
|
|
8
|
+
scenario_root = bundle_root / scenario_id
|
|
9
|
+
result = _load_json(scenario_root / 'result.json')
|
|
10
|
+
commands = _load_json(scenario_root / 'command.json')
|
|
11
|
+
versions = _load_json(scenario_root / 'versions.json')
|
|
12
|
+
peer_command = [str(item) for item in commands['peer']['command']]
|
|
13
|
+
negotiation = dict((result.get('negotiation') or {}).get('peer') or {})
|
|
14
|
+
transcript = dict((result.get('transcript') or {}).get('peer') or {})
|
|
15
|
+
transcript_quic = dict(transcript.get('quic') or {})
|
|
16
|
+
certificate_inputs = dict(negotiation.get('certificate_inputs') or _default_certificate_inputs(repo_root, peer_command))
|
|
17
|
+
handshake_complete = bool(
|
|
18
|
+
negotiation.get('handshake_complete')
|
|
19
|
+
or transcript_quic.get('handshake_complete')
|
|
20
|
+
or (result.get('passed') and (result.get('peer') or {}).get('exit_code') == 0 and negotiation.get('protocol') == 'h3')
|
|
21
|
+
)
|
|
22
|
+
artifacts = result.get('artifacts') or {}
|
|
23
|
+
response = dict(transcript.get('response') or {})
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
'scenario_id': scenario_id,
|
|
27
|
+
'kind': _scenario_kind(scenario_id),
|
|
28
|
+
'passed': bool(result.get('passed')),
|
|
29
|
+
'peer_exit_code': int((result.get('peer') or {}).get('exit_code') or 0),
|
|
30
|
+
'peer_module': _module_name(peer_command),
|
|
31
|
+
'peer_command': peer_command,
|
|
32
|
+
'peer_version': (versions.get('peer') or {}).get('implementation_version'),
|
|
33
|
+
'protocol': negotiation.get('protocol'),
|
|
34
|
+
'tls_version': negotiation.get('tls_version'),
|
|
35
|
+
'server_name': negotiation.get('server_name'),
|
|
36
|
+
'handshake_complete': handshake_complete,
|
|
37
|
+
'retry_observed': bool(negotiation.get('retry_observed')),
|
|
38
|
+
'negotiation_metadata_emitted': bool((artifacts.get('peer_negotiation') or {}).get('exists')),
|
|
39
|
+
'transcript_emitted': bool((artifacts.get('peer_transcript') or {}).get('exists')),
|
|
40
|
+
'packet_trace_exists': bool((artifacts.get('packet_trace') or {}).get('exists')),
|
|
41
|
+
'qlog_exists': bool((artifacts.get('qlog') or {}).get('exists')),
|
|
42
|
+
'certificate_inputs': certificate_inputs,
|
|
43
|
+
'certificate_inputs_ready': bool(negotiation.get('certificate_inputs_ready', certificate_inputs.get('ready'))),
|
|
44
|
+
'ca_cert_path': (certificate_inputs.get('ca_cert') or {}).get('path'),
|
|
45
|
+
'ca_cert_exists': _path_ready(certificate_inputs.get('ca_cert') or {}),
|
|
46
|
+
'client_material_requested': bool(certificate_inputs.get('client_material_requested')),
|
|
47
|
+
'response_status': response.get('status'),
|
|
48
|
+
'websocket_connect_protocol_enabled': negotiation.get('connect_protocol_enabled'),
|
|
49
|
+
'websocket_negotiated_extensions': list(negotiation.get('negotiated_extensions') or []),
|
|
50
|
+
'artifact_dir': str(scenario_root.relative_to(bundle_root)),
|
|
51
|
+
'result_path': str((scenario_root / 'result.json').relative_to(bundle_root)),
|
|
52
|
+
'peer_negotiation_path': str((scenario_root / 'peer_negotiation.json').relative_to(bundle_root)),
|
|
53
|
+
'peer_transcript_path': str((scenario_root / 'peer_transcript.json').relative_to(bundle_root)),
|
|
54
|
+
}
|