Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
python -m pip install --upgrade pip
python -m pip install -r requirements.txt -r common/deploy/requirements.txt

- name: Build and package BobSim
- name: Build, test bundled simulation, and package BobSim
shell: bash
run: |
version="${{ inputs.version }}"
Expand Down
69 changes: 69 additions & 0 deletions app/bundle_smoke.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""Exercise edited runtime inputs and archived results inside the built app."""

from __future__ import annotations

import json
import os
import subprocess
import sys

import numpy as np
import pandas as pd
import yaml

from app import app, runtime
from app.visual import reduced_scene_payload
from common.physics_provenance import ggv_physics_sha256
from engines.dynpy.parameters import G, load_reduced_vehicle_parameters


def main() -> None:
"""## Bundled Simulation Check

Run a short edited-vehicle simulation in the release check's temporary home.
"""
if not getattr(sys, "frozen", False) or os.environ.get("BOBSIM_BUNDLE_SMOKE") != "1":
raise RuntimeError("This check requires a bundled app and an isolated smoke-test home.")
root = app.ROOT
assert root != runtime.PACKAGE_ROOT
assert len(ggv_physics_sha256(runtime.PACKAGE_ROOT)) == 64
vehicle = yaml.safe_load((root / "vehicle.yml").read_text())
vehicle["vehicle"]["name"] = "Bundle smoke vehicle"
vehicle["sprung_mass"]["mass_kg"] += 20
for key, table in vehicle["aero"].items():
if "_table_" in key:
vehicle["aero"][key] = np.zeros_like(table).tolist()
app.save_raw_config("vehicle", yaml.safe_dump(vehicle))
workflow = "reduced-step-steer"
config = dict(model_dof=3, speed_mps=8, roadwheel_step_deg=0.5,
step_time_s=0.05, rise_time_s=0.02, stop_time_s=0.2,
sample_period_s=0.01, kinematics_mode="lookup")
app.save_raw_config(workflow, yaml.safe_dump(config))
inputs = app.capture_workflow_inputs(workflow)
subprocess.run([sys.executable, "--run-module", "app.python_simulations", workflow],
cwd=root, check=True, timeout=180)
saved = app.save_active_results(workflow, input_snapshot=inputs)["saved"]
files = {item["label"]: root / item["path"] for item in saved["files"]}
summary = json.loads(files["Summary"].read_text())
assert summary["success"] and summary["model_dof"] == 3
assert files["Report"].read_bytes().startswith(b"%PDF")
frame = pd.read_csv(files["Signals"])
assert len(frame) == 21 and np.isclose(frame["time"].iloc[-1], 0.2)
assert np.isclose(frame["velX"].iloc[0], 8)
params = load_reduced_vehicle_parameters(root / "vehicle.yml")
loads = frame[[f"tireFz{corner}" for corner in ("FL", "FR", "RL", "RR")]]
assert np.allclose(loads.sum(axis=1), params.mass_kg * G, atol=0.01)
archived_vehicle = root / saved["vehicle_snapshot"]
assert yaml.safe_load(archived_vehicle.read_text()) == vehicle
plot = app.result_series_payload(files["Signals"].relative_to(root).as_posix(),
x_axis="time", signals=["accY"])
assert len(plot["x"]) == 21
scene = reduced_scene_payload(archived_vehicle, files["Signals"], 3)
assert scene.buffer and scene.header["grip"]
assert "unavailable" not in scene.header["representation"].lower()
(root / "bundle-smoke-passed.json").write_text(json.dumps(summary), encoding="utf-8")
print("Bundled simulation, report, signals, and replay checks passed.", flush=True)


if __name__ == "__main__":
main()
21 changes: 21 additions & 0 deletions common/deploy/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import subprocess
import sys
import tarfile
import tempfile
from typing import Iterable
import zipfile

Expand Down Expand Up @@ -696,6 +697,25 @@ def upload_github_release(version: str | None, tag: str | None, files: Iterable[
)


def check_bundle(mode: str) -> None:
"""## Bundled Workflow Check

Parameters
----------
mode : str
Built artifact layout, onefile or onedir.
"""
executable = _artifact_path(DIST_ROOT / mode, mode)
if executable.suffix == ".app":
executable /= "Contents/MacOS/BobSim"
with tempfile.TemporaryDirectory(prefix="bobsim-bundle-smoke-") as directory:
env = {**os.environ, "BOBSIM_HOME": directory, "BOBSIM_BUNDLE_SMOKE": "1"}
subprocess.run([str(executable), "--run-module", "app.bundle_smoke"],
cwd=directory, env=env, check=True, timeout=300)
if not (Path(directory) / "bundle-smoke-passed.json").is_file():
raise RuntimeError("The packaged simulation check did not finish.")


def build_release(
mode: str,
version: str | None,
Expand All @@ -706,6 +726,7 @@ def build_release(
) -> None:
clean_generated_artifacts(include_deploy=True)
build_pyinstaller(mode, install_deps, skip_conflict_check)
check_bundle(mode)
archive, checksum_path = package_release(mode, version)
if upload_release:
upload_github_release(version, tag, [archive, checksum_path])
Expand Down
Loading