Skip to content
Draft
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
22 changes: 22 additions & 0 deletions .github/workflows/build_and_test_autosd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ jobs:
with:
name: eclipse-autosd-bootc-qemu-${{ matrix.platform }}.qcow2.xz
path: images/autosd/eclipse-autosd-bootc-qemu-${{ matrix.platform }}.qcow2.xz
- name: Package platform and showcases
if: github.event_name == 'release'
run: |
bazel build --lockfile_mode=error --config autosd-${{ matrix.platform }} //deploy:platform_tar //showcases:showcases_pkg_tar
gzip -c bazel-bin/deploy/platform_tar.tar > score-platform-autosd-${{ matrix.platform }}.tar.gz
gzip -c bazel-bin/showcases/showcases_pkg_tar.tar > score-showcases-autosd-${{ matrix.platform }}.tar.gz
- name: Store platform and showcases packages
if: github.event_name == 'release'
uses: actions/upload-artifact@v4
with:
name: score-release-autosd-${{ matrix.platform }}
path: |
score-platform-autosd-${{ matrix.platform }}.tar.gz
score-showcases-autosd-${{ matrix.platform }}.tar.gz
release:
name: Publish image to release
needs: build
Expand All @@ -143,10 +157,18 @@ jobs:
with:
name: eclipse-autosd-bootc-qemu-x86_64.qcow2.xz
path: ./release-assets
- name: Download platform and showcases packages
uses: actions/download-artifact@v4
with:
name: score-release-autosd-x86_64
path: ./release-assets
- name: Upload image to release
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: |
gh release upload "$RELEASE_NAME" \
./release-assets/eclipse-autosd-bootc-qemu-x86_64.qcow2.xz \
./release-assets/score-platform-autosd-x86_64.tar.gz \
./release-assets/score-showcases-autosd-x86_64.tar.gz \
--clobber
35 changes: 35 additions & 0 deletions .github/workflows/build_and_test_ebclfsa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
# *******************************************************************************
name: EB corbos Linux for Safety Applications - Build & Integration Test
on:
release:
types: [created]
pull_request:
types: [opened, reopened, synchronize]
merge_group:
Expand Down Expand Up @@ -50,3 +52,36 @@ jobs:
name: test-logs
path: |
bazel-bin/images/ebclfsa_aarch64/build/*.log
- name: Package platform and showcases
if: github.event_name == 'release'
run: |
bazel build --lockfile_mode=error --config=eb-aarch64 //deploy:platform_tar //showcases:showcases_pkg_tar
gzip -c bazel-bin/deploy/platform_tar.tar > score-platform-eb-aarch64.tar.gz
gzip -c bazel-bin/showcases/showcases_pkg_tar.tar > score-showcases-eb-aarch64.tar.gz
- name: Store platform and showcases packages
if: github.event_name == 'release'
uses: actions/upload-artifact@v4
with:
name: score-release-eb-aarch64
path: |
score-platform-eb-aarch64.tar.gz
score-showcases-eb-aarch64.tar.gz
release:
name: Publish platform and showcases to release
needs: build
if: github.event_name == 'release'
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- name: Download platform and showcases packages
uses: actions/download-artifact@v4
with:
name: score-release-eb-aarch64
path: ./release-assets
- name: Upload platform and showcases to release
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: gh release upload "$RELEASE_TAG" ./release-assets/*.tar.gz --clobber
12 changes: 12 additions & 0 deletions .github/workflows/build_and_test_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,15 @@ jobs:
with:
name: sbom-spdx
path: bazel-bin/sbom.spdx.json
- name: Package platform and showcases
if: github.event_name == 'release'
run: |
bazel build --lockfile_mode=error --config=linux-x86_64 //deploy:platform_tar //showcases:showcases_pkg_tar
gzip -c bazel-bin/deploy/platform_tar.tar > score-platform-linux-x86_64.tar.gz
gzip -c bazel-bin/showcases/showcases_pkg_tar.tar > score-showcases-linux-x86_64.tar.gz
- name: Upload platform and showcases to release
if: github.event_name == 'release'
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: gh release upload "$RELEASE_TAG" score-platform-linux-x86_64.tar.gz score-showcases-linux-x86_64.tar.gz --clobber
41 changes: 41 additions & 0 deletions .github/workflows/build_and_test_qnx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
# *******************************************************************************
name: QNX8 - Build & Integration Test
on:
release:
types: [created]
pull_request_target:
types: [opened, reopened, synchronize]
merge_group:
Expand Down Expand Up @@ -77,6 +79,45 @@ jobs:
- name: Run integration tests
run: |
bazel test --lockfile_mode=error --config=itf-qnx-x86_64 //feature_integration_tests/itf
- name: Run platform integration tests
run: |
bazel test --lockfile_mode=error --config=itf-qnx-x86_64 //platform_integration_tests:pit
- name: Package platform and showcases
if: github.event_name == 'release'
env:
SCORE_QNX_USER: ${{ secrets.SCORE_QNX_USER }}
SCORE_QNX_PASSWORD: ${{ secrets.SCORE_QNX_PASSWORD }}
run: |
bazel build --lockfile_mode=error --config qnx-x86_64 //deploy:platform_tar //showcases:showcases_pkg_tar
gzip -c bazel-bin/deploy/platform_tar.tar > score-platform-qnx-x86_64.tar.gz
gzip -c bazel-bin/showcases/showcases_pkg_tar.tar > score-showcases-qnx-x86_64.tar.gz
- name: Store platform and showcases packages
if: github.event_name == 'release'
uses: actions/upload-artifact@v4
with:
name: score-release-qnx-x86_64
path: |
score-platform-qnx-x86_64.tar.gz
score-showcases-qnx-x86_64.tar.gz
- name: Cleanup QNX license
if: always()
run: sudo rm -rf /opt/score_qnx
release:
name: Publish platform and showcases to release
needs: qnx-build-x86_64
if: github.event_name == 'release'
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- name: Download platform and showcases packages
uses: actions/download-artifact@v4
with:
name: score-release-qnx-x86_64
path: ./release-assets
- name: Upload platform and showcases to release
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: gh release upload "$RELEASE_TAG" ./release-assets/*.tar.gz --clobber
44 changes: 44 additions & 0 deletions deploy/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

load("@rules_pkg//pkg:mappings.bzl", "pkg_filegroup")
load("@rules_pkg//pkg:tar.bzl", "pkg_tar")

exports_files(["README.md"])

# The S-CORE platform as deployed by this distribution: every binary and
# configuration file that an S-CORE module contributes to the shipped image.
# This is the system under test for the conformance showcases; it must stay
# free of anything that only exists to demonstrate or test the platform.
#
# Consumed by the QNX images via qnx_ifs srcs.
pkg_filegroup(
name = "platform_bundle",
srcs = [
"//deploy/communication:heartbeat_service_bin",
"//deploy/communication:heartbeat_service_configs",
"//deploy/config_management:config_daemon_bin",
"//deploy/config_management:config_daemon_configs",
"//deploy/lifecycle:launch_manager_bin",
"//deploy/logging:datarouter_bin",
"//deploy/logging:datarouter_configs",
],
visibility = ["//visibility:public"],
)

# Same content as :platform_bundle, as a tar for oci_image consumers.
pkg_tar(
name = "platform_tar",
srcs = [":platform_bundle"],
visibility = ["//visibility:public"],
)
99 changes: 99 additions & 0 deletions deploy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Platform deployment (layer 2)

The image content of this distribution is split into three layers:

| Layer | Location | Content |
| --- | --- | --- |
| 1 — Image | `//images/<variant>` | Boot, kernel, drivers, root filesystem skeleton |
| 2 — Platform | `//deploy` | The S-CORE modules as deployed — the system under test |
| 3 — Showcases | `//showcases` | Applications that demonstrate and verify the platform |

A production image ships layers 1 and 2. Layer 3 must be removable without
changing either of them.

## Why layer 2 and layer 3 are separate

The showcases are the probe, the platform is the probe's subject. If a
showcase shipped the component it exercises — as the lifecycle showcase did
with the launch manager before this split — then the check would be verifying
the binary it brought along rather than the one the distribution deployed. A
distribution that fails to deploy an S-CORE module would still pass.

Keeping them apart gives the property that makes the checks meaningful: the
showcase applications run against whatever platform the image provides, and
fail when it is missing.

That is also why a module's binaries can end up in different layers. The
lifecycle launch manager is deployed here; the applications it supervises,
and the launch manager configuration that declares them, live in
`//showcases/simple_lifecycle`.

## Contract between layer 2 and layer 3

Layer 3 may rely on the following, and on nothing else:

| Component | Deployed location |
| --- | --- |
| `launch_manager` | `/usr/bin/launch_manager` |
| `datarouter` | `/usr/bin/datarouter/datarouter`, config in `/usr/bin/datarouter/etc` |
| `config_daemon` | `/usr/bin/config_management/config_daemon`, config in `/usr/bin/config_management/etc` |
| `heartbeat_service` | `/usr/bin/communication/heartbeat_service`, config in `/usr/bin/communication/etc`; offers the mw::com service `score/platform/heartbeat` with the `heartbeat` event and the `platform_info` field, which reports the S-CORE module versions from `known_good.json` (interface in `deploy/communication/heartbeat.h`) |

There is no system-wide mw::com configuration: mw::com reads
`./etc/mw_com_config.json` relative to the working directory, or the file
passed with `-s`. mw::log only has an optional ECU-wide
`/etc/ecu_logging_config.json`; the application configuration
(`etc/logging.json` next to the executable or in the working directory, or
`MW_LOG_CONFIG_FILE`) belongs to the application. Every application ships its
own configuration.

Layer 2 never references anything under `/showcases`. The one place where
layer 2 reads layer 3 data is the launch manager, which is started by the
showcase with a working directory that holds the showcase's own
configuration — the configuration is passed in, not deployed alongside the
daemon.

## Adding a module

Add a `deploy/<module>/BUILD` that packages the module's binaries and
configuration with `pkg_files`, then list those targets in
`//deploy:platform_bundle`. Configuration that is deployed onto the target
is kept in `deploy/<module>/etc/`, not in a test directory. Keep examples,
demo applications and test binaries out; those belong in `//showcases`.
Modules that ship only libraries (for example persistency) have nothing to
deploy and get no package here. Communication is the exception: a library
cannot be checked from outside the process that links it, so the platform
deploys a heartbeat service built on it. The communication showcase consumes
that service and fails unless the platform provides it.

Long term each S-CORE module should export its own deployment package
upstream, so that "deployed correctly" becomes checkable rather than agreed.
Until then this directory is the distribution's local stand-in for it.

## Release downloads: layer 2 and layer 3 separately

Every GitHub release carries both upper layers as separate downloads, one
pair of archives per target the CI builds:

| Target | Layer 2 (platform) | Layer 3 (showcases) | Built with |
| --- | --- | --- | --- |
| Linux x86_64 | `score-platform-linux-x86_64.tar.gz` | `score-showcases-linux-x86_64.tar.gz` | `--config=linux-x86_64` |
| AutoSD x86_64 | `score-platform-autosd-x86_64.tar.gz` | `score-showcases-autosd-x86_64.tar.gz` | `--config=autosd-x86_64` |
| QNX x86_64 | `score-platform-qnx-x86_64.tar.gz` | `score-showcases-qnx-x86_64.tar.gz` | `--config=qnx-x86_64` |
| EB corbos aarch64 | `score-platform-eb-aarch64.tar.gz` | `score-showcases-eb-aarch64.tar.gz` | `--config=eb-aarch64` |

The platform archive is `//deploy:platform_tar` (unpacks to `/usr/bin/...`),
the showcases archive is `//showcases:showcases_pkg_tar` (unpacks to
`/showcases`). Both are relative to `/`:

```sh
tar -xzf score-platform-<target>.tar.gz -C / # only if the image does not ship S-CORE itself
tar -xzf score-showcases-<target>.tar.gz -C /
/showcases/bin/cli --examples all
```

To check an image that claims to ship S-CORE, copy only the showcases
archive onto it. The showcases then run against whatever platform the image
deploys, without rebuilding it — as long as it fulfils the contract above.
`heartbeat_consumer` additionally fails if the platform reports a different
S-CORE version than the release the archive belongs to.
84 changes: 84 additions & 0 deletions deploy/communication/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

# Deployment of the S-CORE communication module (layer 2, see //deploy:README.md).
#
# Communication itself ships as a library. To make "the platform provides
# working inter-process communication" checkable, the platform deploys a
# heartbeat service on top of it; consumers in layer 3 must find it via
# mw::com service discovery and receive intact heartbeats from it.

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("@rules_pkg//pkg:mappings.bzl", "pkg_attributes", "pkg_files", "strip_prefix")
load("@rules_python//python:defs.bzl", "py_binary")

package(default_visibility = ["//deploy:__pkg__"])

py_binary(
name = "gen_platform_info",
srcs = ["gen_platform_info.py"],
)

# The S-CORE module versions the platform is built from, as compile-time constants.
genrule(
name = "platform_info_data",
srcs = ["//:known_good.json"],
outs = ["platform_info_data.h"],
cmd = "$(execpath :gen_platform_info) $(execpath //:known_good.json) $@",
tools = [":gen_platform_info"],
)

# Service interface and payload check: the layer 2/3 contract of the heartbeat service.
cc_library(
name = "heartbeat_interface",
srcs = [
"heartbeat.cpp",
":platform_info_data",
],
hdrs = ["heartbeat.h"],
visibility = ["//showcases:__subpackages__"],
deps = [
"@score_baselibs//score/hash/code/crc:crc_ieee",
"@score_communication//score/mw/com",
],
)

cc_binary(
name = "heartbeat_service",
srcs = ["heartbeat_service.cpp"],
deps = [
":heartbeat_interface",
"@score_baselibs//score/string_manipulation/arguments",
"@score_communication//score/mw/com",
],
)

# Heartbeat service binary (0777), placed as /usr/bin/communication/heartbeat_service.
pkg_files(
name = "heartbeat_service_bin",
srcs = [":heartbeat_service"],
attributes = pkg_attributes(mode = "0777"),
prefix = "usr/bin/communication",
strip_prefix = strip_prefix.files_only(),
)

# mw::com configuration of the heartbeat service (0644), placed under
# /usr/bin/communication/etc. Consumers use this deployed file, so a platform
# that deploys a different service definition fails the check.
pkg_files(
name = "heartbeat_service_configs",
srcs = ["etc/mw_com_config.json"],
attributes = pkg_attributes(mode = "0644"),
prefix = "usr/bin/communication/etc",
strip_prefix = strip_prefix.files_only(),
)
Loading
Loading