Skip to content

Commit 4a51152

Browse files
committed
refactor(pytest): integrate EIP report generation into spec_version_checker plugin
Move EIP version report generation from external script into the spec_version_checker pytest plugin to eliminate brittle regex parsing of console output and improve maintainability. - Add EIPVersionFailure dataclass to capture failure data directly - Implement generate_eip_report_markdown() function within plugin - Use pytest_sessionfinish hook to generate reports automatically
1 parent 43e710c commit 4a51152

File tree

4 files changed

+136
-154
lines changed

4 files changed

+136
-154
lines changed

.github/scripts/generate_eip_report.py

Lines changed: 0 additions & 147 deletions
This file was deleted.

.github/workflows/check_eip_versions.yaml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,6 @@ jobs:
3939
# Always return success to GitHub Actions
4040
exit 0
4141
42-
- name: Generate report file
43-
if: steps.check-eip.outputs.exit_code != 0
44-
run: |
45-
uv run python .github/scripts/generate_eip_report.py ./reports/eip_check_output.txt ./reports/outdated_eips.md
46-
4742
- name: Create Issue From File
4843
if: steps.check-eip.outputs.exit_code != 0
4944
uses: peter-evans/create-issue-from-file@e8ef132d6df98ed982188e460ebb3b5d4ef3a9cd

docs/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ Users can select any of the artifacts depending on their testing needs for their
3838
- 🐞 zkEVM marked tests have been removed from `tests-deployed` tox environment into its own separate workflow `tests-deployed-zkevm` and are filled by `evmone-t8n` ([#1617](https://github.com/ethereum/execution-spec-tests/pull/1617)).
3939
- ✨ Field `postStateHash` is now added to all `blockchain_test` and `blockchain_test_engine` tests that use `exclude_full_post_state_in_output` in place of `postState`. Fixes `evmone-blockchaintest` test consumption and indirectly fixes coverage runs for these tests ([#1667](https://github.com/ethereum/execution-spec-tests/pull/1667)).
4040

41+
#### `check_eip_versions`
42+
43+
- 🔀 Refactor: Move EIP version report generation from external script into the `spec_version_checker` pytest plugin to eliminate brittle regex parsing of console output and improve maintainability.
44+
4145
#### `consume`
4246

4347
#### `execute`

src/pytest_plugins/spec_version_checker/spec_version_checker.py

Lines changed: 132 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import os
77
import re
88
import textwrap
9+
from dataclasses import dataclass
10+
from pathlib import Path
911
from types import ModuleType
1012
from typing import Any, List, Optional, Set
1113

@@ -22,6 +24,17 @@
2224
)
2325

2426

27+
@dataclass
28+
class EIPVersionFailure:
29+
"""Represents a failure in EIP version check."""
30+
31+
file_path: str
32+
eip_spec_name: str
33+
eip_spec_url: str
34+
referenced_version: str
35+
latest_version: str
36+
37+
2538
def pytest_addoption(parser):
2639
"""Add Github token option to pytest command line options."""
2740
group = parser.getgroup(
@@ -52,6 +65,8 @@ def pytest_configure(config):
5265
"eip_version_check: a test that tests the reference spec defined in an EIP test module.",
5366
)
5467

68+
config._eip_version_failures = []
69+
5570
github_token = config.getoption("github_token") or os.environ.get("GITHUB_TOKEN")
5671

5772
if not github_token:
@@ -113,7 +128,11 @@ def is_test_for_an_eip(input_string: str) -> bool:
113128
return False
114129

115130

116-
def test_eip_spec_version(module: ModuleType, github_token: Optional[str] = None):
131+
def test_eip_spec_version(
132+
module: ModuleType,
133+
github_token: Optional[str] = None,
134+
config: Optional[pytest.Config] = None,
135+
):
117136
"""
118137
Test that the ReferenceSpec object as defined in the test module
119138
is not outdated when compared to the remote hash from
@@ -122,8 +141,18 @@ def test_eip_spec_version(module: ModuleType, github_token: Optional[str] = None
122141
Args:
123142
module: Module to test
124143
github_token: Optional GitHub token for API authentication
144+
config: Pytest config object, which must be provided.
125145
126146
"""
147+
# Ensure config is available
148+
if config is None:
149+
# This case should ideally not happen if called via EIPSpecTestItem.runtest
150+
# or if pytest passes config correctly in other scenarios.
151+
# Fallback or error if essential. For now, assume it's provided.
152+
pytest.fail(
153+
"pytest.Config not available in test_eip_spec_version. "
154+
"This is an issue with the plugin itself."
155+
)
127156
ref_spec = get_ref_spec_from_module(module, github_token=github_token)
128157
assert ref_spec, "No reference spec object defined"
129158

@@ -143,6 +172,24 @@ def test_eip_spec_version(module: ModuleType, github_token: Optional[str] = None
143172
f"Reference spec URL: {ref_spec.api_url()}."
144173
) from e
145174

175+
if not is_up_to_date:
176+
failure_data = EIPVersionFailure(
177+
file_path=str(module.__file__),
178+
eip_spec_name=ref_spec.name(),
179+
eip_spec_url=ref_spec.api_url(),
180+
referenced_version=ref_spec.known_version(),
181+
latest_version=ref_spec.latest_version(),
182+
)
183+
# Ensure _eip_version_failures list exists, though it should by pytest_configure
184+
if hasattr(config, "_eip_version_failures"):
185+
config._eip_version_failures.append(failure_data)
186+
else:
187+
# This case should not happen if pytest_configure runs as expected.
188+
# Log an error or handle as appropriate.
189+
print(
190+
"Warning: config._eip_version_failures not found. Failure data cannot be recorded."
191+
)
192+
146193
assert is_up_to_date, message
147194

148195

@@ -189,7 +236,9 @@ def from_parent(cls, parent: Node, **kw: Any) -> "EIPSpecTestItem":
189236

190237
def runtest(self) -> None:
191238
"""Define the test to execute for this item."""
192-
test_eip_spec_version(self.module, github_token=self.github_token)
239+
test_eip_spec_version(
240+
self.module, github_token=self.github_token, config=self.session.config
241+
)
193242

194243
def reportinfo(self) -> tuple[str, int, str]:
195244
"""
@@ -217,3 +266,84 @@ def pytest_collection_modifyitems(
217266
for item in new_test_eip_spec_version_items:
218267
item.add_marker("eip_version_check", append=True)
219268
items.extend(new_test_eip_spec_version_items)
269+
270+
271+
def generate_eip_report_markdown(failures: List[EIPVersionFailure], run_id: str) -> str:
272+
"""Generate a markdown report for EIP version check failures."""
273+
summary_table_header = (
274+
"| File Path | EIP | Referenced Version | Latest Version |\n"
275+
"|-----------|-----|--------------------|----------------|\n"
276+
)
277+
summary_table_rows = []
278+
for failure in failures:
279+
eip_match = re.search(r"eip-?(\d+)", failure.eip_spec_name, re.IGNORECASE)
280+
if not eip_match:
281+
eip_match = re.search(r"eip-?(\d+)", failure.file_path, re.IGNORECASE)
282+
eip_number_str = f"EIP-{eip_match.group(1)}" if eip_match else "Unknown"
283+
284+
# Ensure the URL points to the commit history for better context
285+
eip_link_url = failure.eip_spec_url.replace("blob/", "commits/")
286+
287+
row = (
288+
f"| {failure.file_path} | [{eip_number_str}]({eip_link_url}) | "
289+
f"`{failure.referenced_version}` | `{failure.latest_version}` |"
290+
)
291+
summary_table_rows.append(row)
292+
293+
fail_details_parts = []
294+
for failure in failures:
295+
detail = (
296+
f"File: `{failure.file_path}`\n"
297+
f"Spec Name: `{failure.eip_spec_name}`\n"
298+
f"Spec URL: {failure.eip_spec_url}\n"
299+
f"Referenced Version: `{failure.referenced_version}`\n"
300+
f"Latest Version: `{failure.latest_version}`\n"
301+
"---\n"
302+
)
303+
fail_details_parts.append(detail)
304+
305+
report_template = textwrap.dedent(
306+
"""\
307+
# EIP Version Check Report (Run ID: {run_id})
308+
309+
## Summary of Outdated EIP References
310+
311+
{summary_table_header}{summary_table_rows_str}
312+
313+
## Outdated EIP References Details
314+
315+
{fail_details_str}
316+
"""
317+
)
318+
319+
return report_template.format(
320+
run_id=run_id,
321+
summary_table_header=summary_table_header,
322+
summary_table_rows_str="\n".join(summary_table_rows),
323+
fail_details_str="\n".join(fail_details_parts),
324+
)
325+
326+
327+
def pytest_sessionfinish(session):
328+
"""
329+
Call after the whole test session finishes.
330+
Generate a report if there are any EIP version failures.
331+
"""
332+
failures = session.config._eip_version_failures
333+
if failures:
334+
run_id = os.environ.get("GITHUB_RUN_ID", "local-run")
335+
report_content = generate_eip_report_markdown(failures, run_id)
336+
report_file_path_str = "./reports/outdated_eips.md"
337+
338+
report_file_path = Path(report_file_path_str)
339+
report_file_path.parent.mkdir(parents=True, exist_ok=True)
340+
341+
with open(report_file_path, "w") as f:
342+
f.write(report_content)
343+
344+
print(
345+
f"\nEIP Version Check: {len(failures)} outdated EIP references found. "
346+
f"Report generated at {report_file_path_str}"
347+
)
348+
else:
349+
print("\nEIP Version Check: No outdated EIP references found.")

0 commit comments

Comments
 (0)