boulder 0.6.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- boulder-0.6.1/.cruft.json +17 -0
- boulder-0.6.1/.cursor/skills/boulder-gui/SKILL.md +162 -0
- boulder-0.6.1/.cursor/skills/boulder-gui/reference.md +89 -0
- boulder-0.6.1/.cursorignore +8 -0
- boulder-0.6.1/.cursorrules +21 -0
- boulder-0.6.1/.github/workflows/on-push.yml +193 -0
- boulder-0.6.1/.github/workflows/release.yml +88 -0
- boulder-0.6.1/.gitignore +563 -0
- boulder-0.6.1/.pre-commit-config-cruft.yaml +7 -0
- boulder-0.6.1/.pre-commit-config.yaml +42 -0
- boulder-0.6.1/AGENTS.md +166 -0
- boulder-0.6.1/ARCHITECTURE.md +528 -0
- boulder-0.6.1/FMI_FMU_EXPORT.md +226 -0
- boulder-0.6.1/LICENSE +21 -0
- boulder-0.6.1/Makefile +28 -0
- boulder-0.6.1/PKG-INFO +249 -0
- boulder-0.6.1/README.md +197 -0
- boulder-0.6.1/SPECIFICATIONS.md +32 -0
- boulder-0.6.1/STONE_SPECIFICATIONS.md +1303 -0
- boulder-0.6.1/boulder/__init__.py +46 -0
- boulder-0.6.1/boulder/_frontend/assets/SankeyTab-D_7pX8ns.js +1 -0
- boulder-0.6.1/boulder/_frontend/assets/ThermoReportTab-B1AsQ0LC.js +1 -0
- boulder-0.6.1/boulder/_frontend/assets/index-4YpGN-6Y.js +4117 -0
- boulder-0.6.1/boulder/_frontend/assets/index-CxKeOVn3.css +1 -0
- boulder-0.6.1/boulder/_frontend/assets/index-HWgOWN26.js +11 -0
- boulder-0.6.1/boulder/_frontend/index.html +14 -0
- boulder-0.6.1/boulder/_frontend/vite.svg +1 -0
- boulder-0.6.1/boulder/api/__init__.py +1 -0
- boulder-0.6.1/boulder/api/main.py +348 -0
- boulder-0.6.1/boulder/api/routes/__init__.py +1 -0
- boulder-0.6.1/boulder/api/routes/configs.py +231 -0
- boulder-0.6.1/boulder/api/routes/graph.py +38 -0
- boulder-0.6.1/boulder/api/routes/gui_actions.py +143 -0
- boulder-0.6.1/boulder/api/routes/mechanisms.py +21 -0
- boulder-0.6.1/boulder/api/routes/plugins.py +94 -0
- boulder-0.6.1/boulder/api/routes/scenarios.py +204 -0
- boulder-0.6.1/boulder/api/routes/simulations.py +424 -0
- boulder-0.6.1/boulder/api/routes/sweep.py +251 -0
- boulder-0.6.1/boulder/api/routes/ui.py +49 -0
- boulder-0.6.1/boulder/api/sse.py +94 -0
- boulder-0.6.1/boulder/bindings.py +315 -0
- boulder-0.6.1/boulder/cantera_converter.py +2342 -0
- boulder-0.6.1/boulder/cli.py +805 -0
- boulder-0.6.1/boulder/config.py +2814 -0
- boulder-0.6.1/boulder/ctutils.py +285 -0
- boulder-0.6.1/boulder/data/sample_config.yaml +45 -0
- boulder-0.6.1/boulder/download_script_emitter.py +840 -0
- boulder-0.6.1/boulder/export.py +145 -0
- boulder-0.6.1/boulder/gui_actions.py +120 -0
- boulder-0.6.1/boulder/lagrangian.py +334 -0
- boulder-0.6.1/boulder/live_simulation.py +85 -0
- boulder-0.6.1/boulder/network_plugin.py +164 -0
- boulder-0.6.1/boulder/output_pane_plugins.py +194 -0
- boulder-0.6.1/boulder/output_summary.py +224 -0
- boulder-0.6.1/boulder/parser/__init__.py +13 -0
- boulder-0.6.1/boulder/parser/py_to_yaml.py +266 -0
- boulder-0.6.1/boulder/payload_store.py +485 -0
- boulder-0.6.1/boulder/reactor_energy.py +89 -0
- boulder-0.6.1/boulder/reactor_sizing.py +83 -0
- boulder-0.6.1/boulder/result_cache.py +836 -0
- boulder-0.6.1/boulder/runner.py +847 -0
- boulder-0.6.1/boulder/sankey.py +689 -0
- boulder-0.6.1/boulder/schema_registry.py +326 -0
- boulder-0.6.1/boulder/scopes.py +269 -0
- boulder-0.6.1/boulder/signals.py +365 -0
- boulder-0.6.1/boulder/sim2stone.py +1140 -0
- boulder-0.6.1/boulder/sim2stone_ast.py +794 -0
- boulder-0.6.1/boulder/sim2stone_cli.py +233 -0
- boulder-0.6.1/boulder/sim2stone_trace.py +157 -0
- boulder-0.6.1/boulder/simulation_result.py +190 -0
- boulder-0.6.1/boulder/simulation_worker.py +632 -0
- boulder-0.6.1/boulder/spatial_inference.py +176 -0
- boulder-0.6.1/boulder/stage_network.py +63 -0
- boulder-0.6.1/boulder/staged_network.py +127 -0
- boulder-0.6.1/boulder/staged_solver.py +1483 -0
- boulder-0.6.1/boulder/stone2sim_cli.py +89 -0
- boulder-0.6.1/boulder/styles.py +246 -0
- boulder-0.6.1/boulder/summary_builder.py +237 -0
- boulder-0.6.1/boulder/tests/test_build_isolated_reactor.py +63 -0
- boulder-0.6.1/boulder/tests/test_reactor_energy.py +53 -0
- boulder-0.6.1/boulder/utils.py +650 -0
- boulder-0.6.1/boulder/validation.py +536 -0
- boulder-0.6.1/boulder/verbose_utils.py +81 -0
- boulder-0.6.1/boulder/version.py +2 -0
- boulder-0.6.1/boulder/yaml_unit_map.py +321 -0
- boulder-0.6.1/boulder.egg-info/PKG-INFO +249 -0
- boulder-0.6.1/boulder.egg-info/SOURCES.txt +301 -0
- boulder-0.6.1/boulder.egg-info/dependency_links.txt +1 -0
- boulder-0.6.1/boulder.egg-info/entry_points.txt +4 -0
- boulder-0.6.1/boulder.egg-info/requires.txt +15 -0
- boulder-0.6.1/boulder.egg-info/scm_file_list.json +289 -0
- boulder-0.6.1/boulder.egg-info/scm_version.json +8 -0
- boulder-0.6.1/boulder.egg-info/top_level.txt +1 -0
- boulder-0.6.1/ci/environment-ci.yml +24 -0
- boulder-0.6.1/ci/environment-integration.yml +13 -0
- boulder-0.6.1/configs/README.md +147 -0
- boulder-0.6.1/configs/cstr_methane_air.yaml +80 -0
- boulder-0.6.1/configs/default.yaml +58 -0
- boulder-0.6.1/configs/grouped_nodes.yaml +63 -0
- boulder-0.6.1/configs/mix_react_streams.yaml +58 -0
- boulder-0.6.1/configs/sample_configs2.yaml +61 -0
- boulder-0.6.1/configs/staged_psr_pfr.yaml +116 -0
- boulder-0.6.1/docs/Makefile +39 -0
- boulder-0.6.1/docs/api.rst +8 -0
- boulder-0.6.1/docs/boulder.png +0 -0
- boulder-0.6.1/docs/boulder_logo_small.png +0 -0
- boulder-0.6.1/docs/cantera_examples/.gitignore +3 -0
- boulder-0.6.1/docs/cantera_examples/combustor.py +120 -0
- boulder-0.6.1/docs/cantera_examples/nanosecond_pulse_discharge.py +145 -0
- boulder-0.6.1/docs/cantera_examples/reactor2.py +147 -0
- boulder-0.6.1/docs/cantera_upstream_examples.rst +184 -0
- boulder-0.6.1/docs/conf.py +127 -0
- boulder-0.6.1/docs/index.rst +43 -0
- boulder-0.6.1/docs/installation.rst +10 -0
- boulder-0.6.1/docs/mix_streams_example.png +0 -0
- boulder-0.6.1/docs/plans/2026-06-07-wait-for-server-before-browser-open.md +118 -0
- boulder-0.6.1/docs/stone.rst +125 -0
- boulder-0.6.1/docs/usage.rst +94 -0
- boulder-0.6.1/environment.yml +23 -0
- boulder-0.6.1/examples/GALLERY_HEADER.rst +22 -0
- boulder-0.6.1/examples/launch_from_cli.py +10 -0
- boulder-0.6.1/examples/launch_from_python.py +6 -0
- boulder-0.6.1/examples/mix1.py +96 -0
- boulder-0.6.1/examples/mix1.yaml +108 -0
- boulder-0.6.1/examples/plot_mixed_reactor_stream_example.py +100 -0
- boulder-0.6.1/examples/plot_staged_solve_from_yaml.py +66 -0
- boulder-0.6.1/examples/plot_staged_solve_progressive_draw.py +145 -0
- boulder-0.6.1/examples/plugin_example.py +277 -0
- boulder-0.6.1/frontend/.gitignore +24 -0
- boulder-0.6.1/frontend/README.md +73 -0
- boulder-0.6.1/frontend/SPECIFICATIONS.md +198 -0
- boulder-0.6.1/frontend/eslint.config.js +23 -0
- boulder-0.6.1/frontend/index.html +13 -0
- boulder-0.6.1/frontend/package-lock.json +7906 -0
- boulder-0.6.1/frontend/package.json +54 -0
- boulder-0.6.1/frontend/playwright.config.ts +25 -0
- boulder-0.6.1/frontend/public/vite.svg +1 -0
- boulder-0.6.1/frontend/src/App.css +42 -0
- boulder-0.6.1/frontend/src/App.tsx +21 -0
- boulder-0.6.1/frontend/src/api/client.ts +27 -0
- boulder-0.6.1/frontend/src/api/configs.ts +88 -0
- boulder-0.6.1/frontend/src/api/guiActions.ts +46 -0
- boulder-0.6.1/frontend/src/api/mechanisms.ts +10 -0
- boulder-0.6.1/frontend/src/api/plugins.ts +21 -0
- boulder-0.6.1/frontend/src/api/resultCache.ts +48 -0
- boulder-0.6.1/frontend/src/api/scenarios.ts +50 -0
- boulder-0.6.1/frontend/src/api/simulations.ts +31 -0
- boulder-0.6.1/frontend/src/api/sweep.ts +38 -0
- boulder-0.6.1/frontend/src/assets/react.svg +1 -0
- boulder-0.6.1/frontend/src/components/graph/ReactorGraph.tsx +1463 -0
- boulder-0.6.1/frontend/src/components/layout/AppShell.test.tsx +147 -0
- boulder-0.6.1/frontend/src/components/layout/AppShell.tsx +360 -0
- boulder-0.6.1/frontend/src/components/layout/paneControls.tsx +73 -0
- boulder-0.6.1/frontend/src/components/modals/AddMFCModal.tsx +148 -0
- boulder-0.6.1/frontend/src/components/modals/AddReactorModal.tsx +135 -0
- boulder-0.6.1/frontend/src/components/modals/ConfirmDeleteNodeModal.tsx +51 -0
- boulder-0.6.1/frontend/src/components/modals/YAMLEditorModal.tsx +173 -0
- boulder-0.6.1/frontend/src/components/panels/EditNetworkCard.tsx +85 -0
- boulder-0.6.1/frontend/src/components/panels/PropertiesPanel.test.tsx +145 -0
- boulder-0.6.1/frontend/src/components/panels/PropertiesPanel.tsx +319 -0
- boulder-0.6.1/frontend/src/components/panels/RunControl.tsx +232 -0
- boulder-0.6.1/frontend/src/components/panels/ScenarioPane.tsx +145 -0
- boulder-0.6.1/frontend/src/components/panels/SimulateCard.test.tsx +256 -0
- boulder-0.6.1/frontend/src/components/panels/SimulateCard.tsx +388 -0
- boulder-0.6.1/frontend/src/components/panels/SolverDetailsModal.tsx +185 -0
- boulder-0.6.1/frontend/src/components/panels/solverShared.ts +37 -0
- boulder-0.6.1/frontend/src/components/results/ConvergenceTab.tsx +265 -0
- boulder-0.6.1/frontend/src/components/results/ErrorTab.tsx +19 -0
- boulder-0.6.1/frontend/src/components/results/PlotsTab.test.tsx +123 -0
- boulder-0.6.1/frontend/src/components/results/PlotsTab.tsx +461 -0
- boulder-0.6.1/frontend/src/components/results/PluginTab.tsx +166 -0
- boulder-0.6.1/frontend/src/components/results/ResultsTabs.tsx +204 -0
- boulder-0.6.1/frontend/src/components/results/SankeyTab.test.tsx +78 -0
- boulder-0.6.1/frontend/src/components/results/SankeyTab.tsx +107 -0
- boulder-0.6.1/frontend/src/components/results/SummaryTab.tsx +50 -0
- boulder-0.6.1/frontend/src/components/results/ThermoReportTab.tsx +162 -0
- boulder-0.6.1/frontend/src/components/simulation/SimulationOverlay.tsx +80 -0
- boulder-0.6.1/frontend/src/components/ui/Button.tsx +57 -0
- boulder-0.6.1/frontend/src/hooks/useKeyboardShortcuts.ts +19 -0
- boulder-0.6.1/frontend/src/hooks/useScenarioFocus.ts +37 -0
- boulder-0.6.1/frontend/src/hooks/useSimulationSSE.ts +114 -0
- boulder-0.6.1/frontend/src/index.css +97 -0
- boulder-0.6.1/frontend/src/lib/cn.ts +10 -0
- boulder-0.6.1/frontend/src/lib/cytoscapeNodeColor.test.ts +32 -0
- boulder-0.6.1/frontend/src/lib/cytoscapeNodeColor.ts +81 -0
- boulder-0.6.1/frontend/src/lib/plotAxis.test.ts +28 -0
- boulder-0.6.1/frontend/src/lib/plotAxis.ts +18 -0
- boulder-0.6.1/frontend/src/lib/units.ts +104 -0
- boulder-0.6.1/frontend/src/main.tsx +10 -0
- boulder-0.6.1/frontend/src/stores/configStore.ts +106 -0
- boulder-0.6.1/frontend/src/stores/layoutStore.ts +74 -0
- boulder-0.6.1/frontend/src/stores/resultsTabStore.ts +12 -0
- boulder-0.6.1/frontend/src/stores/scenarioStore.ts +73 -0
- boulder-0.6.1/frontend/src/stores/selectionStore.ts +18 -0
- boulder-0.6.1/frontend/src/stores/simulationStore.ts +73 -0
- boulder-0.6.1/frontend/src/stores/themeStore.ts +73 -0
- boulder-0.6.1/frontend/src/types/config.ts +32 -0
- boulder-0.6.1/frontend/src/types/guiAction.ts +7 -0
- boulder-0.6.1/frontend/src/types/plugin.ts +23 -0
- boulder-0.6.1/frontend/src/types/simulation.ts +96 -0
- boulder-0.6.1/frontend/tests/e2e/boulder.spec.ts +215 -0
- boulder-0.6.1/frontend/tests/unit/stores/configStore.test.ts +92 -0
- boulder-0.6.1/frontend/tsconfig.app.json +28 -0
- boulder-0.6.1/frontend/tsconfig.json +7 -0
- boulder-0.6.1/frontend/tsconfig.node.json +26 -0
- boulder-0.6.1/frontend/vite.config.ts +28 -0
- boulder-0.6.1/frontend/vitest.config.ts +16 -0
- boulder-0.6.1/mypy.ini +3 -0
- boulder-0.6.1/pyproject.toml +109 -0
- boulder-0.6.1/run.py +6 -0
- boulder-0.6.1/setup.cfg +4 -0
- boulder-0.6.1/tests/README.md +169 -0
- boulder-0.6.1/tests/configs/mix_react_streams.yaml +97 -0
- boulder-0.6.1/tests/configs/mix_react_streams_highT.yaml +15 -0
- boulder-0.6.1/tests/conftest.py +10 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/01_v1_nodes_connections.yaml +11 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/02_mixed_network_and_stages.yaml +11 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/03_missing_dynamic_block.yaml +7 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/04_undeclared_dynamic_block.yaml +10 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/05_source_only_connection.yaml +11 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/06_target_only_connection.yaml +11 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/07_duplicate_node_id.yaml +21 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/08_reserved_stage_name.yaml +12 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/09_unknown_top_level_key.yaml +12 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/10_unknown_item_key.yaml +12 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/11_intra_stage_logical_connection.yaml +22 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/12_downstream_operating_state.yaml +29 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/13_inline_inlet_port.yaml +13 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/14_over_specified_sizing.yaml +11 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/15_ambiguous_mfc_conservation.yaml +36 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/16_stages_and_network_coexist.yaml +16 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/17_stage_dag_cycle.yaml +31 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/18_outlet_sink_as_source.yaml +19 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/19_advance_time_without_advance.yaml +16 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/20_advance_missing_advance_time.yaml +15 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/21_mixed_composition_bases.yaml +12 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/22_reservoir_missing_state.yaml +10 -0
- boulder-0.6.1/tests/fixtures/stone_v2/invalid/23_pressure_controller_no_master.yaml +42 -0
- boulder-0.6.1/tests/fixtures/stone_v2/valid/01_network_single_stage.yaml +22 -0
- boulder-0.6.1/tests/fixtures/stone_v2/valid/02_staged_logical_handoff.yaml +36 -0
- boulder-0.6.1/tests/fixtures/stone_v2/valid/03_staged_logical_handoff_flow.yaml +40 -0
- boulder-0.6.1/tests/fixtures/stone_v2/valid/04_batch_reactor_initial.yaml +14 -0
- boulder-0.6.1/tests/fixtures/stone_v2/valid/05_const_pressure_psr.yaml +38 -0
- boulder-0.6.1/tests/fixtures/stone_v2/valid/06_outlet_sink.yaml +29 -0
- boulder-0.6.1/tests/fixtures/stone_v2/valid/07_empty_mfc_conservation.yaml +31 -0
- boulder-0.6.1/tests/fixtures/stone_v2/valid/08_mechanism_switch.yaml +41 -0
- boulder-0.6.1/tests/fixtures/stone_v2/valid/09_phase_alias.yaml +25 -0
- boulder-0.6.1/tests/test_api.py +812 -0
- boulder-0.6.1/tests/test_bindings_resolver.py +234 -0
- boulder-0.6.1/tests/test_bindings_runtime.py +82 -0
- boulder-0.6.1/tests/test_cantera_script_emitter.py +149 -0
- boulder-0.6.1/tests/test_cli_browser.py +129 -0
- boulder-0.6.1/tests/test_cli_headless.py +261 -0
- boulder-0.6.1/tests/test_cli_import.py +56 -0
- boulder-0.6.1/tests/test_composite_yaml_roundtrip.py +393 -0
- boulder-0.6.1/tests/test_config_output_block.py +131 -0
- boulder-0.6.1/tests/test_config_validation.py +301 -0
- boulder-0.6.1/tests/test_data/invalid/duplicate_node_id.yaml +8 -0
- boulder-0.6.1/tests/test_data/invalid/missing_properties_node.yaml +7 -0
- boulder-0.6.1/tests/test_data/invalid/unknown_connection_ref.yaml +11 -0
- boulder-0.6.1/tests/test_default_boulder_logging.py +61 -0
- boulder-0.6.1/tests/test_export.py +139 -0
- boulder-0.6.1/tests/test_flow_conservation.py +243 -0
- boulder-0.6.1/tests/test_gui_actions_api.py +202 -0
- boulder-0.6.1/tests/test_network_plugin.py +179 -0
- boulder-0.6.1/tests/test_normalize_signals.py +137 -0
- boulder-0.6.1/tests/test_output_summary.py +132 -0
- boulder-0.6.1/tests/test_output_summary_integration.py +55 -0
- boulder-0.6.1/tests/test_package_import.py +8 -0
- boulder-0.6.1/tests/test_payload_store.py +259 -0
- boulder-0.6.1/tests/test_phase_b_transient.py +426 -0
- boulder-0.6.1/tests/test_phase_c_continuation.py +395 -0
- boulder-0.6.1/tests/test_plugin_example.py +347 -0
- boulder-0.6.1/tests/test_ports_conflict.py +226 -0
- boulder-0.6.1/tests/test_ports_expansion.py +137 -0
- boulder-0.6.1/tests/test_pressure_controller_connection.py +226 -0
- boulder-0.6.1/tests/test_pressure_defaults.py +246 -0
- boulder-0.6.1/tests/test_psr_volume_sizing.py +148 -0
- boulder-0.6.1/tests/test_result_cache.py +554 -0
- boulder-0.6.1/tests/test_runner.py +413 -0
- boulder-0.6.1/tests/test_sankey_if_no_species.py +25 -0
- boulder-0.6.1/tests/test_sankey_links_for_api.py +75 -0
- boulder-0.6.1/tests/test_scenario_focus.py +89 -0
- boulder-0.6.1/tests/test_scopes.py +364 -0
- boulder-0.6.1/tests/test_script_emitter_class_dispatch.py +139 -0
- boulder-0.6.1/tests/test_signals_registry.py +319 -0
- boulder-0.6.1/tests/test_sim2stone/test_fixture_scripts_sim2stone.py +392 -0
- boulder-0.6.1/tests/test_sim2stone_ast.py +403 -0
- boulder-0.6.1/tests/test_sim2stone_mechanisms.py +54 -0
- boulder-0.6.1/tests/test_sim_to_yaml.py +454 -0
- boulder-0.6.1/tests/test_simulation_cleanup.py +283 -0
- boulder-0.6.1/tests/test_smart_detection.py +454 -0
- boulder-0.6.1/tests/test_solver_dispatch.py +644 -0
- boulder-0.6.1/tests/test_solver_mode.py +247 -0
- boulder-0.6.1/tests/test_staged_solver.py +1261 -0
- boulder-0.6.1/tests/test_staged_viz_flow.py +777 -0
- boulder-0.6.1/tests/test_stone_v2_fixtures.py +43 -0
- boulder-0.6.1/tests/test_sweep_runset.py +39 -0
- boulder-0.6.1/tests/test_transient_capture.py +73 -0
- boulder-0.6.1/tests/test_unfold.py +302 -0
- boulder-0.6.1/tests/test_unit.py +267 -0
- boulder-0.6.1/tests/test_yaml_comment_system.py +852 -0
- boulder-0.6.1/tests/test_yaml_unit_map.py +237 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"template": "https://github.com/spark-cleantech/package-template",
|
|
3
|
+
"commit": "02767a57f4444fc8413d0e3265a5a0f92d586794",
|
|
4
|
+
"checkout": null,
|
|
5
|
+
"context": {
|
|
6
|
+
"cookiecutter": {
|
|
7
|
+
"project_name": "boulder",
|
|
8
|
+
"repo_name": "boulder",
|
|
9
|
+
"project_author": "Erwan Pannier",
|
|
10
|
+
"project_short_description": "Cantera Reactor Net Simulations",
|
|
11
|
+
"copyright_year": "2025",
|
|
12
|
+
"mypy_strict": false,
|
|
13
|
+
"_template": "https://github.com/spark-cleantech/package-template"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"directory": null
|
|
17
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
______________________________________________________________________
|
|
2
|
+
|
|
3
|
+
## name: boulder-gui description: >- Start, drive, and verify the Boulder web GUI (FastAPI + React/Cytoscape) for STONE YAML simulations. Use when the user asks to run a simulation in the browser, test the UI, click nodes, read Thermo/Summary tabs, preload a YAML, or automate E2E checks against localhost:8050. Covers server startup, config loading, graph interaction, results tabs, and API fallbacks when browser canvas clicks fail.
|
|
4
|
+
|
|
5
|
+
# Boulder GUI
|
|
6
|
+
|
|
7
|
+
Guide for agents verifying STONE simulations through the web UI or its HTTP API.
|
|
8
|
+
|
|
9
|
+
## When to use what
|
|
10
|
+
|
|
11
|
+
| Goal | Prefer |
|
|
12
|
+
|------|--------|
|
|
13
|
+
| Assert outlet T, stream points, staged solve | **HTTP API** (reproducible, no canvas coords) |
|
|
14
|
+
| Visual layout, UX, tab wiring | **Browser MCP** |
|
|
15
|
+
| Custom reactor kinds via plugins | `BOULDER_PLUGINS` + registered plugin package |
|
|
16
|
+
|
|
17
|
+
Always rebuild frontend after UI changes: `cd frontend && npm run build`.
|
|
18
|
+
|
|
19
|
+
## Start the server
|
|
20
|
+
|
|
21
|
+
Default URL: `http://127.0.0.1:8050`. Health: `GET /api/health`.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
conda activate boulder
|
|
25
|
+
cd frontend && npm run build && cd ..
|
|
26
|
+
boulder path/to/config.yaml --no-open
|
|
27
|
+
# or: boulder --no-open # empty / default config
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Optional plugins (entry points or `BOULDER_PLUGINS` env var) load at startup.
|
|
31
|
+
|
|
32
|
+
After changing the YAML on disk, **restart the server** — preloaded config is read once at startup.
|
|
33
|
+
|
|
34
|
+
## UI layout
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
38
|
+
│ Header: [filename.yaml] Light/Dark │
|
|
39
|
+
├──────────────┬──────────────────────────────────────────────┤
|
|
40
|
+
│ Edit Network │ Reactor graph (Cytoscape canvas) │
|
|
41
|
+
│ Simulate │ Click nodes/edges to select │
|
|
42
|
+
│ Properties │ │
|
|
43
|
+
├──────────────┴──────────────────────────────────────────────┤
|
|
44
|
+
│ Results: Plots | Sankey | Thermo | Summary | Convergence │
|
|
45
|
+
│ (+ plugin tabs e.g. Network) │
|
|
46
|
+
└─────────────────────────────────────────────────────────────┘
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- **Header filename button** — opens YAML editor (Save/Cancel).
|
|
50
|
+
- **Upload Config** — load `.yaml` / `.yml` / `.py` from disk.
|
|
51
|
+
- **Run Simulation (Ctrl+Enter)** — disabled when config has no nodes or a run is in progress.
|
|
52
|
+
- **Properties panel** (left) — selected node/edge fields; stream points and terminal sinks show computed Material Stream (read-only after solve).
|
|
53
|
+
- **Graph** — nodes are circles (reactors) or octagons (reservoirs/sinks); edges are flow devices.
|
|
54
|
+
|
|
55
|
+
## Standard agent workflow (browser)
|
|
56
|
+
|
|
57
|
+
Copy and track:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
GUI check:
|
|
61
|
+
- [ ] Server up (/api/health)
|
|
62
|
+
- [ ] Preloaded config matches expected YAML (/api/configs/preloaded)
|
|
63
|
+
- [ ] Page shows correct filename (not untitled.yaml)
|
|
64
|
+
- [ ] Run Simulation → completes (Download Python button appears)
|
|
65
|
+
- [ ] Select target node → Thermo tab shows expected T/P/composition
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
1. Navigate to `http://127.0.0.1:8050/` (hard refresh if assets 404 or MIME errors after a frontend rebuild).
|
|
69
|
+
1. Confirm header shows the expected `*.yaml` name. If it shows `untitled.yaml`, wait for preload toast or reload; click filename to verify YAML content.
|
|
70
|
+
1. Click **Run Simulation** (or wait until enabled).
|
|
71
|
+
1. Wait for **Download Python** and result tabs — not "Running...".
|
|
72
|
+
1. Open **Thermo** tab.
|
|
73
|
+
1. **Select a node** on the graph (see canvas clicks below).
|
|
74
|
+
1. Read `reactor_report` / thermo text in the tab; cross-check via API if critical.
|
|
75
|
+
|
|
76
|
+
### Graph selection (browser automation)
|
|
77
|
+
|
|
78
|
+
- The graph is a **Cytoscape `<canvas>`** — node ids are **not** exposed to `browser_search`.
|
|
79
|
+
- **Single click** on a node → selects it (Properties + Thermo).
|
|
80
|
+
- **Double click** on a node within ~300 ms → also switches to **Thermo** tab.
|
|
81
|
+
- **Single click** on a **MassFlowController** edge → selects it and opens **Thermo** (shows source reactor T/P/X and mdot).
|
|
82
|
+
|
|
83
|
+
**Reliable click pattern (cursor-ide-browser):**
|
|
84
|
+
|
|
85
|
+
1. `browser_snapshot` with `take_screenshot_afterwards: true`.
|
|
86
|
+
1. Immediately `browser_mouse_click_xy` using **screenshot coordinates** on the node center.
|
|
87
|
+
1. Target must be `<canvas>`, not `#graph-container` or the sidebar grid.
|
|
88
|
+
1. If click fails, take a **fresh** screenshot before retrying (cache invalidates).
|
|
89
|
+
|
|
90
|
+
Clicks are fragile at narrow viewports — resize browser wider if needed. When automation cannot select a node, use the **API verification** below; do not loop on coordinates.
|
|
91
|
+
|
|
92
|
+
## Results tabs
|
|
93
|
+
|
|
94
|
+
| Tab | Needs selection? | Content |
|
|
95
|
+
|-----|------------------|---------|
|
|
96
|
+
| **Plots** | No | Time series after transient runs |
|
|
97
|
+
| **Sankey** | No | Species / energy flows (plugin may customize) |
|
|
98
|
+
| **Thermo** | **Yes** — node or MFC | `reactor_report`, full Cantera `thermo_report` |
|
|
99
|
+
| **Summary** | No | Elapsed time, text summary |
|
|
100
|
+
| **Convergence** | No | Staged-solve progress (redirects residence profiles to Plots when applicable) |
|
|
101
|
+
| **Network** | Plugin | Live reactor network view |
|
|
102
|
+
|
|
103
|
+
Terminal **OutletSink** nodes: post-solve thermo comes from `_refresh_terminal_sinks` (legacy path). **Inter-stage stream-point** diamonds (`{source}_outlet`) are refreshed during staged solve — preferred for multi-stage models.
|
|
104
|
+
|
|
105
|
+
## API verification (preferred for agents)
|
|
106
|
+
|
|
107
|
+
Same server session as the browser. See [reference.md](reference.md) for full scripts.
|
|
108
|
+
|
|
109
|
+
Quick checks:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
import json, urllib.request
|
|
113
|
+
|
|
114
|
+
base = "http://127.0.0.1:8050"
|
|
115
|
+
pre = json.loads(urllib.request.urlopen(f"{base}/api/configs/preloaded").read())
|
|
116
|
+
assert pre["preloaded"]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Start simulation → poll results:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
import time
|
|
123
|
+
req = urllib.request.Request(
|
|
124
|
+
f"{base}/api/simulations",
|
|
125
|
+
data=json.dumps({"config": pre["config"]}).encode(),
|
|
126
|
+
headers={"Content-Type": "application/json"},
|
|
127
|
+
method="POST",
|
|
128
|
+
)
|
|
129
|
+
sim_id = json.loads(urllib.request.urlopen(req).read())["simulation_id"]
|
|
130
|
+
for _ in range(120):
|
|
131
|
+
st = json.loads(urllib.request.urlopen(
|
|
132
|
+
f"{base}/api/simulations/{sim_id}/results").read())
|
|
133
|
+
if st.get("is_complete") or st.get("status") == "error":
|
|
134
|
+
break
|
|
135
|
+
time.sleep(0.5)
|
|
136
|
+
|
|
137
|
+
reports = st["reactor_reports"]
|
|
138
|
+
T_K = reports["outlet"]["T"]
|
|
139
|
+
assert T_K > 300, f"outlet stuck at placeholder T: {T_K} K"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Assert invariants on `updated_nodes` / `updated_connections` when testing staged networks (stream points present, no duplicate interface edges).
|
|
143
|
+
|
|
144
|
+
## Common failures
|
|
145
|
+
|
|
146
|
+
| Symptom | Cause | Fix |
|
|
147
|
+
|---------|-------|-----|
|
|
148
|
+
| `untitled.yaml`, Run disabled | No preload; empty default | Start with YAML arg or Upload Config |
|
|
149
|
+
| Outlet 25 °C / N2 | Terminal `Reservoir` placeholder, or stale preload | Use `OutletSink: {}`; restart server |
|
|
150
|
+
| `Unsupported reactor type` | Kind not in core Boulder; plugin not loaded | Set `BOULDER_PLUGINS`; restart server |
|
|
151
|
+
| JS chunk MIME `text/html` | Stale frontend hash or SPA catch-all | `npm run build`, restart server, hard refresh |
|
|
152
|
+
| Thermo says "Select a node…" | Nothing selected, or canvas click missed | Retry canvas click or use API |
|
|
153
|
+
| Properties Material Stream empty | `terminal_sink` not in `updated_nodes` yet | Thermo tab / API `reactor_reports` still authoritative |
|
|
154
|
+
|
|
155
|
+
## Dev mode (human)
|
|
156
|
+
|
|
157
|
+
`boulder --dev` — Vite on `:5173` proxies `/api` to backend. Agents testing production-like behavior should use built `frontend/dist` (default server).
|
|
158
|
+
|
|
159
|
+
## Further reading
|
|
160
|
+
|
|
161
|
+
- [reference.md](reference.md) — SSE stream, upload endpoint, browser checklist
|
|
162
|
+
- Repo [AGENTS.md](../../../AGENTS.md) — conda env, `make qa`, targeted tests
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Boulder GUI — API & automation reference
|
|
2
|
+
|
|
3
|
+
Base URL assumed: `http://127.0.0.1:8050`.
|
|
4
|
+
|
|
5
|
+
## Endpoints
|
|
6
|
+
|
|
7
|
+
| Method | Path | Purpose |
|
|
8
|
+
|--------|------|---------|
|
|
9
|
+
| GET | `/api/health` | Liveness |
|
|
10
|
+
| GET | `/api/configs/preloaded` | Config loaded at server start (`preloaded`, `config`, `yaml`, `filename`) |
|
|
11
|
+
| GET | `/api/configs/default` | Built-in empty template |
|
|
12
|
+
| POST | `/api/configs/upload` | Multipart YAML/YML/PY upload |
|
|
13
|
+
| POST | `/api/configs/validate` | Validate normalized config JSON |
|
|
14
|
+
| POST | `/api/simulations` | Body: `{"config": {...}}` → `{simulation_id}` |
|
|
15
|
+
| GET | `/api/simulations/{id}/stream` | SSE progress (`complete`, `error`, partial updates) |
|
|
16
|
+
| GET | `/api/simulations/{id}/results` | Full results when done; `status: running` while active |
|
|
17
|
+
|
|
18
|
+
## SSE stream (browser-equivalent completion)
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import urllib.request, json
|
|
22
|
+
|
|
23
|
+
sim_id = "..." # from POST /api/simulations
|
|
24
|
+
with urllib.request.urlopen(
|
|
25
|
+
f"http://127.0.0.1:8050/api/simulations/{sim_id}/stream") as r:
|
|
26
|
+
event = None
|
|
27
|
+
while True:
|
|
28
|
+
line = r.readline().decode().strip()
|
|
29
|
+
if not line:
|
|
30
|
+
continue
|
|
31
|
+
if line.startswith("event:"):
|
|
32
|
+
event = line[6:].strip()
|
|
33
|
+
elif line.startswith("data:") and event in ("complete", "error"):
|
|
34
|
+
payload = json.loads(line[5:])
|
|
35
|
+
print(event, payload.get("error_message"))
|
|
36
|
+
break
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
On `complete`, payload includes `reactor_reports`, `updated_nodes`, `updated_connections`, `sankey_*`, `summary`, `elapsed_time`.
|
|
40
|
+
|
|
41
|
+
## Results shape (thermo checks)
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
reports = payload["reactor_reports"] # dict[node_id → report]
|
|
45
|
+
report = reports["outlet"]
|
|
46
|
+
report["T"] # K
|
|
47
|
+
report["P"] # Pa
|
|
48
|
+
report["X"] # mole fractions dict
|
|
49
|
+
report["reactor_report"] # human-readable multi-line str
|
|
50
|
+
report["thermo_report"] # Cantera phase report str
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Reservoir / OutletSink entries use the reservoir branch of `_generate_reactor_reports` (fixed-state wording in `reactor_report` even when T reflects solved outlet).
|
|
54
|
+
|
|
55
|
+
## Preloaded vs client config
|
|
56
|
+
|
|
57
|
+
The UI sends the **in-memory normalized config** from Zustand (`useConfigStore`) to `POST /api/simulations`, not a re-read of disk. After server restart, the browser reload pulls fresh preload via `GET /api/configs/preloaded` on mount (`AppShell.tsx`).
|
|
58
|
+
|
|
59
|
+
If the header filename is wrong, the client may still hold an old config — reload the page.
|
|
60
|
+
|
|
61
|
+
## Browser MCP tool sequence
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
browser_navigate → http://127.0.0.1:8050/
|
|
65
|
+
browser_snapshot (take_screenshot_afterwards: true)
|
|
66
|
+
browser_click → Run Simulation button ref
|
|
67
|
+
# wait until "Download Python" appears in snapshot
|
|
68
|
+
browser_click → Thermo tab
|
|
69
|
+
browser_snapshot (take_screenshot_afterwards: true)
|
|
70
|
+
browser_mouse_click_xy → screenshot coords on target node (target: canvas)
|
|
71
|
+
browser_snapshot → read Thermo panel text in screenshot / search if rendered
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Unlock browser when finished: `browser_lock` action `unlock`.
|
|
75
|
+
|
|
76
|
+
## Plugins
|
|
77
|
+
|
|
78
|
+
Custom reactor kinds register via `boulder.plugins` entry points or the `BOULDER_PLUGINS` environment variable (dotted module path). The CLI may also pass `--runner PKG.MOD:CLASS` for a custom load/normalize pipeline.
|
|
79
|
+
|
|
80
|
+
Symptom: `Unsupported reactor type: 'MyKind'` → plugin package not loaded or builder not registered.
|
|
81
|
+
|
|
82
|
+
## Frontend rebuild
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
conda activate boulder
|
|
86
|
+
cd frontend && npm run build
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Restart uvicorn after backend or static asset changes.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
These are the Lead developer instructions.
|
|
2
|
+
|
|
3
|
+
Follow the development guidelines :
|
|
4
|
+
|
|
5
|
+
CODING
|
|
6
|
+
- Keep line length under 110 characters
|
|
7
|
+
- Don't wrap code in try except blocks unless you're catching a specific exception.
|
|
8
|
+
- Better raise a clear error message than hide the problems/fix in the back of the user.
|
|
9
|
+
|
|
10
|
+
TESTING
|
|
11
|
+
- Use test-driven development
|
|
12
|
+
- Fix library code, not tests
|
|
13
|
+
- Run tests in 'boulder' conda env. It contains `node`.
|
|
14
|
+
- Frontend build runs from `frontend` with `npm run build`. Always rebuild the frontend after changes.
|
|
15
|
+
- After successful tests, use `make qa` for code quality.
|
|
16
|
+
- Do not implement mock tests, only real tests.
|
|
17
|
+
- Explicit describe in the test docstrings what is asserted in the test.
|
|
18
|
+
- For E2E tests also, explicitely state in the docstrings what is assessed.
|
|
19
|
+
|
|
20
|
+
VERSIONING
|
|
21
|
+
- Never perform git commits
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
name: on-push
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
tags:
|
|
8
|
+
- '*'
|
|
9
|
+
pull_request:
|
|
10
|
+
branches:
|
|
11
|
+
- main
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
defaults:
|
|
18
|
+
run:
|
|
19
|
+
shell: bash -l {0}
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
pre-commit:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
- uses: actions/setup-python@v5
|
|
27
|
+
with:
|
|
28
|
+
python-version: 3.x
|
|
29
|
+
- uses: pre-commit/action@v3.0.1
|
|
30
|
+
|
|
31
|
+
combine-environments:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
- uses: actions/setup-python@v5
|
|
37
|
+
with:
|
|
38
|
+
python-version: 3.x
|
|
39
|
+
- name: Install conda-merge
|
|
40
|
+
run: |
|
|
41
|
+
python -m pip install conda-merge
|
|
42
|
+
- name: Combine environments
|
|
43
|
+
run: |
|
|
44
|
+
for SUFFIX in ci integration; do
|
|
45
|
+
conda-merge ci/environment-$SUFFIX.yml environment.yml > ci/combined-environment-$SUFFIX.yml || exit
|
|
46
|
+
done
|
|
47
|
+
- uses: actions/upload-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
name: combined-environments
|
|
50
|
+
path: ci/combined-environment-*.yml
|
|
51
|
+
|
|
52
|
+
unit-tests:
|
|
53
|
+
name: unit-tests
|
|
54
|
+
needs: combine-environments
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
strategy:
|
|
57
|
+
matrix:
|
|
58
|
+
python-version: ['3.11']
|
|
59
|
+
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v4
|
|
62
|
+
- uses: actions/download-artifact@v4
|
|
63
|
+
with:
|
|
64
|
+
name: combined-environments
|
|
65
|
+
path: ci
|
|
66
|
+
- name: Get current date
|
|
67
|
+
id: date
|
|
68
|
+
run: echo "date=$(date +%Y-%m-%d)" >> "${GITHUB_OUTPUT}"
|
|
69
|
+
- uses: mamba-org/setup-micromamba@v2
|
|
70
|
+
with:
|
|
71
|
+
environment-file: ci/combined-environment-ci.yml
|
|
72
|
+
environment-name: DEVELOP
|
|
73
|
+
cache-environment: true
|
|
74
|
+
cache-environment-key: environment-${{ steps.date.outputs.date }}
|
|
75
|
+
cache-downloads-key: downloads-${{ steps.date.outputs.date }}
|
|
76
|
+
create-args: >-
|
|
77
|
+
python=${{ matrix.python-version }}
|
|
78
|
+
- name: Install package
|
|
79
|
+
run: |
|
|
80
|
+
python -m pip install --no-deps -e .
|
|
81
|
+
- name: Run tests
|
|
82
|
+
run: |
|
|
83
|
+
make unit-tests COV_REPORT=xml
|
|
84
|
+
|
|
85
|
+
type-check:
|
|
86
|
+
needs: [combine-environments, unit-tests]
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
|
|
89
|
+
steps:
|
|
90
|
+
- uses: actions/checkout@v4
|
|
91
|
+
- uses: actions/download-artifact@v4
|
|
92
|
+
with:
|
|
93
|
+
name: combined-environments
|
|
94
|
+
path: ci
|
|
95
|
+
- name: Get current date
|
|
96
|
+
id: date
|
|
97
|
+
run: echo "date=$(date +%Y-%m-%d)" >> "${GITHUB_OUTPUT}"
|
|
98
|
+
- uses: mamba-org/setup-micromamba@v2
|
|
99
|
+
with:
|
|
100
|
+
environment-file: ci/combined-environment-ci.yml
|
|
101
|
+
environment-name: DEVELOP
|
|
102
|
+
cache-environment: true
|
|
103
|
+
cache-environment-key: environment-${{ steps.date.outputs.date }}
|
|
104
|
+
cache-downloads-key: downloads-${{ steps.date.outputs.date }}
|
|
105
|
+
create-args: >-
|
|
106
|
+
python=3.11
|
|
107
|
+
- name: Install package
|
|
108
|
+
run: |
|
|
109
|
+
python -m pip install --no-deps -e .
|
|
110
|
+
- name: Run code quality checks
|
|
111
|
+
run: |
|
|
112
|
+
make type-check
|
|
113
|
+
|
|
114
|
+
frontend-tests:
|
|
115
|
+
name: frontend-tests
|
|
116
|
+
needs: [unit-tests]
|
|
117
|
+
runs-on: ubuntu-latest
|
|
118
|
+
|
|
119
|
+
steps:
|
|
120
|
+
- uses: actions/checkout@v4
|
|
121
|
+
- uses: actions/setup-node@v4
|
|
122
|
+
with:
|
|
123
|
+
node-version: '20'
|
|
124
|
+
cache: 'npm'
|
|
125
|
+
cache-dependency-path: frontend/package-lock.json
|
|
126
|
+
- name: Install frontend dependencies
|
|
127
|
+
working-directory: frontend
|
|
128
|
+
run: npm ci
|
|
129
|
+
- name: Run frontend unit tests
|
|
130
|
+
working-directory: frontend
|
|
131
|
+
run: npm run test:unit
|
|
132
|
+
- name: Type check frontend
|
|
133
|
+
working-directory: frontend
|
|
134
|
+
run: npm run type-check
|
|
135
|
+
- name: Build frontend
|
|
136
|
+
working-directory: frontend
|
|
137
|
+
run: npm run build
|
|
138
|
+
|
|
139
|
+
docs-build:
|
|
140
|
+
needs: [combine-environments, unit-tests]
|
|
141
|
+
runs-on: ubuntu-latest
|
|
142
|
+
|
|
143
|
+
steps:
|
|
144
|
+
- uses: actions/checkout@v4
|
|
145
|
+
- uses: actions/download-artifact@v4
|
|
146
|
+
with:
|
|
147
|
+
name: combined-environments
|
|
148
|
+
path: ci
|
|
149
|
+
- name: Get current date
|
|
150
|
+
id: date
|
|
151
|
+
run: echo "date=$(date +%Y-%m-%d)" >> "${GITHUB_OUTPUT}"
|
|
152
|
+
- uses: mamba-org/setup-micromamba@v2
|
|
153
|
+
with:
|
|
154
|
+
environment-file: ci/combined-environment-ci.yml
|
|
155
|
+
environment-name: DEVELOP
|
|
156
|
+
cache-environment: true
|
|
157
|
+
cache-environment-key: environment-${{ steps.date.outputs.date }}
|
|
158
|
+
cache-downloads-key: downloads-${{ steps.date.outputs.date }}
|
|
159
|
+
create-args: >-
|
|
160
|
+
python=3.11
|
|
161
|
+
- name: Install package
|
|
162
|
+
run: |
|
|
163
|
+
python -m pip install --no-deps -e .
|
|
164
|
+
- name: Build documentation
|
|
165
|
+
run: |
|
|
166
|
+
make docs-build
|
|
167
|
+
# create docs in html/
|
|
168
|
+
# From https://github.com/marketplace/actions/sphinx-build
|
|
169
|
+
- uses: actions/upload-pages-artifact@v3
|
|
170
|
+
with:
|
|
171
|
+
path: docs/_build/html/
|
|
172
|
+
|
|
173
|
+
# Deployment job.
|
|
174
|
+
deploy:
|
|
175
|
+
needs: docs-build
|
|
176
|
+
|
|
177
|
+
# Deploy only when the main branch is pushed.
|
|
178
|
+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
|
179
|
+
|
|
180
|
+
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment.
|
|
181
|
+
permissions:
|
|
182
|
+
contents: read
|
|
183
|
+
pages: write # To deploy to Pages.
|
|
184
|
+
id-token: write # To verify the deployment originates from an appropriate source.
|
|
185
|
+
|
|
186
|
+
environment:
|
|
187
|
+
name: github-pages
|
|
188
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
189
|
+
runs-on: ubuntu-latest
|
|
190
|
+
steps:
|
|
191
|
+
- name: Deploy to GitHub Pages
|
|
192
|
+
id: deployment
|
|
193
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
name: Build and Publish on Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
name: Build wheel + sdist (with bundled frontend)
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write # upload artifacts to the GitHub release
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout code
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0 # Fetch all history for setuptools_scm to work
|
|
19
|
+
|
|
20
|
+
- name: Set up Node
|
|
21
|
+
uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: '20'
|
|
24
|
+
cache: 'npm'
|
|
25
|
+
cache-dependency-path: frontend/package-lock.json
|
|
26
|
+
|
|
27
|
+
- name: Build frontend
|
|
28
|
+
working-directory: frontend
|
|
29
|
+
run: |
|
|
30
|
+
npm ci
|
|
31
|
+
npm run build # emits into boulder/_frontend (see vite.config.ts)
|
|
32
|
+
|
|
33
|
+
- name: Set up Python
|
|
34
|
+
uses: actions/setup-python@v5
|
|
35
|
+
with:
|
|
36
|
+
python-version: '3.11'
|
|
37
|
+
|
|
38
|
+
- name: Install build dependencies
|
|
39
|
+
run: |
|
|
40
|
+
python -m pip install --upgrade pip
|
|
41
|
+
pip install build setuptools setuptools_scm wheel
|
|
42
|
+
|
|
43
|
+
- name: Build wheel and source distribution
|
|
44
|
+
run: python -m build
|
|
45
|
+
|
|
46
|
+
- name: Verify frontend is bundled in the wheel
|
|
47
|
+
run: |
|
|
48
|
+
python -m zipfile -l dist/*.whl | grep -q "boulder/_frontend/index.html" \
|
|
49
|
+
&& echo "OK: frontend bundled in wheel" \
|
|
50
|
+
|| (echo "ERROR: frontend missing from wheel" && exit 1)
|
|
51
|
+
|
|
52
|
+
- name: Display build artifacts
|
|
53
|
+
run: ls -lh dist/
|
|
54
|
+
|
|
55
|
+
- name: Upload artifacts to the GitHub release
|
|
56
|
+
uses: softprops/action-gh-release@v2
|
|
57
|
+
with:
|
|
58
|
+
files: |
|
|
59
|
+
dist/*.whl
|
|
60
|
+
dist/*.tar.gz
|
|
61
|
+
env:
|
|
62
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
63
|
+
|
|
64
|
+
- name: Store distribution packages
|
|
65
|
+
uses: actions/upload-artifact@v4
|
|
66
|
+
with:
|
|
67
|
+
name: python-package-distributions
|
|
68
|
+
path: dist/
|
|
69
|
+
|
|
70
|
+
publish-to-pypi:
|
|
71
|
+
name: Publish to PyPI
|
|
72
|
+
needs: build
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
environment:
|
|
75
|
+
name: pypi
|
|
76
|
+
url: https://pypi.org/project/boulder/
|
|
77
|
+
permissions:
|
|
78
|
+
id-token: write # mandatory for PyPI Trusted Publishing (OIDC)
|
|
79
|
+
|
|
80
|
+
steps:
|
|
81
|
+
- name: Download distribution packages
|
|
82
|
+
uses: actions/download-artifact@v4
|
|
83
|
+
with:
|
|
84
|
+
name: python-package-distributions
|
|
85
|
+
path: dist/
|
|
86
|
+
|
|
87
|
+
- name: Publish distribution to PyPI
|
|
88
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|