diff --git a/.github/linters/.gherkin-lintrc b/.github/linters/.gherkin-lintrc index 5b9953db..61ada6d9 100644 --- a/.github/linters/.gherkin-lintrc +++ b/.github/linters/.gherkin-lintrc @@ -7,7 +7,7 @@ "no-duplicate-tags": "on", "no-empty-background": "on", "no-empty-file": "on", - "no-files-without-scenarios": "on", + "no-files-without-scenarios": "off", "no-multiple-empty-lines": "on", "no-partially-commented-tag-lines": "on", "no-scenario-outlines-without-examples": "on", diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index d6eebf7a..3462f92c 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -90,6 +90,16 @@ jobs: GH_TOKEN: ${{ github.token }} REPOSITORY_OWNER: ${{ github.repository_owner }} REPOSITORY_NAME: ${{ github.event.repository.name }} + - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + pattern: documents + - name: Upload documents to release + run: | + set -Eeuo pipefail + gh release upload "${REF_NAME}" ./*.pdf + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} - name: Update package details in release run: | set -Eeuo pipefail diff --git a/.github/workflows/wc-document-generation.yml b/.github/workflows/wc-document-generation.yml index de0252f5..95ec7e24 100644 --- a/.github/workflows/wc-document-generation.yml +++ b/.github/workflows/wc-document-generation.yml @@ -22,17 +22,16 @@ jobs: 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 + - name: Generate SRS 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 + python docs/support/gherkin-to-sbdl.py test/cpp/features/*.feature + sbdl -m template-fill --template docs/templates/software-requirements-specification.md.j2 output.sbdl > software-requirements-specification.md - uses: docker://pandoc/extra:3.7.0@sha256:a703d335fa237f8fc3303329d87e2555dca5187930da38bfa9010fa4e690933a with: - args: --template eisvogel --output requirements.pdf requirements.md + args: --template eisvogel --listings --number-sections --output software-requirements-specification.pdf software-requirements-specification.md - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: documents - path: requirements.pdf + path: "*.pdf" retention-days: 2 diff --git a/.gitignore b/.gitignore index ef4f50ce..e815c6c8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,6 @@ test-results/ .env *.profdata *.profraw +*.pyc **/playwright/.auth/user.json CMakeUserPresets.json diff --git a/.mega-linter.yml b/.mega-linter.yml index 1326467a..b2af1df8 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -8,7 +8,6 @@ ENABLE: - SPELL - YAML DISABLE_LINTERS: - - MARKDOWN_MARKDOWN_LINK_CHECK - REPOSITORY_DEVSKIM - REPOSITORY_DUSTILOCK - REPOSITORY_KICS @@ -18,6 +17,8 @@ DISABLE_LINTERS: SARIF_REPORTER: true PRINT_ALPACA: false SHOW_SKIPPED_LINTERS: false +SPELL_LYCHEE_FILE_EXTENSIONS: + [".feature", ".json", ".md", ".md.j2", ".txt", ".yaml", ".yml"] FILTER_REGEX_EXCLUDE: (CHANGELOG.md|package-lock.json) # tasks.json is wrongfully matched against another schema, # and schemas for .vscode/[tasks.json|launch.json] are built diff --git a/docs/support/gherkin-to-csv.py b/docs/support/gherkin-to-csv.py deleted file mode 100755 index 5144c632..00000000 --- a/docs/support/gherkin-to-csv.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/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/support/gherkin-to-sbdl.py b/docs/support/gherkin-to-sbdl.py new file mode 100755 index 00000000..8c6d505e --- /dev/null +++ b/docs/support/gherkin-to-sbdl.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import argparse +import os +import sys + +from gherkin_mapping_config import FEATURE_RULE_CONFIG, FEATURE_RULE_SCENARIO_CONFIG +from gherkin_sbdl_converter import GherkinConverter + +def main(): + configs = { + 'feature-rule': FEATURE_RULE_CONFIG, + 'feature-rule-scenario': FEATURE_RULE_SCENARIO_CONFIG + } + + parser = argparse.ArgumentParser(description='Configurable Gherkin to SBDL converter') + parser.add_argument('feature_files', nargs='+', help='Paths to feature files') + parser.add_argument('--output', '-o', default='output.sbdl', help='Output SBDL file') + parser.add_argument('--config', choices=configs.keys(), + default='feature-rule', help='Conversion configuration preset') + + args = parser.parse_args() + config = configs[args.config] + converter = GherkinConverter(config) + gherkin_elements = [] + + for feature_path in args.feature_files: + if os.path.isfile(feature_path): + print(f"Processing {feature_path}") + elements = converter.extract_from_feature_file(feature_path) + gherkin_elements.extend(elements) + else: + print(f"File not found: {feature_path}", file=sys.stderr) + + converter.write_sbdl_output(gherkin_elements, args.output) + print(f"Extracted {len(gherkin_elements)} elements to {args.output}") + +if __name__ == '__main__': + main() diff --git a/docs/support/gherkin_mapping_config.py b/docs/support/gherkin_mapping_config.py new file mode 100644 index 00000000..113e5e59 --- /dev/null +++ b/docs/support/gherkin_mapping_config.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 + +""" +Configuration-driven Gherkin to SBDL mapping with flexible hierarchy support. +""" + +from dataclasses import dataclass +from typing import Dict, List, Optional, Union +from enum import Enum + +class GherkinElementType(Enum): + """Supported Gherkin element types for conversion.""" + FEATURE = "feature" + RULE = "rule" + SCENARIO = "scenario" + SCENARIO_OUTLINE = "scenario_outline" + EXAMPLE = "example" + BACKGROUND = "background" + +class SBDLElementType(Enum): + """Supported SBDL element types for mapping.""" + REQUIREMENT = "requirement" + ASPECT = "aspect" + USECASE = "usecase" + DEFINITION = "definition" + TEST = "test" + +@dataclass +class HierarchyMapping: + """Configuration for mapping Gherkin elements to SBDL with document hierarchy.""" + gherkin_type: GherkinElementType + sbdl_type: SBDLElementType + +@dataclass +class ConversionConfig: + """Complete configuration for Gherkin to SBDL conversion.""" + hierarchy_mappings: List[HierarchyMapping] + + def get_mapping_for_type(self, gherkin_type: GherkinElementType) -> Optional[HierarchyMapping]: + """Get the hierarchy mapping for a specific Gherkin element type.""" + for mapping in self.hierarchy_mappings: + if mapping.gherkin_type == gherkin_type: + return mapping + return None + +# Predefined configurations for different use cases + +# Current Configuration (Feature -> Rule mapping) +FEATURE_RULE_CONFIG = ConversionConfig( + hierarchy_mappings=[ + HierarchyMapping( + gherkin_type=GherkinElementType.FEATURE, + sbdl_type=SBDLElementType.REQUIREMENT + ), + HierarchyMapping( + gherkin_type=GherkinElementType.RULE, + sbdl_type=SBDLElementType.REQUIREMENT + ) + ] +) + +# Extended Configuration (Feature -> Rule -> Scenario mapping) +FEATURE_RULE_SCENARIO_CONFIG = ConversionConfig( + hierarchy_mappings=[ + HierarchyMapping( + gherkin_type=GherkinElementType.FEATURE, + sbdl_type=SBDLElementType.ASPECT + ), + HierarchyMapping( + gherkin_type=GherkinElementType.RULE, + sbdl_type=SBDLElementType.REQUIREMENT + ), + HierarchyMapping( + gherkin_type=GherkinElementType.SCENARIO, + sbdl_type=SBDLElementType.TEST + ) + ] +) + +# Flat Configuration (All as requirements at same level) +FLAT_CONFIG = ConversionConfig( + hierarchy_mappings=[ + HierarchyMapping( + gherkin_type=GherkinElementType.FEATURE, + sbdl_type=SBDLElementType.REQUIREMENT + ), + HierarchyMapping( + gherkin_type=GherkinElementType.RULE, + sbdl_type=SBDLElementType.REQUIREMENT + ) + ] +) + +# Use Case Focused Configuration +USECASE_CONFIG = ConversionConfig( + hierarchy_mappings=[ + HierarchyMapping( + gherkin_type=GherkinElementType.FEATURE, + sbdl_type=SBDLElementType.USECASE + ), + HierarchyMapping( + gherkin_type=GherkinElementType.SCENARIO, + sbdl_type=SBDLElementType.USECASE + ) + ] +) diff --git a/docs/support/gherkin_sbdl_converter.py b/docs/support/gherkin_sbdl_converter.py new file mode 100644 index 00000000..888a8c6d --- /dev/null +++ b/docs/support/gherkin_sbdl_converter.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +"""Gherkin to SBDL converter with configurable hierarchy mappings.""" + +from dataclasses import dataclass +from typing import List, Dict, Optional +from textwrap import dedent +import sys + +from gherkin.parser import Parser +from gherkin.token_scanner import TokenScanner +import sbdl + +from gherkin_mapping_config import ( + ConversionConfig, + GherkinElementType, + SBDLElementType, +) + +@dataclass +class SBDLElement: + """A generalized SBDL element that can represent any type.""" + + identifier: str + element_type: SBDLElementType + description: str + parent: Optional[str] = None + metadata: Optional[Dict] = None + + def __post_init__(self): + self.identifier = self._make_sbdl_identifier(self.identifier) + self.description = dedent(self.description) + + if self.metadata is None: + self.metadata = {} + + def _make_sbdl_identifier(self, name: str) -> str: + return sbdl.SBDL_Parser.sanitize_identifier( + name.replace(" ", "_").replace("-", "_") + ).strip("_") + +class GherkinConverter: + """Converts Gherkin files to SBDL using configurable hierarchy mappings.""" + + def __init__(self, config: ConversionConfig): + self.config = config + # Strategy map for element extraction: key in parsed Gherkin dict -> handler. + self._strategies = { + 'feature': self._handle_feature, + 'rule': self._handle_rule, + 'scenario': self._handle_scenario, + 'scenarioOutline': self._handle_scenario_outline, + } + + def extract_from_feature_file(self, file_path: str) -> List[SBDLElement]: + """Extract all configured elements from a Gherkin feature file.""" + try: + with open(file_path, 'r', encoding='utf-8') as file: + content = file.read() + + parser = Parser() + feature_document = parser.parse(TokenScanner(content)) + + if 'feature' not in feature_document: + return [] + + return self._dispatch(feature_document, None) + except Exception as e: + print(f"Error parsing {file_path}: {e}", file=sys.stderr) + return [] + + def write_sbdl_output(self, elements: List[SBDLElement], output_file: str): + tokens = sbdl.SBDL_Parser.Tokens + attrs = sbdl.SBDL_Parser.Attributes + + with open(output_file, 'w', encoding='utf-8') as f: + f.write("#!sbdl\n") + + for element in elements: + escaped_desc = sbdl.SBDL_Parser.sanitize(element.description) + sbdl_type = element.element_type.value + f.write(f"{element.identifier} {tokens.declaration} {sbdl_type} ") + f.write(f"{tokens.declaration_group_delimeters[0]} ") + f.write(f"{attrs.description}{tokens.declaration_attribute_assign}") + f.write( + f"{tokens.declaration_attribute_delimeter}{escaped_desc}{tokens.declaration_attribute_delimeter} " + ) + + if element.parent: + f.write(f"{attrs.parent}{tokens.declaration_attribute_assign}{element.parent} ") + + f.write(f"{tokens.declaration_group_delimeters[1]}\n") + + def _extract_gherkin_element(self, element_data: Dict, element_type: GherkinElementType, parent_id: Optional[str]) -> Optional[SBDLElement]: + mapping = self.config.get_mapping_for_type(element_type) + + if not mapping: + return None + + name = element_data.get('name', '').strip() + + if not name: + return None + + return SBDLElement( + identifier=name, + element_type=mapping.sbdl_type, + description=element_data.get('description', ''), + parent=parent_id, + metadata={ + 'gherkin_type': element_type.value, + 'original_name': name, + 'line': element_data.get('location', {}).get('line'), + }, + ) + + def _dispatch(self, node: Dict, parent_id: Optional[str]) -> List[SBDLElement]: + for key, handler in self._strategies.items(): + if key in node: + return handler(node[key], parent_id) + return [] + + def _handle_feature(self, feature_data: Dict, parent_id: Optional[str]) -> List[SBDLElement]: + elements: List[SBDLElement] = [] + feature_element = self._extract_gherkin_element(feature_data, GherkinElementType.FEATURE, parent_id) + feature_id = None + if feature_element: + elements.append(feature_element) + feature_id = feature_element.identifier + print(f"Extracted Feature: {feature_element.identifier}") + for child in feature_data.get('children', []): + elements.extend(self._dispatch(child, feature_id)) + return elements + + def _handle_rule(self, rule_data: Dict, parent_id: Optional[str]) -> List[SBDLElement]: + elements: List[SBDLElement] = [] + rule_element = self._extract_gherkin_element(rule_data, GherkinElementType.RULE, parent_id) + if not rule_element: + return elements + elements.append(rule_element) + print(f" Extracted Rule: {rule_element.identifier}") + for rule_child in rule_data.get('children', []): + elements.extend(self._dispatch(rule_child, rule_element.identifier)) + return elements + + def _handle_scenario(self, scenario_data: Dict, parent_id: Optional[str]) -> List[SBDLElement]: + scenario_element = self._extract_gherkin_element(scenario_data, GherkinElementType.SCENARIO, parent_id) + if not scenario_element: + return [] + print(f" Extracted Scenario: {scenario_element.identifier}") + return [scenario_element] + + def _handle_scenario_outline(self, outline_data: Dict, parent_id: Optional[str]) -> List[SBDLElement]: + outline_element = self._extract_gherkin_element(outline_data, GherkinElementType.SCENARIO_OUTLINE, parent_id) + if not outline_element: + return [] + print(f" Extracted Scenario Outline: {outline_element.identifier}") + return [outline_element] diff --git a/docs/templates/requirements.template.md b/docs/templates/requirements.template.md deleted file mode 100644 index 3b941f2a..00000000 --- a/docs/templates/requirements.template.md +++ /dev/null @@ -1,45 +0,0 @@ -# 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/docs/templates/software-requirements-specification.md.j2 b/docs/templates/software-requirements-specification.md.j2 new file mode 100644 index 00000000..dda4cbeb --- /dev/null +++ b/docs/templates/software-requirements-specification.md.j2 @@ -0,0 +1,71 @@ +--- +title: "Software requirements specification for amp-devcontainer" +author: ["@rjaegers"] +colorlinks: true +date: "October-2025" +keywords: [Software, Requirements, SRS, 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 +... + +# 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 + +| Term | 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 Delivery (cd) | Extends ci by automating the release of validated code, extending test automation to ensure that code is production-ready | +| Continuous Integration (ci) | The practice of continuously integrating developers' work to a shared code-base; including automation for build and test | +| 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' and 'parent' not in requirement %} + +## {{ reencode(sbdl_id_to_header(req_id)) }} + +{{ reencode(requirement.description) }} + +{%- if 'child' in requirement %} +{%- for child in requirement.child %} +{%- set child_req = sbdl[child.identifier] %} + +### {{ reencode(sbdl_id_to_header(child.identifier)) }} + +{{ reencode(child_req.description) }} + +{%- endfor %} +{%- endif %} +{%- endif %} +{%- endfor %} diff --git a/test/cpp/features/compatibility.feature b/test/cpp/features/compatibility.feature new file mode 100644 index 00000000..65cdebd8 --- /dev/null +++ b/test/cpp/features/compatibility.feature @@ -0,0 +1,32 @@ +Feature: Compatibility + + As a software craftsperson, + to ensure that my development environment works well with a variety of tools and systems, + I want my development environment to be compatible with commonly used tools and systems. + + Rule: Open Container Initiative (OCI) Image Specification + amp-devcontainer images *SHALL* be compatible with the [OCI image specification](https://github.com/opencontainers/image-spec/blob/main/spec.md) + + To guarantee compatibility with container runtimes and container- and image tooling; amp-devcontainer should be compatible with the OCI image specification. + + Rule: Host architecture + amp-devcontainer *SHALL* be compatible with both the x86-64 (AMD64) *and* AArch64 (ARM64) host architectures. + + Supporting both x86-64 and AArch64 has several advantages: + + - Increasing useability on a wide range of host machines, from PC hardware using the x86-64 architecture to Apple Silicon using the AArch64 architecture + - Unlocking the power efficiency of the AArch64 architecture, potentially reducing energy consumption and cost for metered ci-systems + + Rule: Integrated Development Environment (IDE) + amp-devcontainer *SHOULD* be compatible with [VS Code](https://code.visualstudio.com/) *and* [GitHub Codespaces](https://github.com/features/codespaces). + + It should be possible to use amp-devcontainer and all of its features in both VS Code and GitHub Codespaces with minimal effort. + Where minimal effort means: with the least amount of additional set-up, user intervention or configuration for all functionality that is provided by amp-devcontainer. + Features and functions should work "out-of-the-box" without being overly opinionated. + + Rule: GitHub Actions + amp-devcontainer *SHOULD* support seamless integration with [GitHub Actions](https://github.com/features/actions) without additional configuration. + + Seamless integration with GitHub Actions allows users to easily incorporate amp-devcontainer into their ci/cd workflows. + This integration helps automate the build, test, and deployment processes, improving efficiency and reducing manual errors. + By minimizing the need for additional configuration, users can quickly set up and start using amp-devcontainer in their GitHub Actions workflows, enhancing their overall development experience. diff --git a/test/cpp/features/compilation.feature b/test/cpp/features/compilation.feature index d2bce27e..de0e3e3c 100644 --- a/test/cpp/features/compilation.feature +++ b/test/cpp/features/compilation.feature @@ -1,8 +1,8 @@ Feature: Compilation - As a software developer - To generate a working product - Source code needs to be compiled into working software + As a software developer, + to generate a working product, when using compiled languages, + source code needs to be compiled into working software. 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. @@ -22,14 +22,14 @@ Feature: Compilation 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. + amp-devcontainer *SHOULD* 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. + amp-devcontainer *SHOULD* 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. @@ -37,7 +37,7 @@ Feature: Compilation - 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. + amp-devcontainer *MAY* 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: diff --git a/test/cpp/features/debugging.feature b/test/cpp/features/debugging.feature new file mode 100644 index 00000000..58a64287 --- /dev/null +++ b/test/cpp/features/debugging.feature @@ -0,0 +1,21 @@ +Feature: Debugging + + As a software craftsperson, + to efficiently identify and resolve issues in my code, + I want to be able to debug my source code within the development environment. + + Rule: Debugging support + amp-devcontainer *SHALL* provide debugging support for the primary programming language(s) used within the container. + + Providing debugging support within the development environment enhances the developer experience and productivity. + It allows developers to efficiently identify and resolve issues in their code by setting breakpoints, inspecting variables, and stepping through code execution. + This capability is essential for diagnosing complex problems, understanding code flow, and ensuring the correctness of software. + By having integrated debugging tools, developers can streamline their workflow and reduce the time spent on troubleshooting and fixing bugs. + + Rule: Upload firmware to micro-controller + amp-devcontainer *MAY* provide tools to upload compiled firmware to a connected micro-controller. + + Providing tools to upload compiled firmware to a connected micro-controller enhances the development workflow for embedded systems. + It allows developers to quickly and easily transfer their compiled code to the target hardware for testing and debugging. + This capability is essential for validating the functionality of the firmware on the actual device, ensuring that it behaves as expected in real-world scenarios. + By having integrated tools for firmware upload, developers can streamline their workflow, reduce manual steps, and improve overall productivity when working with micro-controllers. diff --git a/test/cpp/features/maintainability.feature b/test/cpp/features/maintainability.feature new file mode 100644 index 00000000..730ce325 --- /dev/null +++ b/test/cpp/features/maintainability.feature @@ -0,0 +1,38 @@ +Feature: Maintainability + + As a software craftsperson, + to ensure that I have access to a stable and reliable development environment, + I want my development environment to be maintainable over time. + + Rule: Tool and dependency updates + amp-devcontainer *SHOULD* contain up-to-date tools and dependencies. + + Keeping tools and dependencies up-to-date helps ensure that the development environment remains secure, stable, and compatible with the latest technologies. + It also helps prevent issues related to deprecated or unsupported software versions, reducing maintenance overhead and improving overall developer productivity. + Regular updates can also introduce new features and improvements that enhance the development experience. + + Rule: Automatic updates + amp-devcontainer *SHOULD* provide support for automatic updates when consumed as a dependency. + + Providing support for automatic updates when amp-devcontainer is consumed as a dependency helps ensure that users always have access to the latest features, bug fixes, and security patches. + This reduces the maintenance burden on users, as they do not need to manually track and apply updates. + Automatic updates can also help ensure compatibility with other dependencies and tools, improving the overall stability and reliability of the development environment. + + Rule: Architectural decisions + amp-devcontainer *SHOULD* document its architectural decisions. + + Documenting architectural decisions helps provide context and rationale for the design choices made in the development environment. + This information can be valuable for future maintainers, as it helps them understand the reasoning behind certain implementations and can guide them in making informed decisions when modifying or extending the environment. + Clear documentation of architectural decisions can also facilitate collaboration among team members and improve overall maintainability. + + Rule: Container image size + amp-devcontainer *SHOULD* aim to keep its container image size as small as possible without compromising functionality. + + Keeping the container image size small helps improve performance, reduce resource consumption, and minimize the time required to download and deploy the development environment. + A smaller image size can also help reduce storage costs and improve scalability, particularly in cloud-based environments. + By optimizing the container image size, amp-devcontainer can provide a more efficient and user-friendly experience for developers. + + + - The container image size is monitored. + - The compressed image size is kept below 1 GiB. + diff --git a/test/cpp/features/security.feature b/test/cpp/features/security.feature new file mode 100644 index 00000000..061a0053 --- /dev/null +++ b/test/cpp/features/security.feature @@ -0,0 +1,26 @@ +Feature: Security + + As a security engineer and security conscious developer, + to have control over the security posture of my development environment, + I want to have controls in place to identify and mitigate supply-chain vulnerabilities. + + Rule: Build provenance + amp-devcontainer *SHALL* include build provenance as specified in [SLSA v1.0 Build L3](https://slsa.dev/spec/v1.0/levels). + + Including provenance gives confidence that the container images haven't been tampered with and can be securely traced back to its source code. + The primary purpose is to enable [verification](https://slsa.dev/spec/v1.0/verifying-artifacts) that the container image was built as expected. + Consumers have some way of knowing what the expected provenance should look like for a given container image and then compare each container image's actual provenance to those expectations. + Doing so prevents several classes of [supply chain threats](https://slsa.dev/spec/v1.0/threats). + + Rule: Signing + amp-devcontainer *SHALL* cryptographically sign its released container images. + + Cryptographically signing released container images provides integrity and authenticity guarantees. + It enables consumers to verify that the container image hasn't been tampered with and that it indeed originates from the expected publisher. + This helps mitigate several classes of [supply chain threats](https://slsa.dev/spec/v1.0/threats). + + Rule: Software Bill of Materials (SBOM) + amp-devcontainer *SHOULD* provide a Software Bill of Materials (SBOM) for its released container images. + + Providing a Software Bill of Materials (SBOM) enables consumers to identify and manage security risks associated with the software components included in the container images. + It helps identify known vulnerabilities, license compliance issues, and potential supply chain risks. diff --git a/test/cpp/features/static-dynamic-analysis.feature b/test/cpp/features/static-dynamic-analysis.feature index 8feb2195..3254790a 100644 --- a/test/cpp/features/static-dynamic-analysis.feature +++ b/test/cpp/features/static-dynamic-analysis.feature @@ -1,13 +1,47 @@ -Feature: Analyze source code using static and dynamic analysis +Feature: Static and dynamic analysis - As a software craftsperson - To maintain consistent, high-quality and bug-free code - I want my source code to be statically and dynamically analyzed + As a software craftsperson, + to maintain consistent, high-quality and bug-free code, + I want my source code to be statically and dynamically analyzed. - @fixme - Scenario: Format source code according to a formatting style + Rule: Code formatting + amp-devcontainer *MAY* provide code formatting tools for the primary programming language(s) used within the container. + + Providing code formatting tools helps maintain a consistent coding style across the codebase, improving readability and reducing friction during code reviews. + It also helps catch potential issues early by enforcing coding standards and best practices. + By integrating code formatting tools into the development environment, developers can easily format their code according to predefined rules, ensuring that the code adheres to the project's style guidelines. + @flavor:cpp @fixme + Scenario: Format source code according to a formatting style Given the file "clang-tools/unformatted.cpp" is opened in the editor When the active document is formatted And the active document is saved Then the contents of "clang-tools/unformatted.cpp" should match the contents of "clang-tools/formatted.cpp" + + Rule: Static analysis + amp-devcontainer *MAY* provide static analysis tools for the primary programming language(s) used within the container. + + Providing static analysis tools helps identify potential issues in the code before it is executed, improving code quality and reducing the likelihood of runtime errors. + These tools can analyze the code for common pitfalls, coding standards violations, and potential bugs, providing developers with valuable feedback early in the development process. + By integrating static analysis tools into the development environment, developers can catch issues before they become more significant problems, streamlining the development workflow and improving overall code quality. + + Rule: Coverage analysis + amp-devcontainer *SHOULD* provide code coverage analysis tools for the primary programming language(s) used within the container. + + Providing code coverage analysis tools helps assess the effectiveness of the existing test suite by measuring how much of the code is exercised by the tests. + This information can help identify gaps in test coverage, ensuring that critical parts of the code are adequately tested. + By integrating code coverage analysis tools into the development environment, developers can improve their test suites, leading to higher code quality and increased confidence in the software's correctness. + + Rule: Mutation testing + amp-devcontainer *MAY* provide mutation testing tools for the primary programming language(s) used within the container. + + Providing mutation testing tools helps assess the effectiveness of the existing test suite by introducing small changes (mutations) to the code and checking if the tests can detect these changes. + This process helps identify gaps in test coverage and ensures that the tests are robust enough to catch potential issues in the code. + By integrating mutation testing tools into the development environment, developers can improve their test suites, leading to higher code quality and increased confidence in the software's correctness. + + Rule: Fuzz testing + amp-devcontainer *MAY* provide fuzz testing tools for the primary programming language(s) used within the container. + + Providing fuzz testing tools helps identify potential security vulnerabilities and robustness issues in the code by automatically generating and executing a large number of random inputs. + This process can uncover edge cases and unexpected behaviors that may not be covered by traditional testing methods. + By integrating fuzz testing tools into the development environment, developers can improve the overall security and reliability of their software, reducing the risk of vulnerabilities being exploited in production.