Skip to content

Commit 3b74317

Browse files
authored
Add CBMC-running GitHub Action; (#83)
This commit adds a GitHub Action that runs the CBMC proofs in this repository upon pushes and pull requests
1 parent 409ba22 commit 3b74317

File tree

3 files changed

+140
-6
lines changed

3 files changed

+140
-6
lines changed

.github/workflows/ci.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,12 @@ jobs:
105105
with:
106106
config: .github/memory_statistics_config.json
107107
check_against: docs/doxygen/include/size_table.md
108+
proof_ci:
109+
runs-on: cbmc_ubuntu-latest_16-core
110+
steps:
111+
- name: Set up CBMC runner
112+
uses: FreeRTOS/CI-CD-Github-Actions/set_up_cbmc_runner@main
113+
- name: Run CBMC
114+
uses: FreeRTOS/CI-CD-Github-Actions/run_cbmc@main
115+
with:
116+
proofs_dir: test/cbmc/proofs
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4+
# SPDX-License-Identifier: MIT-0
5+
6+
7+
import logging
8+
import pathlib
9+
import shutil
10+
import subprocess
11+
12+
13+
_TOOLS = [
14+
"cadical",
15+
"cbmc",
16+
"cbmc-viewer",
17+
"cbmc-starter-kit-update",
18+
"kissat",
19+
"litani",
20+
]
21+
22+
23+
def _format_versions(table):
24+
lines = [
25+
"<table>",
26+
'<tr><td colspan="2" style="font-weight: bold">Tool Versions</td></tr>',
27+
]
28+
for tool, version in table.items():
29+
if version:
30+
v_str = f'<code><pre style="margin: 0">{version}</pre></code>'
31+
else:
32+
v_str = '<em>not found</em>'
33+
lines.append(
34+
f'<tr><td style="font-weight: bold; padding-right: 1em; '
35+
f'text-align: right;">{tool}:</td>'
36+
f'<td>{v_str}</td></tr>')
37+
lines.append("</table>")
38+
return "\n".join(lines)
39+
40+
41+
def _get_tool_versions():
42+
ret = {}
43+
for tool in _TOOLS:
44+
err = f"Could not determine version of {tool}: "
45+
ret[tool] = None
46+
if not shutil.which(tool):
47+
logging.error("%s'%s' not found on $PATH", err, tool)
48+
continue
49+
cmd = [tool, "--version"]
50+
proc = subprocess.Popen(cmd, text=True, stdout=subprocess.PIPE)
51+
try:
52+
out, _ = proc.communicate(timeout=10)
53+
except subprocess.TimeoutExpired:
54+
logging.error("%s'%s --version' timed out", err, tool)
55+
continue
56+
if proc.returncode:
57+
logging.error(
58+
"%s'%s --version' returned %s", err, tool, str(proc.returncode))
59+
continue
60+
ret[tool] = out.strip()
61+
return ret
62+
63+
64+
def main():
65+
exe_name = pathlib.Path(__file__).name
66+
logging.basicConfig(format=f"{exe_name}: %(message)s")
67+
68+
table = _get_tool_versions()
69+
out = _format_versions(table)
70+
print(out)
71+
72+
73+
if __name__ == "__main__":
74+
main()

test/cbmc/proofs/lib/summarize.py

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,28 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: MIT-0
33

4+
import argparse
45
import json
56
import logging
7+
import os
8+
import sys
9+
10+
11+
DESCRIPTION = """Print 2 tables in GitHub-flavored Markdown that summarize
12+
an execution of CBMC proofs."""
13+
14+
15+
def get_args():
16+
"""Parse arguments for summarize script."""
17+
parser = argparse.ArgumentParser(description=DESCRIPTION)
18+
for arg in [{
19+
"flags": ["--run-file"],
20+
"help": "path to the Litani run.json file",
21+
"required": True,
22+
}]:
23+
flags = arg.pop("flags")
24+
parser.add_argument(*flags, **arg)
25+
return parser.parse_args()
626

727

828
def _get_max_length_per_column_list(data):
@@ -56,6 +76,7 @@ def _get_status_and_proof_summaries(run_dict):
5676
run_dict
5777
A dictionary representing a Litani run.
5878
79+
5980
Returns
6081
-------
6182
A list of 2 lists.
@@ -70,8 +91,9 @@ def _get_status_and_proof_summaries(run_dict):
7091
count_statuses[status_pretty_name] += 1
7192
except KeyError:
7293
count_statuses[status_pretty_name] = 1
73-
proof = proof_pipeline["name"]
74-
proofs.append([proof, status_pretty_name])
94+
if proof_pipeline["name"] == "print_tool_versions":
95+
continue
96+
proofs.append([proof_pipeline["name"], status_pretty_name])
7597
statuses = [["Status", "Count"]]
7698
for status, count in count_statuses.items():
7799
statuses.append([status, str(count)])
@@ -83,10 +105,39 @@ def print_proof_results(out_file):
83105
Print 2 strings that summarize the proof results.
84106
When printing, each string will render as a GitHub flavored Markdown table.
85107
"""
108+
output = "## Summary of CBMC proof results\n\n"
109+
with open(out_file, encoding='utf-8') as run_json:
110+
run_dict = json.load(run_json)
111+
status_table, proof_table = _get_status_and_proof_summaries(run_dict)
112+
for summary in (status_table, proof_table):
113+
output += _get_rendered_table(summary)
114+
115+
print(output)
116+
sys.stdout.flush()
117+
118+
github_summary_file = os.getenv("GITHUB_STEP_SUMMARY")
119+
if github_summary_file:
120+
with open(github_summary_file, "a") as handle:
121+
print(output, file=handle)
122+
handle.flush()
123+
else:
124+
logging.warning(
125+
"$GITHUB_STEP_SUMMARY not set, not writing summary file")
126+
127+
msg = (
128+
"Click the 'Summary' button to view a Markdown table "
129+
"summarizing all proof results")
130+
if run_dict["status"] != "success":
131+
logging.error("Not all proofs passed.")
132+
logging.error(msg)
133+
sys.exit(1)
134+
logging.info(msg)
135+
136+
137+
if __name__ == '__main__':
138+
args = get_args()
139+
logging.basicConfig(format="%(levelname)s: %(message)s")
86140
try:
87-
with open(out_file, encoding='utf-8') as run_json:
88-
run_dict = json.load(run_json)
89-
for summary in _get_status_and_proof_summaries(run_dict):
90-
print(_get_rendered_table(summary))
141+
print_proof_results(args.run_file)
91142
except Exception as ex: # pylint: disable=broad-except
92143
logging.critical("Could not print results. Exception: %s", str(ex))

0 commit comments

Comments
 (0)