mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
- Added timestamp normalization to function in , allowing for optional timestamp input. - Updated command-line interface to accept a timestamp argument for generating deterministic build variant IDs. - Modified in to include a build timestamp in the environment variables. - Introduced a new test suite in to validate the pin-map generator, including tests for duplicate pads and invalid entries. - Added a test for ensuring the regression runner uses a consistent build timestamp. - Created a new SystemVerilog testbench for the APB4 interface bridge to verify correct signal crossing.
78 lines
2.4 KiB
Python
78 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import re
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
from scripts.dependency_lock import DEFAULT_LOCK, lock_digest, load_lock # noqa: E402
|
|
|
|
TIMESTAMP_FORMAT = "%Y-%m-%d-%H-%M"
|
|
TIMESTAMP_RE = re.compile(r"[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}")
|
|
|
|
|
|
def slug(value: str) -> str:
|
|
normalized = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
|
|
return normalized or "manual"
|
|
|
|
|
|
def normalize_timestamp(value: str | None) -> str:
|
|
if value is None:
|
|
return datetime.now().astimezone().strftime(TIMESTAMP_FORMAT)
|
|
if TIMESTAMP_RE.fullmatch(value) is None:
|
|
raise ValueError(f"timestamp must use {TIMESTAMP_FORMAT}")
|
|
try:
|
|
datetime.strptime(value, TIMESTAMP_FORMAT)
|
|
except ValueError as error:
|
|
raise ValueError(f"timestamp must use {TIMESTAMP_FORMAT}") from error
|
|
return value
|
|
|
|
|
|
def variant_id(
|
|
profile: str,
|
|
values: list[str],
|
|
lock: Path,
|
|
timestamp: str | None = None,
|
|
) -> str:
|
|
parsed: dict[str, str] = {}
|
|
for item in values:
|
|
name, separator, value = item.partition("=")
|
|
if not separator or not name or name in parsed:
|
|
raise ValueError(f"invalid or duplicate configuration value: {item}")
|
|
parsed[name] = value
|
|
payload = {
|
|
"schema_version": 1,
|
|
"configuration": dict(sorted(parsed.items())),
|
|
"dependency_lock_sha256": lock_digest(lock),
|
|
}
|
|
digest = hashlib.sha256(
|
|
json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
|
).hexdigest()[:12]
|
|
return f"{slug(profile)}-{normalize_timestamp(timestamp)}-{digest}"
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Generate a timestamped build variant ID")
|
|
parser.add_argument("--lock", type=Path, default=DEFAULT_LOCK)
|
|
parser.add_argument("--profile", default="manual")
|
|
parser.add_argument("--value", action="append", default=[])
|
|
parser.add_argument("--timestamp", help=f"creation time in {TIMESTAMP_FORMAT} format")
|
|
args = parser.parse_args()
|
|
load_lock(args.lock)
|
|
try:
|
|
print(variant_id(args.profile, args.value, args.lock, args.timestamp))
|
|
except ValueError as error:
|
|
parser.error(str(error))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|