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 2f2b56e7..2bc7faa8 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__) @@ -93,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"], @@ -101,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] } @@ -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}") 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."