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
115 changes: 115 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: Release

on:
push:
tags:
- 'v*'

permissions:
contents: write

jobs:
build-linux-x64:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Linux release dependencies
run: |
sudo apt-get update
sudo apt-get install -y gcc make python3 raylib-dev

- name: Build Linux release bundle
run: |
make package-release-linux \
CC=gcc \
PYTHON=python3 \
RELEASE_PLATFORM=linux-x64 \
RELEASE_ARCHIVE=./dist/engine-control-test-rig-simulator-linux-x64.tar.gz

- name: Audit Linux release bundle
run: |
rm -rf ./dist/test-linux
mkdir -p ./dist/test-linux
tar -xzf ./dist/engine-control-test-rig-simulator-linux-x64.tar.gz -C ./dist/test-linux
cd ./dist/test-linux/engine-control-test-rig-simulator-linux-x64
python3 tools/release_audit.py --bundle-dir . --skip-visualizer

- name: Upload Linux bundle artifact
uses: actions/upload-artifact@v4
with:
name: release-linux-x64
path: dist/engine-control-test-rig-simulator-linux-x64.tar.gz

build-win64:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

- name: Install MSYS2 toolchain
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: >-
mingw-w64-x86_64-gcc
mingw-w64-x86_64-make
mingw-w64-x86_64-python
mingw-w64-x86_64-raylib

- name: Build Win64 release bundle
shell: msys2 {0}
run: |
make all visualizer CC=gcc PYTHON=python
make generate-visualization-json CC=gcc PYTHON=python

rm -rf dist/win64-runtime
mkdir -p dist/win64-runtime
python tools/collect_mingw_runtime_dlls.py \
--output-dir ./dist/win64-runtime \
--binary ./build/testrig.exe \
--binary ./build/visualizer.exe \
--search-dir /mingw64/bin \
--objdump objdump

python tools/package_release.py \
--platform win64 \
--testrig ./build/testrig.exe \
--visualizer ./build/visualizer.exe \
--archive ./dist/engine-control-test-rig-simulator-win64.zip \
--extra-tree ./dist/win64-runtime:.

- name: Audit Win64 release bundle
shell: msys2 {0}
run: |
rm -rf ./dist/test-win64
mkdir -p ./dist/test-win64
python -c 'import zipfile; zipfile.ZipFile("./dist/engine-control-test-rig-simulator-win64.zip").extractall("./dist/test-win64")'
cd ./dist/test-win64/engine-control-test-rig-simulator-win64
python tools/release_audit.py --bundle-dir . --skip-visualizer

- name: Upload Win64 bundle artifact
uses: actions/upload-artifact@v4
with:
name: release-win64
path: dist/engine-control-test-rig-simulator-win64.zip

publish:
needs:
- build-linux-x64
- build-win64
runs-on: ubuntu-latest
steps:
- name: Download release artifacts
uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true

- name: Publish GitHub release
uses: softprops/action-gh-release@v2
with:
files: |
dist/engine-control-test-rig-simulator-linux-x64.tar.gz
dist/engine-control-test-rig-simulator-win64.zip
generate_release_notes: true
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
build/
build-win64/
coverage/
.vscode/
.cache/
dist/
tools/__pycache__/
*.pyc
.vscode/*
!.vscode/tasks.json
70 changes: 70 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "release: test linux artifact",
"type": "shell",
"command": "make",
"args": [
"test-release-linux",
"RELEASE_PLATFORM=linux-x64",
"RELEASE_ARCHIVE=./dist/engine-control-test-rig-simulator-linux-x64.tar.gz",
"CC=gcc",
"PYTHON=python3"
],
"group": "test",
"problemMatcher": []
},
{
"label": "release: test win64 artifact with wine",
"type": "shell",
"command": "make",
"args": [
"test-release-win64-local",
"RELEASE_PLATFORM=win64",
"RELEASE_ARCHIVE=./dist/engine-control-test-rig-simulator-win64.zip",
"PYTHON=python3"
],
"group": "test",
"problemMatcher": []
},
{
"label": "release: package linux artifact",
"type": "shell",
"command": "make",
"args": [
"package-release-linux",
"RELEASE_PLATFORM=linux-x64",
"RELEASE_ARCHIVE=./dist/engine-control-test-rig-simulator-linux-x64.tar.gz",
"CC=gcc",
"PYTHON=python3"
],
"group": "build",
"problemMatcher": []
},
{
"label": "release: package win64 artifact",
"type": "shell",
"command": "make",
"args": [
"package-release-win64-local",
"RELEASE_PLATFORM=win64",
"RELEASE_ARCHIVE=./dist/engine-control-test-rig-simulator-win64.zip",
"PYTHON=python3"
],
"group": "build",
"problemMatcher": []
},
{
"label": "release: generate visualization bundle",
"type": "shell",
"command": "make",
"args": [
"generate-visualization-json",
"CC=gcc",
"PYTHON=python3"
],
"problemMatcher": []
}
]
}
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
All notable changes to the Engine Control Test Rig Simulator are documented here.
This project follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) conventions.

## [1.4.0] - 2026-03-13

### Added
- GitHub Release workflow that builds and publishes Linux and Win64 runnable artifacts for both the simulator and the Raylib visualizer.
- Portable release bundle audit script and packaged verification assets so unpacked artifacts can validate simulator behavior outside the source tree.
- Local Linux and Wine-based Win64 artifact test targets, runtime dependency collection helpers, and VS Code tasks for repeatable release dry runs.

### Changed
- Release documentation moved out of the main README into a dedicated release artifacts document to keep the top-level project overview focused on architecture and verification.

## [1.3.2] - 2026-03-13

### Changed
Expand Down
87 changes: 78 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
CC = clang
GCOV = llvm-cov gcov
PYTHON ?= python3

SRC_DIR = src
BUILD_DIR = ./build
COVERAGE_DIR = ./coverage
COVERAGE_HTML_DIR = ./coverage/html

TARGET = $(BUILD_DIR)/testrig
VISUALIZER_TARGET = $(BUILD_DIR)/visualizer
UNIT_TEST_TARGET = $(BUILD_DIR)/unit_tests
VALGRIND_TARGET = $(BUILD_DIR)/testrig_valgrind
VALGRIND_UNIT_TEST_TARGET = $(BUILD_DIR)/unit_tests_valgrind
DIST_DIR = ./dist
LINUX_RELEASE_RUNTIME_DIR = $(DIST_DIR)/linux-runtime
WIN64_RELEASE_RUNTIME_DIR = $(DIST_DIR)/win64-runtime
WIN64_BUILD_DIR = ./build-win64

EXEEXT =
ifeq ($(OS),Windows_NT)
EXEEXT = .exe
RAYLIB_LIBS ?= -lraylib -lopengl32 -lgdi32 -lwinmm
else
RAYLIB_LIBS ?= -lraylib -lm -lpthread -ldl -lrt -lX11
endif

TARGET = $(BUILD_DIR)/testrig$(EXEEXT)
VISUALIZER_TARGET = $(BUILD_DIR)/visualizer$(EXEEXT)
UNIT_TEST_TARGET = $(BUILD_DIR)/unit_tests$(EXEEXT)
VALGRIND_TARGET = $(BUILD_DIR)/testrig_valgrind$(EXEEXT)
VALGRIND_UNIT_TEST_TARGET = $(BUILD_DIR)/unit_tests_valgrind$(EXEEXT)

RELEASE_PLATFORM ?= unknown
RELEASE_ARCHIVE ?= $(DIST_DIR)/engine-control-test-rig-simulator-$(RELEASE_PLATFORM).tar.gz

VISUALIZER_SRC = visualization/src/main.c \
visualization/src/visualizer_app.c \
Expand Down Expand Up @@ -48,10 +64,10 @@ CFLAGS = $(COMMON_CFLAGS) -O2 -fstack-protector-strong -D_FORTIFY_SOURCE=2
DEBUG_CFLAGS = $(COMMON_CFLAGS) -O0 -g -fsanitize=address,undefined -fno-omit-frame-pointer
COVERAGE_CFLAGS = $(COMMON_CFLAGS) -O0 --coverage
VALGRIND_CFLAGS = $(COMMON_CFLAGS) -O0 -g -fno-omit-frame-pointer
VISUALIZER_CFLAGS = $(filter-out -pedantic,$(CFLAGS)) -Wno-pedantic

CPPFLAGS = -I./include -I./src -I./visualization/include -DSIM_BUILD_COMMIT_OVERRIDE=\"$(BUILD_COMMIT)\"
LDFLAGS = -lm
RAYLIB_LIBS = -lraylib -lm -lpthread -ldl
VALGRIND ?= valgrind
VALGRIND_ARGS = --leak-check=full --show-leak-kinds=all --track-origins=yes --errors-for-leak-kinds=all --error-exitcode=1
LCOV_RC_OPTS ?= --rc derive_function_end_line=0
Expand All @@ -65,7 +81,7 @@ $(TARGET): $(BUILD_DIR) $(SRCS)
$(CC) $(CPPFLAGS) $(CFLAGS) -o $(TARGET) $(SRCS) $(LDFLAGS)

$(VISUALIZER_TARGET): $(BUILD_DIR) $(VISUALIZER_SRC)
$(CC) $(CPPFLAGS) $(CFLAGS) -o $(VISUALIZER_TARGET) $(VISUALIZER_SRC) $(RAYLIB_LIBS)
$(CC) $(CPPFLAGS) $(VISUALIZER_CFLAGS) -o $(VISUALIZER_TARGET) $(VISUALIZER_SRC) $(RAYLIB_LIBS)

$(UNIT_TEST_TARGET): $(BUILD_DIR) $(UNIT_TEST_SRCS) $(UNIT_TEST_DEPS)
$(CC) $(CPPFLAGS) $(CFLAGS) -o $(UNIT_TEST_TARGET) $(UNIT_TEST_SRCS) $(UNIT_TEST_DEPS) $(LDFLAGS)
Expand All @@ -87,6 +103,7 @@ clean:
rm -f $(VISUALIZER_TARGET)
rm -f $(BUILD_DIR)/*.gcno $(BUILD_DIR)/*.gcda $(BUILD_DIR)/*.info
rm -rf $(COVERAGE_DIR)
rm -rf $(DIST_DIR)

run-script: $(TARGET)
@if [ -z "$(SCRIPT)" ]; then \
Expand Down Expand Up @@ -126,7 +143,59 @@ run-visualizer: $(VISUALIZER_TARGET)
$(VISUALIZER_TARGET) "$(JSON)"

generate-visualization-json: $(TARGET)
python3 tools/generate_visualization_scenario_json.py
$(PYTHON) tools/generate_visualization_scenario_json.py --testrig "$(TARGET)"

package-release: $(TARGET) $(VISUALIZER_TARGET) generate-visualization-json
$(PYTHON) tools/package_release.py \
--platform "$(RELEASE_PLATFORM)" \
--testrig "$(TARGET)" \
--visualizer "$(VISUALIZER_TARGET)" \
--archive "$(RELEASE_ARCHIVE)"

package-release-linux: $(TARGET) $(VISUALIZER_TARGET) generate-visualization-json
rm -rf "$(LINUX_RELEASE_RUNTIME_DIR)"
mkdir -p "$(LINUX_RELEASE_RUNTIME_DIR)/lib"
$(PYTHON) tools/collect_linux_runtime_deps.py \
--output-dir "$(LINUX_RELEASE_RUNTIME_DIR)/lib" \
--binary "$(TARGET)" \
--binary "$(VISUALIZER_TARGET)"
$(PYTHON) tools/package_release.py \
--platform "$(RELEASE_PLATFORM)" \
--testrig "$(TARGET)" \
--visualizer "$(VISUALIZER_TARGET)" \
--archive "$(RELEASE_ARCHIVE)" \
--linux-launchers \
--extra-tree "$(LINUX_RELEASE_RUNTIME_DIR)/lib:lib"

package-release-win64-local:
sh tools/build_win64_release.sh
rm -rf "$(WIN64_RELEASE_RUNTIME_DIR)"
mkdir -p "$(WIN64_RELEASE_RUNTIME_DIR)"
$(PYTHON) tools/collect_mingw_runtime_dlls.py \
--output-dir "$(WIN64_RELEASE_RUNTIME_DIR)" \
--binary "$(WIN64_BUILD_DIR)/testrig.exe" \
--binary "$(WIN64_BUILD_DIR)/visualizer.exe" \
--search-dir /usr/x86_64-w64-mingw32/bin
$(PYTHON) tools/package_release.py \
--platform "$(RELEASE_PLATFORM)" \
--testrig "$(WIN64_BUILD_DIR)/testrig.exe" \
--visualizer "$(WIN64_BUILD_DIR)/visualizer.exe" \
--archive "$(RELEASE_ARCHIVE)" \
--extra-tree "$(WIN64_RELEASE_RUNTIME_DIR):."

test-release-linux: package-release-linux
rm -rf "$(DIST_DIR)/test-linux"
mkdir -p "$(DIST_DIR)/test-linux"
tar -xzf "$(RELEASE_ARCHIVE)" -C "$(DIST_DIR)/test-linux"
cd "$(DIST_DIR)/test-linux/engine-control-test-rig-simulator-$(RELEASE_PLATFORM)" && \
$(PYTHON) tools/release_audit.py --bundle-dir . --visualizer-timeout 3

test-release-win64-local: package-release-win64-local
rm -rf "$(DIST_DIR)/test-win64"
mkdir -p "$(DIST_DIR)/test-win64"
$(PYTHON) -c 'import zipfile; zipfile.ZipFile("$(RELEASE_ARCHIVE)").extractall("$(DIST_DIR)/test-win64")'
cd "$(DIST_DIR)/test-win64/engine-control-test-rig-simulator-$(RELEASE_PLATFORM)" && \
$(PYTHON) tools/release_audit.py --bundle-dir . --command-prefix wine --visualizer-timeout 5 --skip-visualization-regeneration

analyze-cppcheck:
cppcheck --enable=all --std=c11 --error-exitcode=1 \
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,16 @@ Theme selection:
- Start with `--theme default`, `--theme dos`, `--theme onyx`, `--theme gruvbox`, or `--theme light`
- Press `T` while the visualizer is running to cycle between themes

### Release bundles

Release packaging, shipped audit flow, local artifact testing, and VS Code tasks are documented in [docs/release_artifacts.md](docs/release_artifacts.md).

At a high level:

- tags matching `v*` publish Linux and Win64 runnable artifacts
- each bundle includes a shipped simulator audit entry point via `tools/release_audit.py`
- local Linux and Wine-based Win64 artifact tests execute that packaged audit before release, with the Wine path using a lighter audit mode to avoid repeated slow Wine startups



## Module API Overview
Expand Down Expand Up @@ -397,9 +407,10 @@ Usage:
| [docs/static_analysis_baseline_policy.md](docs/static_analysis_baseline_policy.md) | Zero-warning baseline policy |
| [docs/determinism_guarantee.md](docs/determinism_guarantee.md) | Determinism implementation and CI enforcement via SHA-256 replay check |
| [docs/schema_evolution.md](docs/schema_evolution.md) | Semantic versioning policy for JSON output schema |
| [docs/release_artifacts.md](docs/release_artifacts.md) | Release packaging, shipped audit flow, local artifact testing, and VS Code tasks |
| [docs/message_map.md](docs/message_map.md) | BusFrame ID registry documentation with payload layouts |
| [docs/error_severity_model.md](docs/error_severity_model.md) | Structured severity/recoverability classification reference |
| [docs/MISRA_C:2012_Supported_Rules.md](docs/MISRA_C:2012_Supported_Rules.md) | List of supported MISRA C:2012 rules |
| [docs/MISRA_C_2012_Supported_Rules.md](docs/MISRA_C_2012_Supported_Rules.md) | List of supported MISRA C:2012 rules |
| `docs/adr/` | Architecture Decision Records (ADR-001 through ADR-004) |
| [CHANGELOG.md](CHANGELOG.md) | Version history following Keep a Changelog conventions |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,4 @@
| Rule 23.5 | A generic selection should not depend on implicit pointer type conversion. | Advisory |
| Rule 23.6 | The controlling expression of a generic selection shall have an essential type that matches its standard type. | Required |
| Rule 23.7 | A generic selection that is expanded from a macro should evaluate its argument only once. | Advisory |
| Rule 23.8 | A default association shall appear as either the first or the last association of a generic selection. | Required |
| Rule 23.8 | A default association shall appear as either the first or the last association of a generic selection. | Required |
Loading
Loading