diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index fe877611..6d482dc7 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -96,3 +96,8 @@ jobs: - uses: EnricoMi/publish-unit-test-result-action@3a74b2957438d0b6e2e61d67b05318aa25c9e6c6 # v2.20.0 with: files: test-report-*.xml + + generate-documents: + uses: ./.github/workflows/wc-document-generation.yml + permissions: + contents: read diff --git a/.github/workflows/wc-document-generation.yml b/.github/workflows/wc-document-generation.yml new file mode 100644 index 00000000..4241b822 --- /dev/null +++ b/.github/workflows/wc-document-generation.yml @@ -0,0 +1,38 @@ +--- +name: Document Generation + +on: + workflow_call: + +permissions: + contents: read + +jobs: + generate-documents: + runs-on: ubuntu-latest + steps: + - uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - 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==35.1.0 sbdl==1.16.4 + - name: Generate requirements document + run: | + set -Eeuo pipefail + python docs/support/gherkin-to-csv.py test/cpp/features/*.feature + python -m csv-to-sbdl --identifier 0 --description 1 --skipheader rules.csv + sbdl -m template-fill --template docs/templates/requirements.template.md output.sbdl > requirements.md + - uses: docker://pandoc/extra:3.7.0@sha256:a703d335fa237f8fc3303329d87e2555dca5187930da38bfa9010fa4e690933a + with: + args: --template eisvogel --output requirements.pdf requirements.md + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: documents + path: requirements.pdf + retention-days: 2 diff --git a/docs/support/gherkin-to-csv.py b/docs/support/gherkin-to-csv.py new file mode 100755 index 00000000..5144c632 --- /dev/null +++ b/docs/support/gherkin-to-csv.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 + +import argparse +import csv +import os +import sys +from dataclasses import dataclass, asdict, fields +from typing import List +from gherkin.parser import Parser +from gherkin.token_scanner import TokenScanner + +@dataclass +class Rule: + """A class representing a rule extracted from a Gherkin feature file.""" + identifier: str + description: str + + @classmethod + def field_names(cls) -> List[str]: + return [f.name for f in fields(cls)] + +def extract_rules_from_feature(file_path): + """Parse a Gherkin feature file and extract the rules.""" + try: + with open(file_path, 'r', encoding='utf-8') as file: + content = file.read() + + parser = Parser() + feature_document = parser.parse(TokenScanner(content)) + rules = [] + + if 'feature' in feature_document: + feature = feature_document['feature'] + + if 'children' in feature: + for child in feature['children']: + if 'rule' in child: + rule = child['rule'] + rule_id = rule.get('name', '').strip() + description = rule.get('description', '').strip() + rules.append(Rule( + identifier=rule_id, + description=description + )) + + return rules + except Exception as e: + print(f"Error parsing {file_path}: {e}", file=sys.stderr) + return [] + +def main(): + parser = argparse.ArgumentParser(description='Extract rules from Gherkin feature files and save to CSV') + parser.add_argument('feature_files', nargs='+', help='Paths to feature files') + parser.add_argument('--output', '-o', default='rules.csv', help='Output CSV file path') + + args = parser.parse_args() + all_rules = [] + + for feature_path in args.feature_files: + if os.path.isfile(feature_path): + print(f"Processing {feature_path}") + rules = extract_rules_from_feature(feature_path) + all_rules.extend(rules) + else: + print(f"File not found: {feature_path}", file=sys.stderr) + + with open(args.output, 'w', newline='', encoding='utf-8') as csvfile: + writer = csv.DictWriter(csvfile, fieldnames=Rule.field_names()) + writer.writeheader() + writer.writerows([asdict(rule) for rule in all_rules]) + + print(f"Extracted {len(all_rules)} rules to {args.output}") + +if __name__ == '__main__': + main() diff --git a/docs/templates/requirements.template.md b/docs/templates/requirements.template.md new file mode 100644 index 00000000..3b941f2a --- /dev/null +++ b/docs/templates/requirements.template.md @@ -0,0 +1,45 @@ +# amp-devcontainer requirement specification + +## Introduction + +### Purpose + +This document describes the software system requirements for amp-devcontainer. + +### 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). + +### Abstract + +amp-devcontainer is a set of [devcontainers](https://containers.dev/) tailored towards modern, embedded, software development. + +The containers may be used both for local development and continuous integration (ci). + +### Terminology and Abbreviations + +| Terminology and Abbreviations | Description/Definition | +|-------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ARM | A family of RISC architectures for computer processors and micro controllers, formerly an acronym for Advanced RISC Machines and originally Acorn RISC Machine | +| Continuous Integration (ci) | The practice of continuously merging developers work to a shared code-base; ideally including automation for build, test and deployment | +| ELF | Executable and Linkable Format, formerly named Extensible Linking Format | +| RISC | Reduced Instruction Set Computer | + +## Requirements + +{%- macro reencode(text) -%} +{{ text.encode('utf-8').decode('unicode_escape') }} +{%- endmacro -%} + +{%- macro sbdl_id_to_header(text) -%} +{{ text.replace('_', ' ') }} +{%- endmacro -%} + +{% for req_id, requirement in sbdl.items() %} +{% if requirement.type == 'requirement' %} +### {{ reencode(sbdl_id_to_header(req_id)) }} + +{{ reencode(requirement.description) }} + +{% endif %} +{% endfor %} diff --git a/test/cpp/features/compilation.feature b/test/cpp/features/compilation.feature index b2128ef8..d2bce27e 100644 --- a/test/cpp/features/compilation.feature +++ b/test/cpp/features/compilation.feature @@ -1,19 +1,45 @@ -Feature: Compile source code into working software +Feature: Compilation - As a developer - To generate working software - Source code needs to be compiled successfully + As a software developer + To generate a working product + Source code needs to be compiled into working software - Scenario: Compile valid source code into working software targeting the host architecture + Rule: Compile for container host architecture and operating system + amp-devcontainer *SHALL* be able to compile valid source code into a working executable targeting the container host architecture and operating system. - Compiling valid source code into working software, able to run on the host architecture, + Compiling valid source code into working software, able to run on the container host architecture and operating system, can be necessary in several scenarios; for example when: - - the host is the deployment target - - running tests on the host - - building plug-ins, extensions, code generators, or other additional tools that need to run on the host + - the container host is the deployment target + - running tests on the container host + - building plug-ins, extensions, code generators, or other additional tools that need to run on the container host - Given build configuration "gcc" is selected - And build preset "gcc" is selected - When the selected target is built - Then the output should contain "Build finished with exit code 0" + @flavor:cpp + Scenario: Compile valid source code into working software targeting the container host architecture + Given build configuration "gcc" is selected + And build preset "gcc" is selected + When the selected target is built + Then the output should contain "Build finished with exit code 0" + + Rule: Compile for ARM Cortex target architecture + amp-devcontainer *SHALL* be able to compile valid source-code into a working ELF executable targeting the ARM Cortex architecture. + + Compiling valid source-code into working ELF executables, able to run on the ARM Cortex architecture, + is a primary function for amp-devcontainer. It enables building firmware for micro-controllers based + on the ARM Cortex architecture. + + Rule: Compile for Microsoft® Windows operating system + amp-devcontainer *SHALL* be able to compile valid source-code into a working executable targeting the Microsoft® Windows operating system. + + Compiling valid source-code into working executables, able to run on the Microsoft® Windows operating system, can be necessary in several scenarios e.g. + + - Cross platform code is written and needs to be compiled and deployed + - Executables needs to be deployed outside of container context to a host running the Microsoft® Windows operating system + + Rule: Compilation cache + amp-devcontainer *SHOULD* be able to cache the results of a compilation to speed-up subsequent compilations. + + Maintaining a compilation cache can be useful in both local and ci development scenarios. A compilation cache can provide benefits like: + + - Reduce developer waiting time and context switches, [maintaining flow-state](https://azure.microsoft.com/en-us/blog/quantifying-the-impact-of-developer-experience/) + - Reduce CPU usage at the cost of more storage usage, potentially reducing energy consumption and cost for metered ci-systems