certora-prover-cli 0.1.0__tar.gz → 0.2.1__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.
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/PKG-INFO +3 -4
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/pyproject.toml +4 -1
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/_version.py +2 -2
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/aws_data_fetcher.py +19 -11
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/base_data_fetcher.py +19 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/data_fetcher.py +20 -12
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/local_data_fetcher.py +28 -29
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/main.py +42 -3
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/tree_parser.py +1 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/models.py +3 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/prover_cli.py +38 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/.gitignore +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/LICENSE +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/README.md +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/__init__.py +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/__init__.py +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/cache.py +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/url_utils.py +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/auth.py +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/aws_auth.py +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/breadcrumb.py +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/exceptions.py +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/group_summary.py +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/job_report.py +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/parsers.py +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/prover_api.py +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/py.typed +0 -0
- {certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/sighash.py +0 -0
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: certora-prover-cli
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A Python API and CLI for parsing outputs from the Certora Prover
|
|
5
5
|
Project-URL: Homepage, https://github.com/Certora/ProverCLI
|
|
6
6
|
Project-URL: Repository, https://github.com/Certora/ProverCLI
|
|
7
7
|
Project-URL: Bug Tracker, https://github.com/Certora/ProverCLI/issues
|
|
8
8
|
Author-email: Certora <support@certora.com>
|
|
9
9
|
Maintainer-email: Certora <support@certora.com>
|
|
10
|
-
License
|
|
11
|
-
License-File: LICENSE
|
|
10
|
+
License: GPL-3.0-only
|
|
12
11
|
Classifier: Development Status :: 3 - Alpha
|
|
13
12
|
Classifier: Intended Audience :: Developers
|
|
14
13
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
|
|
2
|
+
# hatchling >=1.27 emits wheel Metadata-Version 2.5 (PEP 639 license fields),
|
|
3
|
+
# which the pinned pypa/gh-action-pypi-publish twine cannot validate. Cap below
|
|
4
|
+
# 1.27 so wheels carry Metadata-Version 2.3, accepted by that action and PyPI.
|
|
5
|
+
requires = ["hatchling<1.27", "hatch-vcs"]
|
|
3
6
|
build-backend = "hatchling.build"
|
|
4
7
|
|
|
5
8
|
[project]
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.1
|
|
22
|
-
__version_tuple__ = version_tuple = (0,
|
|
21
|
+
__version__ = version = '0.2.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 1)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -7,6 +7,7 @@ AWS Lambda-based data fetcher for CI environments.
|
|
|
7
7
|
Uses AWS SigV4 authentication to access Certora API through Lambda.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
import json
|
|
10
11
|
import os
|
|
11
12
|
from typing import Any, Dict, List, Optional, cast
|
|
12
13
|
|
|
@@ -199,37 +200,44 @@ class AWSDataFetcher(BaseDataFetcher):
|
|
|
199
200
|
raise ProverAPIError(f"Failed to fetch tree-view data for {job_identifier}: {e}")
|
|
200
201
|
|
|
201
202
|
def fetch_statsdata(self, job_identifier: str) -> Dict[str, Any]:
|
|
202
|
-
"""
|
|
203
|
-
|
|
203
|
+
"""Fetch statsdata.json for a job via Lambda."""
|
|
204
|
+
try:
|
|
205
|
+
return cast(Dict[str, Any], json.loads(self.fetch_output_file(job_identifier, "statsdata.json")))
|
|
206
|
+
except json.JSONDecodeError as e:
|
|
207
|
+
raise ProverAPIError(f"Failed to parse statsdata.json for {job_identifier}: {e}")
|
|
208
|
+
|
|
209
|
+
def fetch_output_file(self, job_identifier: str, rel_path: str) -> str:
|
|
210
|
+
"""Fetch the raw text of a Reports/-relative output file (e.g. unsat_core_map.json).
|
|
204
211
|
|
|
205
212
|
Args:
|
|
206
|
-
job_identifier: Job ID
|
|
213
|
+
job_identifier: Job ID
|
|
214
|
+
rel_path: Path of the file relative to the job's Reports/ directory
|
|
207
215
|
|
|
208
216
|
Returns:
|
|
209
|
-
|
|
217
|
+
The file contents as text
|
|
210
218
|
|
|
211
219
|
Raises:
|
|
212
220
|
AuthenticationError: If authentication fails
|
|
213
|
-
JobNotFoundError: If job is not found
|
|
214
|
-
ProverAPIError: If API call fails
|
|
221
|
+
JobNotFoundError: If the job or file is not found
|
|
222
|
+
ProverAPIError: If the API call fails
|
|
215
223
|
"""
|
|
216
|
-
endpoint = f"{self.base_url}/v1/domain/jobs/{job_identifier}/f/
|
|
224
|
+
endpoint = f"{self.base_url}/v1/domain/jobs/{job_identifier}/f/{rel_path}"
|
|
217
225
|
|
|
218
226
|
try:
|
|
219
227
|
response = self._make_signed_request("GET", endpoint)
|
|
220
228
|
|
|
221
229
|
if response.status_code == 200:
|
|
222
|
-
return
|
|
230
|
+
return response.text
|
|
223
231
|
elif response.status_code == 401:
|
|
224
232
|
raise AuthenticationError("Authentication failed - check AWS credentials")
|
|
225
233
|
elif response.status_code == 404:
|
|
226
|
-
raise JobNotFoundError(f"
|
|
234
|
+
raise JobNotFoundError(f"Output file not found for job {job_identifier}: {rel_path}")
|
|
227
235
|
else:
|
|
228
236
|
response.raise_for_status()
|
|
229
|
-
return
|
|
237
|
+
return response.text
|
|
230
238
|
|
|
231
239
|
except requests.exceptions.RequestException as e:
|
|
232
|
-
raise ProverAPIError(f"Failed to fetch
|
|
240
|
+
raise ProverAPIError(f"Failed to fetch output file {rel_path} for {job_identifier}: {e}")
|
|
233
241
|
|
|
234
242
|
def fetch_outputs(self, job_identifier: str) -> bytes:
|
|
235
243
|
"""
|
|
@@ -159,6 +159,25 @@ class BaseDataFetcher(ABC):
|
|
|
159
159
|
"""
|
|
160
160
|
pass
|
|
161
161
|
|
|
162
|
+
@abstractmethod
|
|
163
|
+
def fetch_output_file(self, job_identifier: str, rel_path: str) -> str:
|
|
164
|
+
"""
|
|
165
|
+
Fetch the raw text of a file under the job's Reports/ output dir.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
job_identifier: Job identifier (job ID for remote, emv path for local)
|
|
169
|
+
rel_path: Reports/-relative filename (e.g. "unsat_core_map.json", "UnsatCoreTAC-....txt")
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Raw file content as a string
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
AuthenticationError: If authentication fails (remote fetchers)
|
|
176
|
+
JobNotFoundError: If the file is not found
|
|
177
|
+
ProverAPIError: If fetch fails
|
|
178
|
+
"""
|
|
179
|
+
pass
|
|
180
|
+
|
|
162
181
|
@abstractmethod
|
|
163
182
|
def fetch_alert_report(self, job_identifier: str) -> List[Dict[str, Any]]:
|
|
164
183
|
"""
|
{certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/data_fetcher.py
RENAMED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
Low-level data fetching utilities for the Prover API.
|
|
7
7
|
"""
|
|
8
8
|
import functools
|
|
9
|
+
import json
|
|
9
10
|
from typing import Any, Callable, Dict, List, Optional, TypeVar, cast
|
|
10
11
|
|
|
11
12
|
import requests
|
|
@@ -255,39 +256,46 @@ class DataFetcher(BaseDataFetcher):
|
|
|
255
256
|
except requests.exceptions.RequestException as e:
|
|
256
257
|
raise ProverAPIError(f"Failed to fetch tree-view data for {job_identifier}: {e}")
|
|
257
258
|
|
|
258
|
-
@handle_token_expiration
|
|
259
259
|
def fetch_statsdata(self, job_identifier: str) -> Dict[str, Any]:
|
|
260
|
-
"""
|
|
261
|
-
|
|
260
|
+
"""Fetch statsdata.json for a job."""
|
|
261
|
+
try:
|
|
262
|
+
return cast(Dict[str, Any], json.loads(self.fetch_output_file(job_identifier, "statsdata.json")))
|
|
263
|
+
except json.JSONDecodeError as e:
|
|
264
|
+
raise ProverAPIError(f"Failed to parse statsdata.json for {job_identifier}: {e}")
|
|
265
|
+
|
|
266
|
+
@handle_token_expiration
|
|
267
|
+
def fetch_output_file(self, job_identifier: str, rel_path: str) -> str:
|
|
268
|
+
"""Fetch the raw text of a Reports/-relative output file (e.g. unsat_core_map.json).
|
|
262
269
|
|
|
263
270
|
Args:
|
|
264
|
-
job_identifier: Job ID
|
|
271
|
+
job_identifier: Job ID
|
|
272
|
+
rel_path: Path of the file relative to the job's Reports/ directory
|
|
265
273
|
|
|
266
274
|
Returns:
|
|
267
|
-
|
|
275
|
+
The file contents as text
|
|
268
276
|
|
|
269
277
|
Raises:
|
|
270
278
|
AuthenticationError: If authentication fails
|
|
271
|
-
JobNotFoundError: If job is not found
|
|
272
|
-
ProverAPIError: If API call fails
|
|
279
|
+
JobNotFoundError: If the job or file is not found
|
|
280
|
+
ProverAPIError: If the API call fails
|
|
273
281
|
"""
|
|
274
|
-
endpoint = f"{self.api_base_url}/v1/domain/jobs/{job_identifier}/f/
|
|
282
|
+
endpoint = f"{self.api_base_url}/v1/domain/jobs/{job_identifier}/f/{rel_path}"
|
|
275
283
|
|
|
276
284
|
try:
|
|
277
285
|
response = self.session.get(endpoint)
|
|
278
286
|
|
|
279
287
|
if response.status_code == 200:
|
|
280
|
-
return
|
|
288
|
+
return response.text
|
|
281
289
|
elif response.status_code == 401:
|
|
282
290
|
raise AuthenticationError("Authentication failed - check CERTORAKEY")
|
|
283
291
|
elif response.status_code == 404:
|
|
284
|
-
raise JobNotFoundError(f"
|
|
292
|
+
raise JobNotFoundError(f"Output file not found for job {job_identifier}: {rel_path}")
|
|
285
293
|
else:
|
|
286
294
|
response.raise_for_status()
|
|
287
|
-
return
|
|
295
|
+
return response.text
|
|
288
296
|
|
|
289
297
|
except requests.exceptions.RequestException as e:
|
|
290
|
-
raise ProverAPIError(f"Failed to fetch
|
|
298
|
+
raise ProverAPIError(f"Failed to fetch output file {rel_path} for {job_identifier}: {e}")
|
|
291
299
|
|
|
292
300
|
def fetch_outputs(self, job_identifier: str) -> bytes:
|
|
293
301
|
"""
|
|
@@ -200,38 +200,11 @@ class LocalDataFetcher(BaseDataFetcher):
|
|
|
200
200
|
raise ProverAPIError("cancel_jobs is not supported for local prover outputs")
|
|
201
201
|
|
|
202
202
|
def fetch_statsdata(self, job_identifier: str) -> Dict[str, Any]:
|
|
203
|
-
"""
|
|
204
|
-
Fetch statsdata.json from local emv-* folder.
|
|
205
|
-
|
|
206
|
-
Args:
|
|
207
|
-
job_identifier: Path to the emv-* folder
|
|
208
|
-
|
|
209
|
-
Returns:
|
|
210
|
-
Stats data from statsdata.json
|
|
211
|
-
|
|
212
|
-
Raises:
|
|
213
|
-
JobNotFoundError: If statsdata.json is not found
|
|
214
|
-
ProverAPIError: If local files cannot be read
|
|
215
|
-
"""
|
|
216
|
-
emv_path = os.path.abspath(job_identifier)
|
|
217
|
-
|
|
218
|
-
if not os.path.exists(emv_path):
|
|
219
|
-
raise JobNotFoundError(f"Local prover output path not found: {job_identifier}")
|
|
220
|
-
|
|
203
|
+
"""Fetch statsdata.json from local emv-* folder."""
|
|
221
204
|
try:
|
|
222
|
-
|
|
223
|
-
statsdata_path = os.path.join(emv_path, "Reports", "statsdata.json")
|
|
224
|
-
|
|
225
|
-
if not os.path.exists(statsdata_path):
|
|
226
|
-
raise JobNotFoundError(f"Stats data not found for local job {job_identifier}")
|
|
227
|
-
|
|
228
|
-
with open(statsdata_path, "r") as f:
|
|
229
|
-
return json.load(f)
|
|
230
|
-
|
|
205
|
+
return json.loads(self.fetch_output_file(job_identifier, "statsdata.json"))
|
|
231
206
|
except json.JSONDecodeError as e:
|
|
232
207
|
raise ProverAPIError(f"Failed to parse statsdata.json: {e}")
|
|
233
|
-
except Exception as e:
|
|
234
|
-
raise ProverAPIError(f"Failed to read stats data from {job_identifier}: {e}")
|
|
235
208
|
|
|
236
209
|
def fetch_outputs(self, job_identifier: str) -> bytes:
|
|
237
210
|
"""
|
|
@@ -266,6 +239,32 @@ class LocalDataFetcher(BaseDataFetcher):
|
|
|
266
239
|
"""
|
|
267
240
|
raise ProverAPIError("fetch_source_file_content is not supported for local prover outputs")
|
|
268
241
|
|
|
242
|
+
def fetch_output_file(self, job_identifier: str, rel_path: str) -> str:
|
|
243
|
+
"""Read a Reports/-relative output file from a local emv-* folder.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
job_identifier: Path to the emv-* folder
|
|
247
|
+
rel_path: Path of the file relative to the folder's Reports/ directory
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
The file contents as text
|
|
251
|
+
|
|
252
|
+
Raises:
|
|
253
|
+
JobNotFoundError: If the folder or file is not found
|
|
254
|
+
ProverAPIError: If the file cannot be read
|
|
255
|
+
"""
|
|
256
|
+
emv_path = os.path.abspath(job_identifier)
|
|
257
|
+
if not os.path.exists(emv_path):
|
|
258
|
+
raise JobNotFoundError(f"Local prover output path not found: {job_identifier}")
|
|
259
|
+
file_path = os.path.join(emv_path, "Reports", rel_path)
|
|
260
|
+
if not os.path.exists(file_path):
|
|
261
|
+
raise JobNotFoundError(f"Output file not found: {rel_path}")
|
|
262
|
+
try:
|
|
263
|
+
with open(file_path, "r") as f:
|
|
264
|
+
return f.read()
|
|
265
|
+
except OSError as e:
|
|
266
|
+
raise ProverAPIError(f"Failed to read output file {rel_path}: {e}")
|
|
267
|
+
|
|
269
268
|
def fetch_alert_report(self, job_identifier: str) -> List[Dict[str, Any]]:
|
|
270
269
|
"""
|
|
271
270
|
Fetch alert report (alertReport.json) from local emv-* folder.
|
|
@@ -22,7 +22,7 @@ import requests
|
|
|
22
22
|
from ..auth import ProverAuth
|
|
23
23
|
from ..aws_auth import AWSAuth
|
|
24
24
|
from ..breadcrumb import BreadcrumbParser
|
|
25
|
-
from ..exceptions import ProverAPIError
|
|
25
|
+
from ..exceptions import JobNotFoundError, ProverAPIError
|
|
26
26
|
from ..job_report import JobAnalyzer, JobReport
|
|
27
27
|
from ..models import (
|
|
28
28
|
BreadcrumbInfo,
|
|
@@ -1135,12 +1135,27 @@ class ProverOutputAPI:
|
|
|
1135
1135
|
return tar_content
|
|
1136
1136
|
|
|
1137
1137
|
def extract_unsat_core_files(self, job_input: str, dest_dir: Path) -> List[Path]:
|
|
1138
|
-
"""Extract UnsatCoreTAC*.txt files
|
|
1138
|
+
"""Extract UnsatCoreTAC*.txt files into dest_dir.
|
|
1139
1139
|
|
|
1140
|
-
|
|
1140
|
+
Uses unsat_core_map.json to fetch only the referenced files; falls back to the
|
|
1141
|
+
full output tar when the map is absent. No filtering is applied — the caller is
|
|
1142
|
+
responsible for any filtering.
|
|
1141
1143
|
|
|
1142
1144
|
Returns list of extracted file paths.
|
|
1143
1145
|
"""
|
|
1146
|
+
dest_dir.mkdir(parents=True, exist_ok=True)
|
|
1147
|
+
core_map = self.unsat_core_map(job_input)
|
|
1148
|
+
if core_map:
|
|
1149
|
+
filenames = sorted({f for files in core_map.values() for f in files})
|
|
1150
|
+
extracted: List[Path] = []
|
|
1151
|
+
for name in filenames:
|
|
1152
|
+
file_dest = dest_dir / Path(name).name
|
|
1153
|
+
file_dest.write_text(self.fetch_output_file(job_input, name), encoding="utf-8")
|
|
1154
|
+
extracted.append(file_dest)
|
|
1155
|
+
return extracted
|
|
1156
|
+
return self._extract_unsat_core_files_from_tar(job_input, dest_dir)
|
|
1157
|
+
|
|
1158
|
+
def _extract_unsat_core_files_from_tar(self, job_input: str, dest_dir: Path) -> List[Path]:
|
|
1144
1159
|
job_identifier = self._extract_job_identifier(job_input)
|
|
1145
1160
|
dest_dir.mkdir(parents=True, exist_ok=True)
|
|
1146
1161
|
tar_content = self._fetch_outputs_cached(job_identifier)
|
|
@@ -1159,6 +1174,30 @@ class ProverOutputAPI:
|
|
|
1159
1174
|
extracted.append(file_dest)
|
|
1160
1175
|
return extracted
|
|
1161
1176
|
|
|
1177
|
+
def fetch_output_file(self, job_input: str, rel_path: str) -> str:
|
|
1178
|
+
"""Fetch the raw text of a Reports/-relative output file (e.g. 'unsat_core_map.json')."""
|
|
1179
|
+
job_identifier = self._extract_job_identifier(job_input)
|
|
1180
|
+
return self.data_fetcher.fetch_output_file(job_identifier, rel_path)
|
|
1181
|
+
|
|
1182
|
+
def unsat_core_map(self, job_input: str) -> Dict[str, List[str]]:
|
|
1183
|
+
"""The job's `{ ruleId -> [UnsatCoreTAC .txt filenames] }` map, or {} if the job has none."""
|
|
1184
|
+
try:
|
|
1185
|
+
content = self.fetch_output_file(job_input, "unsat_core_map.json")
|
|
1186
|
+
except JobNotFoundError:
|
|
1187
|
+
return {}
|
|
1188
|
+
try:
|
|
1189
|
+
return json.loads(content)
|
|
1190
|
+
except json.JSONDecodeError as e:
|
|
1191
|
+
raise ProverAPIError(f"Failed to parse unsat_core_map.json for {job_input}: {e}")
|
|
1192
|
+
|
|
1193
|
+
def unsat_core_filenames(self, job_input: str, rule_id: str) -> List[str]:
|
|
1194
|
+
"""UnsatCoreTAC .txt filenames for a rule (by its treeView ruleId); [] if none."""
|
|
1195
|
+
return self.unsat_core_map(job_input).get(rule_id, [])
|
|
1196
|
+
|
|
1197
|
+
def read_unsat_cores(self, job_input: str, rule_id: str) -> List[str]:
|
|
1198
|
+
"""Contents of a rule's UnsatCoreTAC .txt dumps (by its treeView ruleId)."""
|
|
1199
|
+
return [self.fetch_output_file(job_input, name) for name in self.unsat_core_filenames(job_input, rule_id)]
|
|
1200
|
+
|
|
1162
1201
|
def extract_certora_sources(self, job_input: str, dest_dir: Path) -> None:
|
|
1163
1202
|
"""Extract the .certora_sources tree from the job output tar into dest_dir.
|
|
1164
1203
|
|
{certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/tree_parser.py
RENAMED
|
@@ -285,6 +285,7 @@ class TreeParser:
|
|
|
285
285
|
return CheckResult(
|
|
286
286
|
rule_name=self._get_rule_name_from_context(context),
|
|
287
287
|
method_name=method_name, # Use the explicitly tracked method name
|
|
288
|
+
rule_id=node.get("ruleId"),
|
|
288
289
|
contract_name=contract_name,
|
|
289
290
|
method_only=method_only,
|
|
290
291
|
assert_message=assert_message,
|
|
@@ -31,6 +31,7 @@ class NodeStatus(str, Enum):
|
|
|
31
31
|
|
|
32
32
|
VIOLATED = "VIOLATED"
|
|
33
33
|
VERIFIED = "VERIFIED"
|
|
34
|
+
SANITY_FAILED = "SANITY_FAILED"
|
|
34
35
|
TIMEOUT = "TIMEOUT"
|
|
35
36
|
ERROR = "ERROR"
|
|
36
37
|
RUNNING = "RUNNING"
|
|
@@ -221,6 +222,7 @@ class CheckResult:
|
|
|
221
222
|
node_type: NodeType
|
|
222
223
|
contract_name: Optional[str] = None
|
|
223
224
|
method_only: Optional[str] = None
|
|
225
|
+
rule_id: Optional[str] = None
|
|
224
226
|
ui_id: Optional[str] = None
|
|
225
227
|
output_files: List[str] = field(default_factory=list)
|
|
226
228
|
debug_trace_file: Optional[str] = None
|
|
@@ -255,6 +257,7 @@ class CheckResult:
|
|
|
255
257
|
return {
|
|
256
258
|
"rule_name": self.rule_name,
|
|
257
259
|
"method_name": self.method_name,
|
|
260
|
+
"rule_id": self.rule_id,
|
|
258
261
|
"contract_name": self.contract_name,
|
|
259
262
|
"method_only": self.method_only,
|
|
260
263
|
"assert_message": self.assert_message,
|
{certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/prover_cli.py
RENAMED
|
@@ -13,6 +13,7 @@ import argparse
|
|
|
13
13
|
import json
|
|
14
14
|
import os
|
|
15
15
|
import sys
|
|
16
|
+
from pathlib import Path
|
|
16
17
|
|
|
17
18
|
from .api import ProverOutputAPI
|
|
18
19
|
from .breadcrumb import format_breadcrumbs_text
|
|
@@ -124,6 +125,7 @@ Examples:
|
|
|
124
125
|
%(prog)s --job-id 12345 --statsdata
|
|
125
126
|
%(prog)s --job-id 12345 --full-report
|
|
126
127
|
%(prog)s --job-id 12345 --full-report --format json
|
|
128
|
+
%(prog)s --job-id 12345 --download-sources ./job_sources
|
|
127
129
|
%(prog)s --cancel 12345 67890
|
|
128
130
|
|
|
129
131
|
# Group summary
|
|
@@ -215,6 +217,17 @@ Environment Variables:
|
|
|
215
217
|
"ProverOutputAPI.get_job_report() and intended as the one-shot triage call."
|
|
216
218
|
),
|
|
217
219
|
)
|
|
220
|
+
parser.add_argument(
|
|
221
|
+
"--download-sources",
|
|
222
|
+
nargs="?",
|
|
223
|
+
const=".",
|
|
224
|
+
metavar="DEST_DIR",
|
|
225
|
+
help=(
|
|
226
|
+
"Download the job's source files into DEST_DIR/inputs/.certora_sources/ "
|
|
227
|
+
"(DEST_DIR defaults to the current directory). Files are fetched "
|
|
228
|
+
"individually in parallel; a completed fetch is skipped on rerun."
|
|
229
|
+
),
|
|
230
|
+
)
|
|
218
231
|
parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
|
|
219
232
|
parser.add_argument("--format", choices=["json", "text"], default="text", help="Output format")
|
|
220
233
|
parser.add_argument(
|
|
@@ -477,6 +490,22 @@ Environment Variables:
|
|
|
477
490
|
report = api.get_job_report(job_input)
|
|
478
491
|
result = {"job_id": job_input, "report": report.to_dict()}
|
|
479
492
|
|
|
493
|
+
elif args.download_sources:
|
|
494
|
+
# Download the job's source files (remote jobs only)
|
|
495
|
+
dest_dir = Path(args.download_sources)
|
|
496
|
+
api.fetch_job_sources(job_input, dest_dir)
|
|
497
|
+
sources_dir = dest_dir / "inputs" / ".certora_sources"
|
|
498
|
+
files_count = sum(
|
|
499
|
+
1
|
|
500
|
+
for p in sources_dir.rglob("*")
|
|
501
|
+
if p.is_file() and p.name != ".source_fetch_complete"
|
|
502
|
+
)
|
|
503
|
+
result = {
|
|
504
|
+
"job_id": job_input,
|
|
505
|
+
"sources_dir": str(sources_dir),
|
|
506
|
+
"files_count": files_count,
|
|
507
|
+
}
|
|
508
|
+
|
|
480
509
|
else:
|
|
481
510
|
# Get violated rules (default behavior)
|
|
482
511
|
violations = api.get_violated_rules(job_input)
|
|
@@ -516,6 +545,11 @@ Environment Variables:
|
|
|
516
545
|
print(f"Found {count} alerts. Results written to {args.output}")
|
|
517
546
|
elif args.full_report:
|
|
518
547
|
print(f"✅ Full report retrieved. Results written to {args.output}")
|
|
548
|
+
elif args.download_sources:
|
|
549
|
+
print(
|
|
550
|
+
f"Downloaded {result['files_count']} source files to {result['sources_dir']}. "
|
|
551
|
+
f"Results written to {args.output}"
|
|
552
|
+
)
|
|
519
553
|
else:
|
|
520
554
|
count = result.get("assert_nodes_count", 0)
|
|
521
555
|
print(f"Found {count} violations. Results written to {args.output}")
|
|
@@ -616,6 +650,10 @@ Environment Variables:
|
|
|
616
650
|
else:
|
|
617
651
|
print("\n✅ No alerts found!")
|
|
618
652
|
|
|
653
|
+
elif args.download_sources:
|
|
654
|
+
print(f"Job ID: {job_input}")
|
|
655
|
+
print(f"✅ Downloaded {result['files_count']} source files to {result['sources_dir']}")
|
|
656
|
+
|
|
619
657
|
elif args.full_report:
|
|
620
658
|
# Summary view of the full report. Full content via --format json.
|
|
621
659
|
report = result["report"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/__init__.py
RENAMED
|
File without changes
|
{certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/cache.py
RENAMED
|
File without changes
|
{certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/api/url_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/breadcrumb.py
RENAMED
|
File without changes
|
{certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/exceptions.py
RENAMED
|
File without changes
|
{certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/group_summary.py
RENAMED
|
File without changes
|
{certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/job_report.py
RENAMED
|
File without changes
|
|
File without changes
|
{certora_prover_cli-0.1.0 → certora_prover_cli-0.2.1}/src/prover_output_utility/prover_api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|