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
4 changes: 3 additions & 1 deletion .github/workflows/release-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ jobs:
generate-documents:
name: 📄 Documentation
uses: ./.github/workflows/wc-document-generation.yml
with:
is-release: true
permissions:
contents: read

Expand All @@ -140,7 +142,7 @@ jobs:
- name: Upload documents to release
run: |
set -Eeuo pipefail
gh release upload "${REF_NAME}" ./*.pdf
gh release upload "${REF_NAME}" ./*.pdf ./*.sbdl
Comment thread
rjaegers marked this conversation as resolved.
env:
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
Expand Down
50 changes: 45 additions & 5 deletions .github/workflows/wc-document-generation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ name: Document Generation

on:
workflow_call:
inputs:
is-release:
Comment thread
rjaegers marked this conversation as resolved.
description: Whether the workflow is running for a release.
default: false
type: boolean
Comment thread
rjaegers marked this conversation as resolved.

permissions: {}

Expand All @@ -22,19 +27,54 @@ jobs:
- name: Install dependencies
run: |
set -Eeuo pipefail

sudo apt-get update && sudo apt-get install --no-install-recommends -y plantuml
python -m pip install gherkin-official==38.0.0 sbdl==1.21.3
python -m pip install sbdl==1.22.7
- name: Build & Validate SBDL model
run: sbdl -m compile test/cpp/features/*.feature > amp-devcontainer.sbdl
- name: Generate SRS document
run: sbdl -m template-fill --template docs/templates/software-requirements-specification.md.j2 amp-devcontainer.sbdl > software-requirements-specification.md
- uses: docker://pandoc/extra:3.9.0.0-ubuntu@sha256:72afa9c8d3300e5f10c9c4330e101725687f2179bffd912fb859c6d2ae85de62
run: sbdl -m compile test/cpp/integration-tests.bats test/cpp/features/*.feature > amp-devcontainer.sbdl
Comment thread
rjaegers marked this conversation as resolved.
- name: Create document control context
env:
GITHUB_REF_NAME: ${{ github.ref_name }}
IS_RELEASE: ${{ inputs.is-release }}
run: |
set -Eeuo pipefail

cat > document-control.sbdl <<SBDL
#!sbdl
doc_control is aspect {
description is "Document control metadata"
custom:version is "$(jq -r '.["."]' .release-please-manifest.json)"
custom:generated_at is "[@DATE]"
custom:git_sha is "[@GIT_COMMIT_HASH]"
custom:git_ref is "${GITHUB_REF_NAME}"
custom:sbdl_compiler_version is "[@COMPILER_VERSION]"
custom:sbdl_dsl_version is "[@DSL_VERSION]"
custom:is_release is "${IS_RELEASE}"
}
Comment thread
rjaegers marked this conversation as resolved.
SBDL
- name: 📄 Generate SRS document
run: sbdl -m template-fill --set-config template_extensions_file docs/templates/jinja-extensions.py --template docs/templates/software-requirements-specification.md.j2 amp-devcontainer.sbdl document-control.sbdl > software-requirements-specification.md
- name: 🧪 Generate STP document
run: sbdl -m template-fill --set-config template_extensions_file docs/templates/jinja-extensions.py --template docs/templates/software-test-plan.md.j2 amp-devcontainer.sbdl document-control.sbdl > software-test-plan.md
- name: 🧩 Generate RTM document
run: sbdl -m template-fill --set-config template_extensions_file docs/templates/jinja-extensions.py --template docs/templates/requirements-traceability-matrix.md.j2 amp-devcontainer.sbdl document-control.sbdl > requirements-traceability-matrix.md
- name: 📄 Generate SRS PDF
uses: docker://pandoc/extra:3.9.0.0-ubuntu@sha256:72afa9c8d3300e5f10c9c4330e101725687f2179bffd912fb859c6d2ae85de62
with:
args: --template eisvogel --syntax-highlighting idiomatic --number-sections --output software-requirements-specification.pdf software-requirements-specification.md
- name: 🧪 Generate STP PDF
uses: docker://pandoc/extra:3.9.0.0-ubuntu@sha256:72afa9c8d3300e5f10c9c4330e101725687f2179bffd912fb859c6d2ae85de62
with:
args: --template eisvogel --syntax-highlighting idiomatic --number-sections --output software-test-plan.pdf software-test-plan.md
- name: 🧩 Generate RTM PDF
uses: docker://pandoc/extra:3.9.0.0-ubuntu@sha256:72afa9c8d3300e5f10c9c4330e101725687f2179bffd912fb859c6d2ae85de62
with:
args: --template eisvogel --syntax-highlighting idiomatic --number-sections --output requirements-traceability-matrix.pdf requirements-traceability-matrix.md
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: documents
path: |
*.pdf
*.sbdl
!document-control.sbdl
retention-days: 2
Comment thread
rjaegers marked this conversation as resolved.
9 changes: 9 additions & 0 deletions docs/templates/jinja-extensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from datetime import datetime

def strftime_filter(value, fmt):
Comment thread
rjaegers marked this conversation as resolved.
if value.endswith('Z'):
value = value[:-1] + '+00:00'

return datetime.fromisoformat(value).strftime(fmt)

filters = { 'strftime': strftime_filter }
7 changes: 7 additions & 0 deletions docs/templates/partials/document-control.md.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
| Property | Value |
|-------------------|---------------------------------------------------------------------------------------------------------------------------|
| Document version | {{ sbdl['doc_control']['custom:version'] }} |
| Generation date | {{ sbdl['doc_control']['custom:generated_at'] | strftime('%Y-%m-%d') }} |
| Source revision | {{ sbdl['doc_control']['custom:git_sha'] }} |
| Source branch/tag | {{ sbdl['doc_control']['custom:git_ref'] }} |
| Model | SBDL {{ sbdl['doc_control']['custom:sbdl_compiler_version'] }} (DSL {{ sbdl['doc_control']['custom:sbdl_dsl_version'] }}) |
15 changes: 15 additions & 0 deletions docs/templates/partials/text-utilities.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% macro reencode(text) -%}
{{ text.encode('utf-8').decode('unicode_escape') }}
{%- endmacro -%}

{%- macro strip_gherkin_prefix(text) -%}
{{ text | replace('Rule: ', '') | replace('Feature: ', '') }}
{%- endmacro -%}

{%- macro strip_bats_syntax(text) -%}
{{ text | replace('@test "', '') | replace('" \{', '') | replace('" {', '') }}
{%- endmacro -%}

{%- macro display_short(text) -%}
{{ text[:60] }}
{%- endmacro %}
115 changes: 115 additions & 0 deletions docs/templates/requirements-traceability-matrix.md.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
---
title: "Requirements traceability matrix for amp-devcontainer"
author: ["@rjaegers"]
colorlinks: true
date: "{{ sbdl['doc_control']['custom:generated_at'] | strftime('%Y-%m-%d') }}"
keywords: [Traceability, Requirements, RTM, amp-devcontainer]
lang: "en"
titlepage: true
titlepage-color: "0B5ED7"
titlepage-text-color: "FFFFFF"
titlepage-rule-color: "FFFFFF"
titlepage-rule-height: 2
toc: true
toc-own-page: true
header-includes:
- \AtEndDocument{\label{lastpage}}
{%- if sbdl['doc_control']['custom:is_release'] != 'true' %}
watermark: "DRAFT"
{%- endif %}
footer-right: "\\thepage \\hspace{1pt} of \\pageref*{lastpage}"
...

{% import "partials/text-utilities.j2" as utils %}

# Introduction

## Purpose

This document provides a requirements traceability matrix (RTM) for amp-devcontainer. It maps each requirement to its associated verification tests, providing evidence of test coverage across the system.

## Abstract

amp-devcontainer is a set of [devcontainers](https://containers.dev/) tailored towards modern, embedded, software development. This traceability matrix traces requirements from the *Software Requirements Specification (SRS)* to their corresponding verification tests as enumerated in the *Software Test Plan (STP)*.

## Document Control

{% include "partials/document-control.md.j2" with context %}

This document is generated from a formal model defined in [sbdl](https://sbdl.dev) and versioned alongside the source code in Git.
The authoritative source of change history is the [Git log](https://github.com/philips-software/amp-devcontainer/commits/) of the source material from which the model is built.

# Traceability Matrix
{%- for id, item in sbdl.items() %}
{%- if item.type == 'aspect' and 'custom:title' in item %}

## {{ utils.reencode(utils.strip_gherkin_prefix(item['custom:title'])) }}

| Requirement | Test | Status |
|-------------|------|--------|

{%- if 'requirement' in item %}
{%- for req_ref in item.requirement %}
{%- set req = sbdl[req_ref.identifier] %}
{%- set req_name = utils.reencode(utils.strip_gherkin_prefix(req['custom:title'])) | trim %}

{%- set ns_tests = namespace(test_list=[]) -%}

{%- for test_id, test_elem in sbdl.items() -%}
{%- if test_elem.type == 'test' -%}
{%- if 'requirement' in test_elem -%}
{%- for test_req_ref in test_elem.requirement if test_req_ref.identifier == req_ref.identifier -%}
{%- set _ = ns_tests.test_list.append(test_id) -%}
{%- endfor -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}

{%- if ns_tests.test_list -%}
{%- for test_id in ns_tests.test_list %}
{%- set test_name = utils.strip_bats_syntax(sbdl[test_id]['custom:title']) | trim %}
| {{ utils.display_short(req_name) }} | {{ test_id }}: {{ utils.display_short(test_name) }} | Traced |
{%- endfor -%}
{%- else %}
| {{ utils.display_short(req_name) }} | — | **Not covered** |
{%- endif -%}

{%- endfor %}
{%- endif %}
{%- endif %}
{%- endfor %}

# Coverage Summary

{%- set ns_summary = namespace(total_reqs=0, covered_reqs=0, total_tests=0) -%}

{%- for elem_id, elem in sbdl.items() -%}
{%- if elem.type == 'requirement' -%}
{%- set ns_summary.total_reqs = ns_summary.total_reqs + 1 -%}

{%- set ns_covered = namespace(is_covered=false) -%}
{%- for test_id, test_elem in sbdl.items() -%}
{%- if test_elem.type == 'test' and 'requirement' in test_elem -%}
{%- for req_ref in test_elem.requirement if req_ref.identifier == elem_id -%}
{%- set ns_covered.is_covered = true -%}
{%- endfor -%}
{%- endif -%}
{%- endfor -%}

{%- if ns_covered.is_covered -%}
{%- set ns_summary.covered_reqs = ns_summary.covered_reqs + 1 -%}
{%- endif -%}

{%- endif -%}

{%- if elem.type == 'test' -%}
{%- set ns_summary.total_tests = ns_summary.total_tests + 1 -%}
{%- endif -%}
{%- endfor %}

| Metric | Value |
|---|---|
| Total requirements | {{ ns_summary.total_reqs }} |
| Requirements with tests | {{ ns_summary.covered_reqs }} |
| Requirements without tests | {{ ns_summary.total_reqs - ns_summary.covered_reqs }} |
| Total tests | {{ ns_summary.total_tests }} |
73 changes: 55 additions & 18 deletions docs/templates/software-requirements-specification.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: "Software requirements specification for amp-devcontainer"
author: ["@rjaegers"]
colorlinks: true
date: "February-2026"
date: "{{ sbdl['doc_control']['custom:generated_at'] | strftime('%Y-%m-%d') }}"
keywords: [Software, Requirements, SRS, amp-devcontainer]
lang: "en"
titlepage: true
Expand All @@ -12,14 +12,59 @@ titlepage-rule-color: "FFFFFF"
titlepage-rule-height: 2
toc: true
toc-own-page: true
header-includes:
- \AtEndDocument{\label{lastpage}}
{%- if sbdl['doc_control']['custom:is_release'] != 'true' %}
watermark: "DRAFT"
{%- endif %}
footer-right: "\\thepage \\hspace{1pt} of \\pageref*{lastpage}"
...

{% import "partials/text-utilities.j2" as utils %}

# Introduction

## Purpose

This document describes the software system requirements for amp-devcontainer.

## Scope

This document covers the requirements for the amp-devcontainer project: a set of devcontainers tailored towards modern, embedded, software development.

The following is in scope:

- Container image flavors for C++ and Rust development
- Tooling for compilation, debugging, static and dynamic analysis
- Compatibility with IDEs, container runtimes, and ci/cd systems
- Security properties of released container images
- Maintainability of the container images and their build system

The following is out of scope:

- Application-level software built using the containers
- Deployment of end-user products
- Requirements for third-party tools and dependencies included in the containers

## References

| Identifier | Title |
|------------|------------------------------------------------------------------------------------------------------------------------------------------|
| RFC 2119 | [Key words for use in RFCs to Indicate Requirement Levels](https://www.rfc-editor.org/rfc/rfc2119) |
| OCI | [Open Container Initiative Image Specification](https://github.com/opencontainers/image-spec/blob/main/spec.md) |
| SLSA | [Supply-chain Levels for Software Artifacts v1.0](https://slsa.dev/spec/v1.0/levels) |
| SemVer | [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html) |
| DevC | [Development Containers Specification](https://containers.dev/) |

## Document Control

{% include "partials/document-control.md.j2" with context %}

This document is generated from a formal model defined in [sbdl](https://sbdl.dev) and versioned alongside the source code in Git.
The authoritative source of change history is the [Git log](https://github.com/philips-software/amp-devcontainer/commits/) of the source material from which the model is built.

Changes to requirements are tracked at the individual requirement level through the version control system and are part of the project's standard change control process.

## Definitions of key words

The key words *MUST*, *MUST NOT*, *REQUIRED*, *SHALL*, *SHALL NOT*, *SHOULD*, *SHOULD NOT*, *RECOMMENDED*, *MAY*, and *OPTIONAL* in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119).
Expand All @@ -42,30 +87,22 @@ The containers may be used both for local development and continuous integration

# Requirements

{%- macro reencode(text) -%}
{{ text.encode('utf-8').decode('unicode_escape') }}
{%- endmacro -%}

{%- macro strip_gherkin_prefix(text) -%}
{{ text | replace('Rule: ', '') | replace('Feature: ', '') }}
{%- endmacro -%}

{%- for aspect_id, aspect in sbdl.items() %}
{%- if aspect.type == 'aspect' %}
{%- for id, item in sbdl.items() %}
{%- if item.type == 'aspect' and 'custom:title' in item %}

## {{ reencode(strip_gherkin_prefix(aspect['custom:title'])) }}
## {{ utils.reencode(utils.strip_gherkin_prefix(item['custom:title'])) }}

{{ reencode(aspect.description) }}
{{ utils.reencode(item.description) }}

{%- if 'requirement' in aspect %}
{%- for req_ref in aspect.requirement %}
{%- if 'requirement' in item %}
{%- for req_ref in item.requirement %}
{%- set req = sbdl[req_ref.identifier] %}

### {{ req_ref.identifier }}: {{ reencode(strip_gherkin_prefix(req['custom:title'])) }}
### {{ req_ref.identifier }}: {{ utils.reencode(utils.strip_gherkin_prefix(req['custom:title'])) }}

{{ reencode(req.description) }}
{{ utils.reencode(req.description) }}

{{ reencode(req.remark) }}
{{ utils.reencode(req.remark) }}

{%- endfor %}
{%- endif %}
Expand Down
Loading
Loading