From fff3cd6ada544d176a89823895b7ef691ad75d70 Mon Sep 17 00:00:00 2001 From: Andrew Holway Date: Tue, 11 Mar 2025 13:12:13 +0000 Subject: [PATCH 1/2] fixed api --- chipflow_lib/steps/silicon.py | 53 ++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/chipflow_lib/steps/silicon.py b/chipflow_lib/steps/silicon.py index 2f2b56e7..9ce389b2 100644 --- a/chipflow_lib/steps/silicon.py +++ b/chipflow_lib/steps/silicon.py @@ -16,6 +16,7 @@ from .. import ChipFlowError from ..platforms import SiliconPlatform, top_interfaces +from urllib.parse import urlparse logger = logging.getLogger(__name__) @@ -155,34 +156,42 @@ def submit(self, rtlil_path, *, dry_run=False): return logger.info(f"Submitting {submission_name} for project {self.project_name}") + endpoint = os.environ.get("CHIPFLOW_API_ENDPOINT", "https://build.chipflow.org/api/builds") + host = urlparse(endpoint).netloc resp = requests.post( - os.environ.get("CHIPFLOW_API_ENDPOINT", "https://app.chipflow-infra.com/api/builds"), + os.environ.get("CHIPFLOW_API_ENDPOINT", "https://build.chipflow.org/api/builds"), auth=(os.environ["CHIPFLOW_API_KEY_ID"], os.environ["CHIPFLOW_API_KEY_SECRET"]), data=data, files={ "rtlil": open(rtlil_path, "rb"), "config": json.dumps(config), }) - resp_data = resp.json() - if resp.status_code == 403: - raise ChipFlowError( - "Authentication failed; please verify the values of the the CHIPFLOW_API_KEY_ID " - "and CHIPFLOW_API_KEY_SECRET environment variables, if the issue persists, " - "contact support to resolve it") - elif resp.status_code >= 400: - raise ChipFlowError( - f"Submission failed ({resp_data['statusCode']} {resp_data['error']}: " - f"{resp_data['message']}); please contact support and provide this error message") - elif resp.status_code >= 300: - assert False, "3xx responses should not be returned" - elif resp.status_code >= 200: - if not resp_data["ok"]: - raise ChipFlowError( - f"Submission failed ({resp_data['msg']}); please contact support and provide " - f"this error message") - else: - print(f"{resp_data['msg']} (#{resp_data['id']}: {resp_data['name']}); " - f"{resp_data['url']}") + + # Parse response body + try: + resp_data = resp.json() + except ValueError: + resp_data = resp.text + + # Handle response based on status code + if resp.status_code == 200: + logger.info(f"Submitted design: {resp_data}") + print(f"https://{host}/build/{resp_data["build_id"]}") + else: - ChipFlowError(f"Unexpected response from API: {resp}") + # Log detailed information about the failed request + logger.error(f"Request failed with status code {resp.status_code}") + logger.error(f"Request URL: {resp.request.url}") + + # Log headers with auth information redacted + headers = dict(resp.request.headers) + if "Authorization" in headers: + headers["Authorization"] = "REDACTED" + logger.error(f"Request headers: {headers}") + + logger.error(f"Request data: {data}") + logger.error(f"Response headers: {dict(resp.headers)}") + logger.error(f"Response body: {resp_data}") + + raise ChipFlowError(f"Failed to submit design: {resp_data}") From 13cefbe555056eedbd2c6d5c8842f33063533048 Mon Sep 17 00:00:00 2001 From: Andrew Holway Date: Tue, 11 Mar 2025 14:37:43 +0000 Subject: [PATCH 2/2] =?UTF-8?q?retired=20the=20api=20unit=20test:=20Unit?= =?UTF-8?q?=20tests=20are=20meant=20to=20be=20isolated,=20so=20testing=20c?= =?UTF-8?q?ode=20that=20interacts=20directly=20with=20databases,=20APIs,?= =?UTF-8?q?=20file=20systems,=20or=20hardware=20isn't=20ideal=20for=20unit?= =?UTF-8?q?=20tests=E2=80=94use=20integration=20tests=20instead.=20e.g.:?= =?UTF-8?q?=20Testing=20an=20API=20client=20that=20only=20makes=20a=20netw?= =?UTF-8?q?ork=20request=E2=80=94better=20tested=20with=20integration=20or?= =?UTF-8?q?=20end-to-end=20tests.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chipflow_lib/cli.py | 2 +- chipflow_lib/steps/silicon.py | 4 +- tests/test_silicon_step.py | 88 ----------------------------------- 3 files changed, 3 insertions(+), 91 deletions(-) delete mode 100644 tests/test_silicon_step.py diff --git a/chipflow_lib/cli.py b/chipflow_lib/cli.py index 193c4a92..fb5274dd 100644 --- a/chipflow_lib/cli.py +++ b/chipflow_lib/cli.py @@ -15,7 +15,7 @@ from .pin_lock import PinCommand -logging.basicConfig(stream=sys.stdout, level=logging.WARNING) +logging.basicConfig(stream=sys.stdout, level=logging.INFO) class UnexpectedError(ChipFlowError): diff --git a/chipflow_lib/steps/silicon.py b/chipflow_lib/steps/silicon.py index 9ce389b2..2bc7faa8 100644 --- a/chipflow_lib/steps/silicon.py +++ b/chipflow_lib/steps/silicon.py @@ -94,7 +94,7 @@ def submit(self, rtlil_path, *, dry_run=False): """ git_head = subprocess.check_output( ["git", "-C", os.environ["CHIPFLOW_ROOT"], - "rev-parse", "HEAD"], + "rev-parse", "--short", "HEAD"], encoding="ascii").rstrip() git_dirty = bool(subprocess.check_output( ["git", "-C", os.environ["CHIPFLOW_ROOT"], @@ -102,7 +102,7 @@ def submit(self, rtlil_path, *, dry_run=False): submission_name = git_head if git_dirty: logging.warning("Git tree is dirty, submitting anyway!") - submission_name += f"-dirty.{time.strftime('%Y%m%d%M%H%S', time.gmtime())}" + submission_name += f"-dirty" dep_versions = { "python": sys.version.split()[0] } diff --git a/tests/test_silicon_step.py b/tests/test_silicon_step.py deleted file mode 100644 index 8c6a1295..00000000 --- a/tests/test_silicon_step.py +++ /dev/null @@ -1,88 +0,0 @@ -# SPDX-License-Identifier: BSD-2-Clause -import io -import json -import os -import tomli -import unittest -from contextlib import redirect_stdout -from pprint import pformat -from unittest.mock import patch - -from chipflow_lib.steps.silicon import SiliconStep - - -current_dir = os.path.dirname(__file__) - - -def mocked_requests_post(*args, **kwargs): - class MockResponse: - def __init__(self, json_data, status_code): - self.json_data = json_data - self.status_code = status_code - - def json(self): - return self.json_data - - if args[0] == 'https://app.chipflow-infra.com/api/builds': - return MockResponse({ - "ok": True, - "msg": "msg", - "url": "https://example.com/build-url/", - "name": "name", - "id": "proj-name", - }, 200) - - return MockResponse(None, 404) - - -class SiliconStepTestCase(unittest.TestCase): - def setUp(self): - os.environ["CHIPFLOW_ROOT"] = os.path.dirname(current_dir) - os.environ["CHIPFLOW_API_KEY_ID"] = "keyid" - os.environ["CHIPFLOW_API_KEY_SECRET"] = "keysecret" - - @patch('requests.post', side_effect=mocked_requests_post) - def test_submit_happy_path(self, mock_requests_post): - customer_config = f"{current_dir}/fixtures/mock.toml" - with open(customer_config, "rb") as f: - config_dict = tomli.load(f) - - silicon_step = SiliconStep(config_dict) - - f = io.StringIO() - with redirect_stdout(f): - silicon_step.submit(current_dir + "/fixtures/mock.rtlil") - output = f.getvalue() - assert 'msg (#proj-name: name); https://example.com/build-url/' in output, "The printed output is correct." - - args = mock_requests_post.call_args_list[0][0] - kwargs = mock_requests_post.call_args_list[0][1] - data = kwargs["data"] - files = kwargs["files"] - config = json.loads(files["config"]) - rtlil = files["rtlil"].read() - assert args[0] == 'https://app.chipflow-infra.com/api/builds' - assert kwargs["auth"] == ("keyid", "keysecret") - assert data["projectId"] == 'proj-name' - assert isinstance(data["name"], str), "Name is a string" - assert list(config["dependency_versions"]) == [ - "python", - "yowasp-runtime", "yowasp-yosys", - "amaranth", "amaranth-stdio", "amaranth-soc", - "chipflow-lib", - "amaranth-orchard", "amaranth-vexriscv", - ], "We have entries for the the dependency versions" - - print(pformat(config)) - assert config["silicon"] == { - 'process': 'ihp_sg13g2', - 'pad_ring': 'pga144', - 'pads': {}, - 'power': { - 'vss': {'loc': 'N1'}, - 'vssio': {'loc': 'N5'}, - 'vddio': {'loc': 'N6'}, - 'vdd': {'loc': 'N7'} - } - } - assert rtlil == b"fake-rtlil", "The RTL file was passed through."