diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 79287ed..b1f0c77 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -63,4 +63,4 @@ ] } } -} \ No newline at end of file +} diff --git a/.cspell.yaml b/.cspell.yaml index d39e9dd..1145ebd 100644 --- a/.cspell.yaml +++ b/.cspell.yaml @@ -5,11 +5,11 @@ # - Maintain professional documentation and code quality # - Catch spelling errors before publication # - Support consistent technical terminology usage -# - Misspelled words should be fixed in the source -# - NEVER add a misspelled word to the 'words' list +# - NEVER add a misspelled word - fix the typo in the source instead +# - NEVER add a British-English spelling - correct to US-English in the source # - PROPOSE only genuine technical terms/names as needed -version: "0.2" +version: '0.2' language: en # Project-specific technical terms and tool names @@ -17,37 +17,43 @@ words: - buildmark - compilability - Dema + - entrantly - fileassert + - hotspot + - hotspots - Hostx - misparse - msbuild - msvc - opencover - - pandoc + - Pandoc + - postconditions - reqstream - reviewmark + - sarif - sarifmark - SARIF + - slnx + - sonar - sonarmark - versionmark - VSINSTALLDIR - - weasyprint - Weasy + - Weasyprint - xunit - Xunit - yamlfix # Exclude common build artifacts, dependencies, and vendored third-party code ignorePaths: - - "**/.git/**" - - "**/node_modules/**" - - "**/.venv/**" - - "**/thirdparty/**" - - "**/third-party/**" - - "**/3rd-party/**" - - "**/generated/**" - - "**/AGENT_REPORT_*.md" - - "**/.agent-logs/**" - - "**/bin/**" - - "**/obj/**" + - '**/.git/**' + - '**/node_modules/**' + - '**/.venv/**' + - '**/thirdparty/**' + - '**/third-party/**' + - '**/3rd-party/**' + - '**/.agent-logs/**' + - '**/bin/**' + - '**/obj/**' + - '**/generated/**' - package-lock.json diff --git a/.editorconfig b/.editorconfig index 204f58d..7c68025 100644 --- a/.editorconfig +++ b/.editorconfig @@ -36,8 +36,6 @@ indent_size = 4 csharp_prefer_braces = true:warning csharp_prefer_simple_using_statement = true:suggestion csharp_style_namespace_declarations = file_scoped:warning -csharp_style_prefer_method_group_conversion = true:silent -csharp_style_prefer_top_level_statements = true:silent csharp_style_expression_bodied_methods = false:silent csharp_style_expression_bodied_constructors = false:silent csharp_style_expression_bodied_operators = false:silent @@ -63,33 +61,22 @@ dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case # Symbol specifications dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = # Naming styles dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.capitalization = pascal_case -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case # Organize usings dotnet_sort_system_directives_first = true dotnet_separate_import_directive_groups = false -# Code quality - set to suggestion to not break existing code -dotnet_code_quality_unused_parameters = all:suggestion - # Nullable reference types csharp_nullable_reference_types = enable diff --git a/.fileassert.yaml b/.fileassert.yaml index e81cfbd..a66e4b2 100644 --- a/.fileassert.yaml +++ b/.fileassert.yaml @@ -10,300 +10,282 @@ tests: # --- BUILD NOTES --- - - name: Pandoc_BuildNotesHtml - description: "Build Notes HTML was generated by Pandoc" + description: Build Notes HTML was generated by Pandoc tags: [build-notes] files: - - pattern: "docs/build_notes/generated/build_notes.html" + - pattern: docs/build_notes/generated/build_notes.html count: 1 html: - - query: "//head/title" + - query: //head/title count: 1 text: - - contains: "Build Notes" - + - contains: Build Notes - name: WeasyPrint_BuildNotesPdf - description: "Build Notes PDF was generated by WeasyPrint" + description: Build Notes PDF was generated by WeasyPrint tags: [build-notes] files: - - pattern: "docs/generated/VersionMark Build Notes.pdf" + - pattern: docs/generated/VersionMark Build Notes.pdf count: 1 pdf: metadata: - - field: "Title" - contains: "VersionMark" - - field: "Author" - contains: "DEMA Consulting" - - field: "Subject" - contains: "Build notes" + - field: Title + contains: VersionMark + - field: Author + contains: DEMA Consulting + - field: Subject + contains: Build Notes pages: min: 1 text: - - contains: "Build Notes" + - contains: Build Notes # --- CODE QUALITY --- - - name: Pandoc_CodeQualityHtml - description: "Code Quality HTML was generated by Pandoc" + description: Code Quality HTML was generated by Pandoc tags: [code-quality] files: - - pattern: "docs/code_quality/generated/quality.html" + - pattern: docs/code_quality/generated/quality.html count: 1 html: - - query: "//head/title" + - query: //head/title count: 1 text: - - contains: "CodeQL" - + - contains: CodeQL - name: WeasyPrint_CodeQualityPdf - description: "Code Quality PDF was generated by WeasyPrint" + description: Code Quality PDF was generated by WeasyPrint tags: [code-quality] files: - - pattern: "docs/generated/VersionMark Code Quality.pdf" + - pattern: docs/generated/VersionMark Code Quality.pdf count: 1 pdf: metadata: - - field: "Title" - contains: "Code Quality" - - field: "Author" - contains: "DEMA Consulting" - - field: "Subject" - contains: "Code Quality" + - field: Title + contains: Code Quality + - field: Author + contains: DEMA Consulting + - field: Subject + contains: Code Quality pages: min: 1 text: - - contains: "CodeQL" + - contains: CodeQL # --- CODE REVIEW PLAN --- - - name: Pandoc_ReviewPlanHtml - description: "Code Review Plan HTML was generated by Pandoc" + description: Code Review Plan HTML was generated by Pandoc tags: [code-review] files: - - pattern: "docs/code_review_plan/generated/plan.html" + - pattern: docs/code_review_plan/generated/plan.html count: 1 html: - - query: "//head/title" + - query: //head/title count: 1 text: - - contains: "Review Plan" - + - contains: Review Plan - name: WeasyPrint_ReviewPlanPdf - description: "Code Review Plan PDF was generated by WeasyPrint" + description: Code Review Plan PDF was generated by WeasyPrint tags: [code-review] files: - - pattern: "docs/generated/VersionMark Review Plan.pdf" + - pattern: docs/generated/VersionMark Review Plan.pdf count: 1 pdf: metadata: - - field: "Title" - contains: "Review Plan" - - field: "Author" - contains: "DEMA Consulting" - - field: "Subject" - contains: "Review Plan" + - field: Title + contains: Review Plan + - field: Author + contains: DEMA Consulting + - field: Subject + contains: Review Plan pages: min: 1 text: - - contains: "Review Plan" + - contains: Review Plan # --- CODE REVIEW REPORT --- - - name: Pandoc_ReviewReportHtml - description: "Code Review Report HTML was generated by Pandoc" + description: Code Review Report HTML was generated by Pandoc tags: [code-review] files: - - pattern: "docs/code_review_report/generated/report.html" + - pattern: docs/code_review_report/generated/report.html count: 1 html: - - query: "//head/title" + - query: //head/title count: 1 text: - - contains: "Review Report" - + - contains: Review Report - name: WeasyPrint_ReviewReportPdf - description: "Code Review Report PDF was generated by WeasyPrint" + description: Code Review Report PDF was generated by WeasyPrint tags: [code-review] files: - - pattern: "docs/generated/VersionMark Review Report.pdf" + - pattern: docs/generated/VersionMark Review Report.pdf count: 1 pdf: metadata: - - field: "Title" - contains: "Review Report" - - field: "Author" - contains: "DEMA Consulting" - - field: "Subject" - contains: "Review Report" + - field: Title + contains: Review Report + - field: Author + contains: DEMA Consulting + - field: Subject + contains: Review Report pages: min: 1 text: - - contains: "Review Report" + - contains: Review Report # --- DESIGN DOCUMENT --- - - name: Pandoc_DesignHtml - description: "Design HTML was generated by Pandoc" + description: Design HTML was generated by Pandoc tags: [design] files: - - pattern: "docs/design/generated/design.html" + - pattern: docs/design/generated/design.html count: 1 html: - - query: "//head/title" + - query: //head/title count: 1 text: - - contains: "Design" - + - contains: Design - name: WeasyPrint_DesignPdf - description: "Design PDF was generated by WeasyPrint" + description: Design PDF was generated by WeasyPrint tags: [design] files: - - pattern: "docs/generated/VersionMark Software Design.pdf" + - pattern: docs/generated/VersionMark Software Design.pdf count: 1 pdf: metadata: - - field: "Title" - contains: "Design" - - field: "Author" - contains: "DEMA Consulting" - - field: "Subject" - contains: "design document" + - field: Title + contains: Design + - field: Author + contains: DEMA Consulting + - field: Subject + contains: Design pages: min: 3 text: - - contains: "Design" + - contains: Design # --- VERIFICATION --- - - name: Pandoc_VerificationHtml - description: "Verification HTML was generated by Pandoc" + description: Verification HTML was generated by Pandoc tags: [verification] files: - - pattern: "docs/verification/generated/verification.html" + - pattern: docs/verification/generated/verification.html count: 1 html: - - query: "//head/title" + - query: //head/title count: 1 text: - - contains: "Verification" - + - contains: Verification - name: WeasyPrint_VerificationPdf - description: "Verification PDF was generated by WeasyPrint" + description: Verification PDF was generated by WeasyPrint tags: [verification] files: - - pattern: "docs/generated/VersionMark Software Verification Design.pdf" + - pattern: docs/generated/VersionMark Software Verification Design.pdf count: 1 pdf: metadata: - - field: "Title" - contains: "Verification" - - field: "Author" - contains: "DEMA Consulting" - - field: "Subject" - contains: "Verification design document" + - field: Title + contains: Verification + - field: Author + contains: DEMA Consulting + - field: Subject + contains: Verification pages: min: 3 text: - - contains: "Verification" + - contains: Verification # --- USER GUIDE --- - - name: Pandoc_UserGuideHtml - description: "User Guide HTML was generated by Pandoc" + description: User Guide HTML was generated by Pandoc tags: [user-guide] files: - - pattern: "docs/user_guide/generated/user_guide.html" + - pattern: docs/user_guide/generated/user_guide.html count: 1 html: - - query: "//head/title" + - query: //head/title count: 1 text: - - contains: "User Guide" - + - contains: User Guide - name: WeasyPrint_UserGuidePdf - description: "User Guide PDF was generated by WeasyPrint" + description: User Guide PDF was generated by WeasyPrint tags: [user-guide] files: - - pattern: "docs/generated/VersionMark User Guide.pdf" + - pattern: docs/generated/VersionMark User Guide.pdf count: 1 pdf: metadata: - - field: "Title" - contains: "User Guide" - - field: "Author" - contains: "DEMA Consulting" - - field: "Subject" - contains: "User guide" + - field: Title + contains: User Guide + - field: Author + contains: DEMA Consulting + - field: Subject + contains: User Guide pages: min: 3 text: - - contains: "User Guide" + - contains: User Guide # --- REQUIREMENTS DOCUMENT --- # Note: these tests run after ReqStream and do not contribute to OTS requirements evidence. - - name: Pandoc_RequirementsHtml - description: "Requirements HTML was generated by Pandoc" + description: Requirements HTML was generated by Pandoc tags: [requirements] files: - - pattern: "docs/requirements_doc/generated/requirements.html" + - pattern: docs/requirements_doc/generated/requirements.html count: 1 html: - - query: "//head/title" + - query: //head/title count: 1 text: - - contains: "Requirements" - + - contains: Requirements - name: WeasyPrint_RequirementsPdf - description: "Requirements PDF was generated by WeasyPrint" + description: Requirements PDF was generated by WeasyPrint tags: [requirements] files: - - pattern: "docs/generated/VersionMark Requirements.pdf" + - pattern: docs/generated/VersionMark Requirements.pdf count: 1 pdf: metadata: - - field: "Title" - contains: "Requirements" - - field: "Author" - contains: "DEMA Consulting" - - field: "Subject" - contains: "Requirements" + - field: Title + contains: Requirements + - field: Author + contains: DEMA Consulting + - field: Subject + contains: Requirements pages: min: 1 text: - - contains: "Requirements" + - contains: Requirements # --- TRACE MATRIX --- # Note: these tests run after ReqStream and do not contribute to OTS requirements evidence. - - name: Pandoc_TraceMatrixHtml - description: "Trace Matrix HTML was generated by Pandoc" + description: Trace Matrix HTML was generated by Pandoc tags: [requirements] files: - - pattern: "docs/requirements_report/generated/trace_matrix.html" + - pattern: docs/requirements_report/generated/trace_matrix.html count: 1 html: - - query: "//head/title" + - query: //head/title count: 1 text: - - contains: "Trace Matrix" - + - contains: Trace Matrix - name: WeasyPrint_TraceMatrixPdf - description: "Trace Matrix PDF was generated by WeasyPrint" + description: Trace Matrix PDF was generated by WeasyPrint tags: [requirements] files: - - pattern: "docs/generated/VersionMark Trace Matrix.pdf" + - pattern: docs/generated/VersionMark Trace Matrix.pdf count: 1 pdf: metadata: - - field: "Title" - contains: "Trace Matrix" - - field: "Author" - contains: "DEMA Consulting" - - field: "Subject" - contains: "Traceability" + - field: Title + contains: Trace Matrix + - field: Author + contains: DEMA Consulting + - field: Subject + contains: Trace Matrix pages: min: 1 text: - - contains: "Trace Matrix" + - contains: Trace Matrix diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 8a4c9f7..ae0cc9a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -58,7 +58,8 @@ body: id: code-sample attributes: label: Code Sample - description: If applicable, provide a minimal code sample that reproduces the issue + description: If applicable, provide a minimal code sample that reproduces the + issue placeholder: | ```csharp // Your code here @@ -98,7 +99,8 @@ body: id: additional-context attributes: label: Additional Context - description: Add any other context about the problem here, such as error messages, stack traces, or screenshots + description: Add any other context about the problem here, such as error messages, + stack traces, or screenshots placeholder: Any additional information... - type: checkboxes diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 8fb81d5..95d0363 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -33,7 +33,8 @@ body: attributes: label: Alternatives Considered description: Describe any alternative solutions or features you've considered - placeholder: A clear and concise description of any alternative solutions or features + placeholder: A clear and concise description of any alternative solutions or + features - type: textarea id: examples @@ -85,7 +86,8 @@ body: id: additional-context attributes: label: Additional Context - description: Add any other context, screenshots, or links about the feature request here + description: Add any other context, screenshots, or links about the feature + request here placeholder: Any additional information... - type: checkboxes diff --git a/.github/agents/developer.agent.md b/.github/agents/developer.agent.md index a95c562..5f208eb 100644 --- a/.github/agents/developer.agent.md +++ b/.github/agents/developer.agent.md @@ -14,6 +14,10 @@ Perform software development tasks by determining and applying appropriate stand 2. **Read relevant standards** using the selection matrix in AGENTS.md 3. **Pre-flight verification** before making any changes: - List files that will be created, modified, or deleted + - For each file to be **created**, check whether a counterpart exists in the + template (URL in the `# Reference Template` section of `AGENTS.md`). + If one exists, fetch it as the starting point; adjust placeholder names and heading + depth to match the target path before writing the file - For each modified file, identify which companion artifacts need updating (requirements, design docs, tests, review-sets) - Include companion artifact updates in the work plan diff --git a/.github/agents/quality.agent.md b/.github/agents/quality.agent.md index da467d4..380d11f 100644 --- a/.github/agents/quality.agent.md +++ b/.github/agents/quality.agent.md @@ -54,21 +54,13 @@ Priority-ordered list of issues that MUST be resolved for the next retry: ## Requirements Compliance: (PASS|FAIL|N/A) -- Were requirements updated to reflect functional changes? -- Were new requirements created for new features? -- Do requirement IDs follow semantic naming standards? -- Do requirement files follow kebab-case naming convention? -- Are requirement files organized under `docs/reqstream/` with proper folder structure? -- Are OTS requirements properly placed in `docs/reqstream/ots/` subfolder? -- Were source filters applied appropriately for platform-specific requirements? +- Were requirements created/updated for all functional changes? +- Were source filters applied for platform-specific requirements? - Is requirements traceability maintained to tests? ## Design Documentation Compliance: (PASS|FAIL|N/A) -- Were design documents updated for architectural changes? -- Were new design artifacts created for new components? -- Do design folder names use kebab-case convention matching source structure? -- Are design files properly named ({subsystem-name}.md, {unit-name}.md patterns)? +- Were design artifacts created/updated for all new or changed components? - Is `docs/design/introduction.md` present with required Software Structure section? - Are design decisions documented with rationale? - Is system/subsystem/unit categorization maintained? @@ -76,55 +68,50 @@ Priority-ordered list of issues that MUST be resolved for the next retry: ## Code Quality Compliance: (PASS|FAIL|N/A) -- Are language-specific standards followed (from applicable standards files)? -- Are quality checks from standards files satisfied? -- Is code properly categorized (system/subsystem/unit/OTS)? -- Is appropriate separation of concerns maintained? -- Was language-specific build tooling executed and passing? +- Do language-specific quality checks from loaded standards pass? +- Is code properly categorized (system/subsystem/unit/OTS/Shared Package)? +- Does the build pass? ## Testing Compliance: (PASS|FAIL|N/A) - Were tests created/updated for all functional changes? - Is test coverage maintained for all requirements? -- Are testing standards followed (AAA pattern, etc.)? -- Do tests respect software item hierarchy boundaries (System/Subsystem/Unit scope)? +- Do tests respect software item hierarchy boundaries? - Are cross-hierarchy test dependencies documented in design docs? -- Does test categorization align with code structure? -- Do all tests pass without failures? +- Do all tests pass? ## Review Management Compliance: (PASS|FAIL|N/A) -- Were review-sets updated for structural changes (new/deleted systems, subsystems, or units)? -- Do file patterns follow include-then-exclude approach? +- Were review-sets updated for structural changes? - Is review scope appropriate for change magnitude? -- Was ReviewMark tooling executed and passing? -- Were review artifacts generated correctly? +- Does ReviewMark pass? ## Documentation Compliance: (PASS|FAIL|N/A) -- Was README.md updated for user-facing changes? -- Were user guides updated for feature changes? +- Were README.md and user guides updated for user-facing changes? - Does API documentation reflect code changes? - Was compliance documentation generated? -- Does documentation follow standards formatting? -- Is documentation organized under `docs/` following standard folder structure? -- Do Pandoc collections include proper `introduction.md` with Purpose and Scope sections? - Are auto-generated markdown files left unmodified? -- Do README.md files use absolute URLs and include concrete examples? -- Is documentation integrated into ReviewMark review-sets for formal review? +- Is documentation integrated into ReviewMark review-sets? ## Software Item Completeness: (PASS|FAIL|N/A) +- Load `software-items.md` before evaluating this section. + - Does every identified software unit have its own requirements file? - Does every identified software unit have its own design document? - Does every identified subsystem have its own requirements file? - Does every identified subsystem have its own design document? +## Repository Structure Compliance: (PASS|FAIL|N/A) + +- Load `repository-map.md` from the template URL in the `# Reference Template` + section of `AGENTS.md` before evaluating this section. + +- Are parallel artifact trees in sync (reqstream/design/verification/src/test)? +- Does the repository conform to the template `repository-map.md`? + ## Process Compliance: (PASS|FAIL|N/A) -- Was Continuous Compliance workflow followed? -- Did all quality gates execute successfully? -- Were appropriate tools used for validation? -- Were standards consistently applied across work? -- Was compliance evidence generated and preserved? +- Was compliance evidence (test results, review artifacts, generated docs) generated and preserved? ``` diff --git a/.github/agents/software-architect.agent.md b/.github/agents/software-architect.agent.md index 494568d..de5efa2 100644 --- a/.github/agents/software-architect.agent.md +++ b/.github/agents/software-architect.agent.md @@ -13,7 +13,7 @@ Interview the user and produce evolving architecture documentation with prioriti # Standards Read `.github/standards/software-items.md` before starting. Use its definitions -(Software Package, System, Subsystem, Unit, OTS) as vocabulary throughout. +(Software Package, System, Subsystem, Unit, OTS, Shared Package) as vocabulary throughout. # Approach diff --git a/.github/agents/template-sync.agent.md b/.github/agents/template-sync.agent.md new file mode 100644 index 0000000..c013503 --- /dev/null +++ b/.github/agents/template-sync.agent.md @@ -0,0 +1,109 @@ +--- +name: template-sync +description: Audits or synchronizes repository files against the canonical template. + Supports four modes - Audit, Sync, Scaffold, and Recreate. +user-invocable: true +--- + +# Template Sync Agent + +This agent is an orchestrator supporting four modes: + +- **Audit** - report structural deviations; no changes +- **Sync** - patch missing sections into existing files +- **Scaffold** - create files that do not yet exist; skip existing files +- **Recreate** - rebuild existing files from the template, migrating old content + +Read the template URL and `repository-map.md` from the `# Reference Template` +section in `AGENTS.md`, then map the requested scope onto the work groups below. +Delegate each group to a sub-agent. + +# Work Groups + +- **Root config files** - all non-collection files at the repository root +- **One group per flat `docs/` folder** - e.g. `docs/build_notes/`, `docs/user_guide/` +- **One group per system subtree** in `docs/design/`, `docs/verification/`, `docs/reqstream/` - + each subtree and all its descendants is one group + +# Orchestration + +For each group intersecting the requested scope, call a sub-agent with: + +- **context**: + - Group scope and template URL from the `# Reference Template` section in `AGENTS.md` + - Applicable standards from the `# Standards Application` matrix in `AGENTS.md` + for the file types in the group scope + - Project-specific names substitute for placeholders at matching path depth + (e.g. `MySystem` → `{SystemName}`, `my-system` → `{system-name}`) + - For files within `{system-name}/` subtrees in `docs/design/`, `docs/verification/`, + and `docs/reqstream/`: consult `docs/design/introduction.md` to determine whether + each item is a subsystem or unit, then select the appropriate template + (`subsystem-name.*` or `unit-name.*`) regardless of the item's folder depth — + do not infer item type from path depth alone + - If a template counterpart cannot be fetched, skip the file and report it +- **goal**: + - Based on the given mode: + - **Audit** - fetch each template counterpart; compare headings; report missing + sections and depth mismatches; no changes + - **Sync** - as Audit, then insert each missing section; run `pwsh ./fix.ps1` + - **Scaffold** - fetch `repository-map.md` from the template URL in `AGENTS.md` + to identify files that should exist but don't; for each, fetch the template, + populate all sections, write the file; run `pwsh ./fix.ps1` + - **Recreate** - fetch the template and use it as the blueprint for a + freshly authored document: + - Work through the template section by section; for each section, find + any `TEMPLATE-DIRECTIVE` blocks (both `` + in markdown and `# ` in YAML) — execute + each directive (read specified standards, apply structural guidance, + substitute content), then **remove the directive block entirely** from + the output; gather the relevant technical details from all available + sources — the old file, README, related docs, sibling files, and any + other repo context — to populate that section correctly; the old file's + structure and headings are irrelevant; only its factual content is mined + as a source + - **Gap-check**: after all template sections are filled, scan the old + file once more for any technical information not yet captured; if + found, preserve it by appending new relevant sections at the end + - **Before writing**: do a mandatory self-check — for every section that + has a `TEMPLATE-DIRECTIVE` block in the template, explicitly state what + format the directive requires, then verify the drafted content matches + that format exactly (e.g. if the directive says "no sub-headings", + confirm there are no `###` headings inside that section; if it says + "bold-name paragraph blocks", confirm each entry is `**Name**: prose` + with no sub-heading); fix any mismatches before writing the file + - Write the rebuilt file; run `pwsh ./fix.ps1` + - When writing any section: `TEMPLATE-DIRECTIVE` blocks are directives — + execute them (read specified standards, apply structural guidance, substitute + content) and **remove the block entirely** from the written file; inline + `TODO:` placeholders in YAML string values (e.g. `title:`, `justification:`) + are content placeholders — always resolve them to real content; infer from + README, related files, sibling docs, and path; if confident write directly; + if ambiguous offer 2–3 concrete options and ask the user; keep asking until + they answer - never leave a TODO or TEMPLATE-DIRECTIVE in the output unless + the user explicitly requests it + +Collect sub-agent results and assemble the final report. + +# Report Template + +```markdown +# Template Sync Report + +**Result**: (SUCCEEDED|FAILED) +**Mode**: (Audit|Sync|Scaffold|Recreate) + +## Files + +### {file-path} + +- **Template**: {template path} +- **Missing sections**: {list or "none"} +- **Heading depth issues**: {list or "none"} +- **Content format issues**: {list of sections where intra-section content did not + match the template comment's prescribed format, or "none"} *(Recreate only)* +- **Action**: (Reported | Sections added | Created | Rebuilt | No template found) + +## Summary + +- **Conformant**: {count} | **Deviations**: {count} | **Updated**: {count} +``` diff --git a/.github/standards/coding-principles.md b/.github/standards/coding-principles.md index 9e67fbb..6797c61 100644 --- a/.github/standards/coding-principles.md +++ b/.github/standards/coding-principles.md @@ -3,11 +3,6 @@ name: Coding Principles description: Follow these standards when developing any software code. --- -# Coding Principles Standards - -This document defines universal coding principles and quality standards for software development within -Continuous Compliance environments. - # Core Principles ## Literate Coding @@ -20,10 +15,9 @@ All code MUST follow literate programming principles: matches design intent without reading the full codebase - **Logical Separation**: Complex functions use block comments to separate and describe logical steps within the implementation -- **Full Symbol Documentation**: ALL symbols have comprehensive documentation - because reviewers and auditors must verify every implementation detail, not - just the public interface - access-level specifics (public, protected, - private, internal, etc.) vary by language; see the language-specific standard +- **Full Symbol Documentation**: ALL symbols have comprehensive documentation — + not just the public interface, because reviewers and auditors must verify every + implementation detail. Access-level specifics vary by language; see the language-specific standard. - **Clarity Over Cleverness**: Code should be immediately understandable by team members ## API Documentation @@ -79,13 +73,13 @@ interface correctly without reading the implementation: ## Universal Anti-Patterns -- **Skip Literate Coding**: Don't skip literate programming comments - they are required for maintainability -- **Ignore Compiler Warnings**: Don't ignore compiler warnings - they exist for quality enforcement +- **Skip Literate Coding**: Don't skip literate programming comments +- **Ignore Compiler Warnings**: Don't ignore compiler warnings - **Hidden Dependencies**: Don't create untestable code with hidden dependencies - **Hidden Functionality**: Don't implement functionality without requirement traceability because untraced functionality cannot be validated during audits - **Monolithic Functions**: Don't write monolithic functions with multiple responsibilities -- **Overcomplicated Solutions**: Don't make solutions more complex than necessary - favor simplicity and clarity +- **Overcomplicated Solutions**: Don't make solutions more complex than necessary - **Premature Optimization**: Don't optimize for performance before establishing correctness - **Copy-Paste Programming**: Don't duplicate logic - extract common functionality into reusable components - **Magic Numbers**: Don't use unexplained constants - either name them or add clear comments diff --git a/.github/standards/csharp-language.md b/.github/standards/csharp-language.md index 6df39cd..ec05a25 100644 --- a/.github/standards/csharp-language.md +++ b/.github/standards/csharp-language.md @@ -12,9 +12,6 @@ Read these standards first before applying this standard: # API Documentation and Literate Coding Example -The example below demonstrates good XmlDoc API documentation combined with -literate coding comments. - ```csharp /// /// Converts a raw sensor reading into a validated measurement ready for downstream consumers. diff --git a/.github/standards/csharp-testing.md b/.github/standards/csharp-testing.md index 181de02..1f93b72 100644 --- a/.github/standards/csharp-testing.md +++ b/.github/standards/csharp-testing.md @@ -66,8 +66,6 @@ These are non-obvious v3 behaviors that differ from v2 or common assumptions: # Quality Checks -Before submitting C# tests, verify: - - [ ] All tests follow AAA pattern with clear section comments - [ ] Test names follow hierarchical naming pattern above - [ ] Each test verifies single, specific behavior (no shared state between tests) diff --git a/.github/standards/design-documentation.md b/.github/standards/design-documentation.md index 768bf3f..e5b7bf9 100644 --- a/.github/standards/design-documentation.md +++ b/.github/standards/design-documentation.md @@ -4,214 +4,112 @@ description: Follow these standards when creating design documentation. globs: ["docs/design/**/*.md"] --- -# Design Documentation Standards - -This document defines standards for design documentation within Continuous -Compliance environments, extending the general technical documentation -standards with specific requirements for software design artifacts. - -## Required Standards - -Read these standards first before applying this standard: +# Required Standards - **`technical-documentation.md`** - General technical documentation standards -- **`software-items.md`** - Software categorization (System/Subsystem/Unit/OTS) - -# Core Principles +- **`software-items.md`** - Software categorization (System/Subsystem/Unit/OTS/Shared Package) -Design documentation serves as the bridge between requirements and -implementation, providing detailed technical specifications that enable: - -- **Formal Code Review**: Reviewers can verify implementation matches design -- **Compliance Evidence**: Auditors can trace requirements through design to code -- **Maintenance Support**: Developers can understand system structure and interactions -- **Quality Assurance**: Testing teams can validate against detailed specifications - -# Required Structure and Documents - -Design documentation must be organized under `docs/design/` with folder structure -mirroring source code organization because reviewers need clear navigation from -design to implementation: +# Folder Structure ```text docs/design/ -├── introduction.md # Document overview - heading depth # -├── {system-name}.md # System-level design - heading depth # -└── {system-name}/ # System folder (one per system) - ├── {subsystem-name}.md # Subsystem overview - heading depth ## - ├── {subsystem-name}/ # Subsystem folder (kebab-case); may nest recursively - │ ├── {child-subsystem}.md # Child subsystem overview - heading depth ### - │ ├── {child-subsystem}/ # Child subsystem folder (same structure as parent) - │ └── {unit-name}.md # Unit design - heading depth ### - └── {unit-name}.md # System-level unit design - heading depth ## +├── introduction.md # heading depth # +├── {system-name}.md # heading depth # +├── {system-name}/ +│ ├── {subsystem-name}.md # heading depth ## +│ ├── {subsystem-name}/ +│ │ └── {unit-name}.md # heading depth ### +│ └── {unit-name}.md # heading depth ## +├── ots.md # heading depth # (if OTS items exist) +├── ots/ +│ └── {ots-name}.md # heading depth ## +├── shared.md # heading depth # (if Shared Packages exist) +└── shared/ + └── {package-name}.md # heading depth ## ``` -Each scope's overview file lives in its **parent** folder, not inside the scope's own -subfolder - this aligns heading depth with folder depth so the compiled PDF has a -meaningful multi-level outline (see Heading Depth Rule in `technical-documentation.md`). - -## introduction.md (MANDATORY) +All sections in every file are mandatory; write "N/A - {justification}" rather than removing any. +Determine subsystem vs. unit classification from `docs/design/introduction.md` — folder depth does not determine classification. +Do not record version numbers anywhere in design documentation — version information is managed in SBOMs. -The `introduction.md` file serves as the design entry point and MUST include -these sections because auditors need clear scope boundaries and architectural -overview: +# introduction.md (MANDATORY) -### Purpose Section +Must include: -Clear statement of the design document's purpose, audience, and regulatory -or compliance drivers. +- **Purpose**: audience and compliance drivers +- **Scope**: items covered and explicitly excluded (no test projects) +- **Software Structure**: text tree showing all Systems/Subsystems/Units/OTS/Shared items +- **Folder Layout**: text tree showing source folder structure +- **Companion Artifact Structure**: parallel paths for requirements, design, verification, source, tests +- **References** _(if applicable)_: external standards or specifications - only in `introduction.md` -### Scope Section +# System Design (MANDATORY) -Define what software items are covered and what is explicitly excluded. -Design documentation must NOT include test projects, test classes, or test -infrastructure because design documentation documents the architecture of -shipping product code, not ancillary content used to validate it. +Create `{system-name}.md` (`#` heading) and `{system-name}/` folder: -### Software Structure Section (MANDATORY) +- **Architecture**: software items, relationships, and collaboration +- **External Interfaces**: name, direction, format, constraints +- **Dependencies**: OTS and Shared Packages used; cross-reference their design docs +- **Risk Control Measures**: segregation required for risk control (IEC 62304 §5.3.3) +- **Data Flow**: inputs to outputs +- **Design Constraints**: platform, performance, security, regulatory -Include a text-based tree diagram showing the software organization across -System, Subsystem, and Unit levels. Agents MUST read `software-items.md` -to understand these classifications before creating this section. +# Subsystem Design (MANDATORY) -Example format: +Place `{subsystem-name}.md` in the **parent** folder; create `{subsystem-name}/` for children: -```text -Project1Name (System) -├── ComponentA (Subsystem) -│ ├── SubComponentP (Subsystem) -│ │ └── ClassW (Unit) -│ ├── ClassX (Unit) -│ └── ClassY (Unit) -├── ComponentB (Subsystem) -│ └── ClassZ (Unit) -└── UtilityClass (Unit) - -Project2Name (System) -└── HelperClass (Unit) -``` - -### Folder Layout Section (MANDATORY) - -Include a text-based tree diagram showing how the source code folders -mirror the software structure, with file paths and brief descriptions. +- **Overview**: responsibility, boundaries, contained units +- **Interfaces**: what it exposes and consumes +- **Design**: how internal units collaborate -Example format: +# Unit Design (MANDATORY) -```text -src/Project1Name/ -├── ComponentA/ -│ ├── SubComponentP/ -│ │ └── ClassW.cs - Specialized processing engine -│ ├── ClassX.cs - Core business logic handler -│ └── ClassY.cs - Data validation service -├── ComponentB/ -│ └── ClassZ.cs - Integration interface -└── UtilityClass.cs - Common utility functions - -src/Project2Name/ -└── HelperClass.cs - Helper functions -``` +Place `{unit-name}.md` in the **parent** folder: -### References Section (RECOMMENDED) +- **Purpose**: single responsibility +- **Data Model**: fields, properties, types, invariants (IEC 62304 §5.4.2) +- **Key Methods**: name, purpose, algorithm, preconditions, postconditions, parameter types +- **Error Handling**: detection and handling; what is propagated vs. handled locally +- **Dependencies**: other units, subsystems, OTS items, and shared packages used +- **Callers**: units or subsystems that call or consume this unit -If the design references external documents (standards, specifications), include -a `## References` section in `introduction.md`. This is the **only** place in the -design document collection where a References section should appear - do not add -one to any other design file. +# OTS Integration Design (when OTS items exist) -### Companion Artifact Structure (RECOMMENDED) +Create `docs/design/ots.md` (`#` heading) covering the overall OTS integration strategy. -Include a brief note explaining that each software item has parallel artifacts -across the repository, so agents and reviewers can navigate from any one -artifact to all related files: +For each OTS item, create `docs/design/ots/{ots-name}.md` (`##` heading) with sections: -Example format: +- **Purpose**: why chosen and what it provides to the local system +- **Features Used**: which specific features, APIs, or capabilities are consumed +- **Integration Pattern**: how it is consumed; initialization, configuration, disposal requirements -```text -Each in-house software item has corresponding artifacts in parallel directory trees: +# Shared Package Integration Design (when Shared Packages exist) -- Requirements: `docs/reqstream/{system-name}.yaml`, `docs/reqstream/{system-name}/.../{item}.yaml` -- Design docs: `docs/design/{system-name}.md`, `docs/design/{system-name}/.../{item}.md` -- Verification: `docs/verification/{system-name}.md`, `docs/verification/{system-name}/.../{item}.md` -- Source code: `src/{SystemName}/.../{Item}.{ext}` (cased per language - see `software-items.md`) -- Tests: `test/{SystemName}.Tests/.../{Item}Tests.{ext}` (cased per language) +Create `docs/design/shared.md` (`#` heading) covering the overall consumption strategy. -OTS items have no design documentation; their artifacts sit parallel to system folders: +For each Shared Package, create `docs/design/shared/{package-name}.md` (`##` heading) with sections: -- Requirements: `docs/reqstream/ots/{ots-name}.yaml` -- Verification: `docs/verification/ots/{ots-name}.md` -- Tests (optional): `test/{OtsSoftwareTests}/...` (cased per language - see `software-items.md`) - -Review-sets: defined in `.reviewmark.yaml` -``` - -## System Design Documentation (MANDATORY) - -For each system identified in the repository: - -- Create `{system-name}.md` directly under `docs/design/` (heading depth `#`) -- Create a kebab-case folder `{system-name}/` to hold its subsystems and units -- `{system-name}.md` must cover: - - System architecture and major components - - External interfaces and dependencies - - Data flow and control flow - - System-wide design constraints and decisions - - Integration patterns and communication protocols - -## Subsystem and Unit Design Documents - -For each subsystem identified in the software structure: - -- Place `{subsystem-name}.md` inside the **parent** folder (the system folder, or parent - subsystem folder) - not inside its own subfolder -- Create a kebab-case folder `{subsystem-name}/` to hold its child units and subsystems -- `{subsystem-name}.md` must cover subsystem overview and design - -For every unit identified in the software structure: - -- Place `{unit-name}.md` inside its parent scope's folder (system or subsystem folder) -- Document data models, algorithms, and key methods -- Describe interactions with other units -- Include sufficient detail for formal code review - -Follow the Heading Depth Rule from `technical-documentation.md` - a file's top-level -heading depth equals its folder depth under `docs/design/`. - -# Software Items Integration (CRITICAL) - -Read `software-items.md` before creating design documentation - correct -System/Subsystem/Unit categorization is required for software structure -diagrams and folder layout. +- **Advertised Features Consumed**: which features the local system relies on +- **Integration Pattern**: how the package is referenced, initialized, and consumed +- **Assumptions**: any assumptions the local system makes about the package's behavior # Writing Guidelines -Design documentation must be technical and specific because it serves as the -implementation specification for formal code review: - -- **Implementation Detail**: Provide sufficient detail for code review and implementation -- **Architectural Clarity**: Clearly define component boundaries and interfaces -- **Traceability**: Link to requirements where applicable using ReqStream patterns -- **Verbal Cross-References**: Reference other parts of the design by name (e.g., - "See *Parser Design* for more details") - do not use markdown hyperlinks, which - break in compiled PDFs - -# Mermaid Diagram Integration - -Use Mermaid diagrams to supplement text descriptions (diagrams must not replace text content). +- Use Mermaid diagrams to supplement (not replace) text +- Use verbal cross-references ("see _Parser Design_") - not markdown hyperlinks (break in PDF) +- Provide sufficient detail for formal code review # Quality Checks -Before submitting design documentation, verify: - -- [ ] `introduction.md` includes both Software Structure and Folder Layout sections -- [ ] Software structure correctly categorizes items as System/Subsystem/Unit per `software-items.md` -- [ ] Folder layout mirrors software structure organization -- [ ] Files organized under `docs/design/` following the folder structure pattern above -- [ ] Each file's top-level heading depth matches its folder depth per the Heading Depth Rule -- [ ] Design documents provide sufficient detail for code review -- [ ] System documentation provides comprehensive system-level design -- [ ] All documentation folders use kebab-case names mirroring source code structure -- [ ] All documents follow technical documentation formatting standards -- [ ] Content is current with implementation and requirements -- [ ] Documents are integrated into ReviewMark review-sets for formal review +- [ ] `introduction.md` includes Software Structure, Folder Layout, and Companion Artifact Structure +- [ ] Software structure correctly categorizes items per `software-items.md` +- [ ] Each file's heading depth matches its folder depth +- [ ] All folders use kebab-case mirroring source structure +- [ ] System design includes all mandatory sections (Architecture, External Interfaces, Dependencies, + Risk Control Measures, Data Flow, Design Constraints) +- [ ] Subsystem design includes all mandatory sections (Overview, Interfaces, Design) +- [ ] Unit design includes all mandatory sections (Purpose, Data Model, Key Methods, Error Handling, Dependencies, Callers) +- [ ] Non-applicable mandatory sections contain "N/A - {justification}" +- [ ] `docs/design/ots.md` and `docs/design/ots/{ots-name}.md` exist when OTS items are present +- [ ] `docs/design/shared.md` and `docs/design/shared/{package-name}.md` exist when Shared Packages are present +- [ ] Documents are integrated into ReviewMark review-sets diff --git a/.github/standards/reqstream-usage.md b/.github/standards/reqstream-usage.md index 303bb43..2371164 100644 --- a/.github/standards/reqstream-usage.md +++ b/.github/standards/reqstream-usage.md @@ -9,7 +9,7 @@ globs: ["requirements.yaml", "docs/reqstream/**/*.yaml"] Read these standards first before applying this standard: - **`requirements-principles.md`** - Requirements principles and unidirectionality -- **`software-items.md`** - Software categorization (System/Subsystem/Unit/OTS) +- **`software-items.md`** - Software categorization (System/Subsystem/Unit/OTS/Shared Package) # Requirements Organization @@ -25,52 +25,76 @@ docs/reqstream/ │ ├── platform-requirements.yaml # Platform support requirements │ ├── {subsystem-name}.yaml # Subsystem requirements │ ├── {subsystem-name}/ # Subsystem folder (kebab-case); may nest recursively -│ │ ├── {child-subsystem}.yaml # Child subsystem requirements -│ │ ├── {child-subsystem}/ # Child subsystem folder +│ │ ├── {subsystem-name}.yaml # Child subsystem requirements +│ │ ├── {subsystem-name}/ # Child subsystem folder │ │ └── {unit-name}.yaml # Unit requirements │ └── {unit-name}.yaml # System-level unit requirements -└── ots/ # OTS items appear as a distinct section in reports - └── {ots-name}.yaml # Requirements for OTS components +├── ots/ # OTS items appear as a distinct section in reports +│ └── {ots-name}.yaml # Requirements for OTS components +└── shared/ # Shared Packages appear as a distinct section in reports + └── {package-name}.yaml # Requirements for Shared Package dependencies ``` -In-house items have matching relative paths across `docs/reqstream/`, `docs/design/`, and -`docs/verification/`. OTS items appear only in `docs/reqstream/ots/` and -`docs/verification/ots/` - they have no design documentation. +Local items have matching relative paths across `docs/reqstream/`, `docs/design/`, and `docs/verification/`: + +- Requirements: `{system-name}[/{subsystem-name}...]/{item-name}.yaml` +- Design: `{system-name}[/{subsystem-name}...]/{item-name}.md` +- Verification: `{system-name}[/{subsystem-name}...]/{item-name}.md` # Requirements File Format -```yaml -sections: - - title: Functional Requirements - requirements: - - id: System-Component-Feature # Used as-is in all reports - make it readable - title: The system shall perform the required function. - justification: | - Business rationale and any regulatory references. - # ReqStream extracts this field into the justifications report (--justifications) - children: # ReqStream validates this decomposition chain - - ChildSystem-Feature-Behavior # Downward links only (see requirements-principles.md) - tests: # ReqStream matches these by method name in test results - - TestMethodName - - windows@PlatformSpecificTest # Only test runs on Windows count as evidence -``` +Each file adds requirements at exactly one level of the hierarchy. The file spells out +its full ancestry as nested `{ItemName} Requirements` sections down to that level, then +places requirements there. ReqStream merges identical section title paths across included +files automatically. Always determine item classification from `docs/design/introduction.md` - +folder depth does not determine whether an item is a subsystem or unit. -# OTS Software Requirements +Valid section nestings (names in `{braces}` are placeholders): + +```text +{SystemName} Requirements # system-level requirements +├── {SubsystemName} Requirements # root subsystem requirements +│ ├── {SubsystemName} Requirements # nested subsystem (may recurse) +│ │ └── {UnitName} Requirements # unit under a nested subsystem +│ └── {UnitName} Requirements # unit under a root subsystem +└── {UnitName} Requirements # unit directly under the system +OTS Software Requirements # OTS root section (fixed title) +└── {OtsName} Requirements # requirements for one OTS item +Shared Package Requirements # shared package root section (fixed title) +└── {PackageName} Requirements # requirements for one shared package +``` -Use nested sections in `docs/reqstream/ots/` because ReqStream renders the `ots/` -subtree as a distinct section in generated reports, separate from in-house -system requirements: +Each file implements one path through this tree: ```yaml sections: - - title: OTS Software Requirements + - title: '{SystemName} Requirements' sections: - - title: System.Text.Json + - title: '{SubsystemName} Requirements' requirements: - - id: SystemTextJson-Core-ReadJson - title: System.Text.Json shall be able to read JSON files. - tests: - - JsonReaderTests.TestReadValidJson + - id: System-Subsystem-Feature # Used as-is in all reports - make it readable + title: The subsystem shall perform the required function. + justification: | # ReqStream extracts this into the justifications report (--justifications) + Business rationale and any regulatory references. + tags: # Optional: categorize for filtering with --filter + - security + children: # Optional: ReqStream validates this decomposition chain + - System-Subsystem-Unit-Feat # Downward links only (see requirements-principles.md) + tests: # ReqStream matches these by method name in test results + - TestMethodName + - windows@PlatformSpecificTest # Only test runs on Windows count as evidence +``` + +# Tags (OPTIONAL) + +Tags are free-form - no mandatory vocabulary. Common tags: `security`, `safety`, `performance`, +`compliance`, `reliability`, `critical`. Use `--filter` to selectively export or enforce subsets +(OR logic across comma-separated tags): + +```bash +dotnet reqstream --requirements requirements.yaml \ + --filter security,critical \ + --report docs/requirements_doc/generated/security_requirements.md ``` # Semantic IDs (MANDATORY) @@ -125,12 +149,9 @@ dotnet reqstream --requirements requirements.yaml \ Before submitting requirements, verify: -- [ ] All requirements have semantic IDs (`System-Section-Feature` pattern) -- [ ] Every requirement links to at least one passing test +- [ ] All requirements have semantic IDs (`System-Component-Feature` pattern) +- [ ] Every requirement has a justification explaining business/regulatory need +- [ ] Every requirement links to at least one test - [ ] Platform-specific requirements use source filters (`platform@TestName`) -- [ ] Comprehensive justification explains business/regulatory need -- [ ] Files organized under `docs/reqstream/` following the folder structure pattern above -- [ ] All documentation folders use kebab-case names matching source code structure -- [ ] OTS requirements placed in `ots/` subfolder -- [ ] Valid YAML syntax passes yamllint validation -- [ ] Test result formats compatible (TRX, JUnit XML) +- [ ] All files and folders use kebab-case names matching source code structure +- [ ] All files are organized under `docs/reqstream/` following the folder structure above diff --git a/.github/standards/reviewmark-usage.md b/.github/standards/reviewmark-usage.md index 2d95832..b521433 100644 --- a/.github/standards/reviewmark-usage.md +++ b/.github/standards/reviewmark-usage.md @@ -9,7 +9,7 @@ description: Follow these standards when configuring file reviews with ReviewMar Read these standards first before applying this standard: -- **`software-items.md`** - Software categorization (System/Subsystem/Unit/OTS) +- **`software-items.md`** - Software categorization (System/Subsystem/Unit/OTS/Shared Package) ## Purpose @@ -88,12 +88,11 @@ When constructing review-sets, follow these principles to maintain manageable sc # Review-Set Organization -Organize review-sets using these standard patterns to ensure comprehensive coverage -while keeping each review manageable in scope: - -**Naming conventions**: See `software-items.md` - kebab-case placeholders -(e.g., `{system-name}`) are always kebab-case; cased placeholders -(e.g., `{SystemName}`) follow your language's convention. +**Naming conventions**: Placeholders in documentation, requirements, design, and +verification file paths are kebab-case (e.g., `{system-name}`). Placeholders in +source and test file paths may use the casing conventional for the project's +source language or repository layout (e.g., `{SystemName}`). Review-set name +placeholders are always PascalCase (e.g., `{SystemName}`). ## `Purpose` Review (only one per repository) @@ -110,12 +109,12 @@ Reviews user-facing capabilities and system promises: - Design introduction: `docs/design/introduction.md` - System design: `docs/design/{system-name}.md` -## `{System}-Architecture` Review (one per system) +## `{SystemName}-Architecture` Review (one per system) Reviews system architecture and operational validation: - **Purpose**: Proves that the system is designed and tested to satisfy its requirements -- **Title**: "Review that {System} Architecture Satisfies Requirements" +- **Title**: "Review that {SystemName} Architecture Satisfies Requirements" - **Scope**: Excludes subsystem and unit files, relying on system-level design to describe what subsystems and units it uses - **File Path Patterns**: @@ -126,12 +125,12 @@ Reviews system architecture and operational validation: - System verification design: `docs/verification/{system-name}.md` - System integration tests: `test/{SystemName}.Tests/{SystemName}Tests.{ext}` -## `{System}-Design` Review (one per system) +## `{SystemName}-Design` Review (one per system) Reviews architectural and design consistency: - **Purpose**: Proves the system design is consistent and complete -- **Title**: "Review that {System} Design is Consistent and Complete" +- **Title**: "Review that {SystemName} Design is Consistent and Complete" - **Scope**: Only brings in top-level requirements and relies on brevity of design documentation - **File Path Patterns**: - System requirements: `docs/reqstream/{system-name}.yaml` @@ -139,59 +138,91 @@ Reviews architectural and design consistency: - Design introduction: `docs/design/introduction.md` - System design: `docs/design/{system-name}.md` - System design files: `docs/design/{system-name}/**/*.md` + - OTS overview: `docs/design/ots.md` _(only if OTS items exist)_ + - Shared Package overview: `docs/design/shared.md` _(only if Shared Package items exist)_ + +## `{SystemName}-Verification` Review (one per system) + +Reviews verification completeness and consistency: -## `{System}-AllRequirements` Review (one per system) +- **Purpose**: Proves the system verification design is consistent and covers all requirements +- **Title**: "Review that {SystemName} Verification is Consistent and Complete" +- **Scope**: Only brings in top-level requirements and all verification docs for the system +- **File Path Patterns**: + - System requirements: `docs/reqstream/{system-name}.yaml` + - Verification introduction: `docs/verification/introduction.md` + - System verification: `docs/verification/{system-name}.md` + - System verification files: `docs/verification/{system-name}/**/*.md` + - OTS overview: `docs/verification/ots.md` _(only if OTS items exist)_ + - Shared Package overview: `docs/verification/shared.md` _(only if Shared Package items exist)_ + +## `{SystemName}-AllRequirements` Review (one per system) Reviews requirements quality and traceability: - **Purpose**: Proves the requirements are consistent and complete -- **Title**: "Review that All {System} Requirements are Complete" +- **Title**: "Review that All {SystemName} Requirements are Complete" - **Scope**: Only brings in requirements files to keep review manageable - **File Path Patterns**: - Root requirements: `requirements.yaml` - System requirements: `docs/reqstream/{system-name}.yaml` - Subsystem/unit requirements: `docs/reqstream/{system-name}/**/*.yaml` -## `{System}-{Subsystem[-Child...]}` Review (one per subsystem at any depth) +## `{SystemName}-{SubsystemName}[-{SubsystemName}...]` Review (one per subsystem at any depth) Reviews subsystem architecture and interfaces: - **Purpose**: Proves that the subsystem is designed and tested to satisfy its requirements -- **Title**: "Review that {System} {Subsystem} Satisfies Subsystem Requirements" +- **Title**: "Review that {SystemName} {SubsystemName} Satisfies Subsystem Requirements" - **Scope**: Excludes units under the subsystem, relying on subsystem design to describe what units it uses - **File Path Patterns**: - - Requirements: `docs/reqstream/{system-name}/.../{subsystem-name}.yaml` - - Design: `docs/design/{system-name}/.../{subsystem-name}.md` - - Verification design: `docs/verification/{system-name}/.../{subsystem-name}.md` - - Tests: `test/{SystemName}.Tests/.../{SubsystemName}/{SubsystemName}Tests.{ext}` + - Requirements: `docs/reqstream/{system-name}[/{subsystem-name}...]/{subsystem-name}.yaml` + - Design: `docs/design/{system-name}[/{subsystem-name}...]/{subsystem-name}.md` + - Verification design: `docs/verification/{system-name}[/{subsystem-name}...]/{subsystem-name}.md` + - Tests: `test/{SystemName}.Tests[/{SubsystemName}...]/{SubsystemName}Tests.{ext}` -## `{System}-{Subsystem[-Child...]}-{Unit}` Review (one per unit) +## `{SystemName}-{SubsystemName}[-{SubsystemName}...]-{UnitName}` Review (one per unit) Reviews individual software unit implementation: - **Purpose**: Proves the unit is designed, implemented, and tested to satisfy its requirements -- **Title**: "Review that {System} {Subsystem} {Unit} Implementation is Correct" +- **Title**: "Review that {SystemName} {SubsystemName} {UnitName} Implementation is Correct" - **Scope**: Complete unit review including all artifacts - **File Path Patterns**: - - Requirements: `docs/reqstream/{system-name}/.../{unit-name}.yaml` - - Design: `docs/design/{system-name}/.../{unit-name}.md` - - Verification design: `docs/verification/{system-name}/.../{unit-name}.md` - - Source: `src/{SystemName}/.../{UnitName}.{ext}` - - Tests: `test/{SystemName}.Tests/.../{UnitName}Tests.{ext}` + - Requirements: `docs/reqstream/{system-name}[/{subsystem-name}...]/{unit-name}.yaml` + - Design: `docs/design/{system-name}[/{subsystem-name}...]/{unit-name}.md` + - Verification design: `docs/verification/{system-name}[/{subsystem-name}...]/{unit-name}.md` + - Source (C# example): `src/{SystemName}[/{SubsystemName}...]/{UnitName}.cs` + - Tests (C# example): `test/{SystemName}.Tests[/{SubsystemName}...]/{UnitName}Tests.cs` + - Source (snake_case C++ example): `src/{system_name}[/{subsystem_name}...]/{unit_name}.cpp` + - Tests (snake_case C++ example): `test/{system_name}_tests[/{subsystem_name}...]/{unit_name}_tests.cpp` ## `OTS-{OtsName}` Review (one per OTS item) -Reviews OTS item requirements and verification evidence: +Reviews OTS item integration design, requirements, and verification evidence: -- **Purpose**: Proves that the OTS item provides the required functionality +- **Purpose**: Proves that the OTS item provides the required functionality and is correctly integrated - **Title**: "Review that {OtsName} Provides Required Functionality" -- **Scope**: OTS items have no in-house design or source; review covers requirements and - verification evidence only +- **Scope**: No local source code; review covers integration design, requirements, and verification evidence - **File Path Patterns**: - OTS requirements: `docs/reqstream/ots/{ots-name}.yaml` + - OTS integration design: `docs/design/ots/{ots-name}.md` - OTS verification: `docs/verification/ots/{ots-name}.md` - - Tests (if applicable): `test/{OtsSoftwareTests}/...` (cased per language) + - Tests (if applicable): `test/OtsSoftwareTests/...` (C#) or `test/ots_software_tests/...` + (Python/other) — fixed repo-level name, no system prefix + +## `Shared-{PackageName}` Review (one per Shared Package) + +Reviews Shared Package integration design, requirements, and verification evidence: + +- **Purpose**: Proves that the Shared Package provides the required advertised features and is correctly integrated +- **Title**: "Review that {PackageName} Provides Required Features" +- **Scope**: No local source code; review covers integration design, requirements, and verification evidence +- **File Path Patterns**: + - Shared Package requirements: `docs/reqstream/shared/{package-name}.yaml` + - Shared Package integration design: `docs/design/shared/{package-name}.md` + - Shared Package verification: `docs/verification/shared/{package-name}.md` **Note**: File path patterns use `{ext}` as a placeholder for language-specific extensions (`.cs`, `.cpp`/`.hpp`, `.py`, etc.). Adapt to your repository's languages. @@ -202,14 +233,6 @@ Before submitting ReviewMark configuration, verify: - [ ] `.reviewmark.yaml` exists at repository root with proper structure - [ ] Review-set organization follows the standard hierarchy patterns -- [ ] Purpose review-set includes README.md, user guide, system requirements, design introduction, and system design files -- [ ] System-level reviews follow hierarchical scope principle (exclude subsystem/unit details) -- [ ] Subsystem reviews follow hierarchical scope principle (exclude unit source code) -- [ ] Only unit reviews include actual source code files -- [ ] Architecture review-sets include system verification design alongside system design -- [ ] Subsystem review-sets include subsystem verification design -- [ ] Unit review-sets include unit verification design -- [ ] OTS review-sets include OTS requirements and verification evidence - [ ] Each review-set focuses on a single compliance question (single focus principle) - [ ] File patterns use correct glob syntax and match intended files - [ ] Review-set file counts remain manageable (context management principle) diff --git a/.github/standards/software-items.md b/.github/standards/software-items.md index 6be029f..6c29525 100644 --- a/.github/standards/software-items.md +++ b/.github/standards/software-items.md @@ -3,20 +3,16 @@ name: Software Items description: Follow these standards when categorizing software components. --- -# Software Items Definition Standards - -This document defines standards for categorizing software items within -Continuous Compliance environments because proper categorization determines -requirements management approach, testing strategy, and review scope. - # Software Item Categories -Categorize all software into five primary groups: +Categorize all software into six primary groups: - **Software Package**: Distributable unit delivered to end users or dependent systems, containing one software system with all its components. All software - systems are delivered as a software package. When consumed by another system, - our software package is treated as an OTS Software Item by that system. + systems are delivered as a software package. When consumed by a system outside + the producing program, our software package is treated as an OTS Software Item + by that system. When consumed by another repository within the same program, + it is treated as a Shared Package. - **Software System**: Complete deliverable product including all components and external interfaces, contained within a software package - **Software Subsystem**: Major architectural component with well-defined @@ -24,7 +20,11 @@ Categorize all software into five primary groups: - **Software Unit**: Individual class, function, or tightly coupled set of functions that can be tested in isolation - **OTS Software Item**: Third-party component (library, framework, tool, or - published software package) providing functionality not developed in-house + published software package) providing functionality not developed within the program +- **Shared Package**: A software package produced by a different repository within + the same program, consumed as a dependency. Referenced by its advertised features + rather than internal design; traceability to program-level requirements runs + through the top-level project. **Naming**: When names collide in hierarchy, add descriptive suffix to higher-level entity: @@ -34,17 +34,28 @@ Categorize all software into five primary groups: # Naming Conventions in File Path Patterns -Two placeholder styles appear in path patterns across these standards: +Three placeholder forms appear in path patterns across these standards: -- **Kebab-case** (`{system-name}`, `{unit-name}`): always kebab-case - - used in documentation and requirements paths -- **Cased** (`{SystemName}`, `{UnitName}`): follow your language's convention - - `PascalCase` for C#/Java, `snake_case` for C++/Python - - used in source and test file paths +- **Kebab-case** (`{system-name}`, `{unit-name}`): always kebab-case — + documentation and requirements file paths +- **PascalCase IDs** (`{SystemName}`, `{UnitName}`): always PascalCase — + requirements IDs, ReviewMark IDs, and other documentation identifiers +- **Language-cased** (`{SystemName}` or `{system_name}`): follow your language's + convention — `PascalCase` for C#/Java, `snake_case` for C++/Python — + source and test file/folder names -# Categorization Guidelines +## Nesting Depth Notation -Choose the appropriate category based on scope and testability: +Subsystems nest to any depth. Patterns use bracket-ellipsis to express this without +enumerating levels — `[/{subsystem-name}...]` in paths, `[-{SubsystemName}...]` in +dash-separated IDs. Examples covering all three forms: + +- `{SystemName}[-{SubsystemName}...]-{UnitName}-Feature` (PascalCase ID) +- `docs/design/{system-name}[/{subsystem-name}...]/{unit-name}.md` (kebab-case doc path) +- `src/{SystemName}[/{SubsystemName}...]/{UnitName}.cs` (C# source path) +- `src/{system_name}[/{subsystem_name}...]/{unit_name}.cpp` (C++/Python source path) + +# Categorization Guidelines ## Software Package @@ -75,20 +86,37 @@ Choose the appropriate category based on scope and testability: ## OTS Software Item -- External dependency not developed in-house - typically a third-party published +- External dependency from outside the program - typically a third-party published software package (NuGet, npm, etc.), hosted service, or tool -- Our own published software package becomes an OTS item to any system that - consumes it +- A package produced by an unrelated program (inside or outside the organization) + is treated as OTS by any consuming system - Tested through integration tests proving required functionality works - Examples: System.Text.Json, Entity Framework, third-party APIs -- **Artifact locations** (OTS items have no design documentation): +- **Artifact locations** (OTS items have no internal design documentation): - Requirements: `docs/reqstream/ots/{ots-name}.yaml` + - Design: `docs/design/ots/{ots-name}.md` (integration/usage design) - Verification: `docs/verification/ots/{ots-name}.md` - These folders sit parallel to system folders (not inside any system folder) - System design documentation records which OTS items each system depends on - **OTS test project**: If no other verification evidence is available (e.g., vendor test results, - published compliance reports), a dedicated test project (`OtsSoftwareTests` / `ots_software_tests`, - cased per language) holds OTS integration tests - one test file per OTS item requiring tests. + published compliance reports), a dedicated test project holds OTS integration tests - one test + file per OTS item requiring tests. OTS items are repo-level (not per-system), so the project + uses a fixed repo-level name: `test/OtsSoftwareTests/` (C#) or `test/ots_software_tests/` + (Python/other) — never prefixed with a system or project name. + +## Shared Package + +- A software package produced by a different repository within the same program +- The consuming repository references advertised features, not internal design or source +- Traceability to program-level requirements runs through the top-level project, + not directly between repositories +- Verified through any appropriate approach in the consuming repository - most commonly + downstream integration tests that transitively prove the advertised features are functional +- **Artifact locations** (no internal design documentation in the consuming repository): + - Requirements: `docs/reqstream/shared/{package-name}.yaml` + - Design: `docs/design/shared/{package-name}.md` (integration/usage design) + - Verification: `docs/verification/shared/{package-name}.md` + - These folders sit parallel to system and OTS folders # Software Item Artifact Model @@ -97,7 +125,8 @@ unit - because reviewing any one artifact in isolation cannot determine whether the item is correct, well-designed, and proven to work: - **Requirements** - WHAT the item must do (drives all other artifacts; applies to all item types) -- **Design** - HOW the item satisfies its requirements (in-house items only: system, subsystem, unit) +- **Design** - HOW the item satisfies its requirements (full design for local items: system, + subsystem, unit; integration/usage design for OTS and Shared Package) - **Verification Design** - HOW the requirements will be tested (applies to all item types) -- **Source code** - The implementation of the design (in-house units only) +- **Source code** - The implementation of the design (local units only; not applicable to OTS or Shared Package) - **Tests** - PROOF the item does WHAT it is required to do (applies to all item types) diff --git a/.github/standards/technical-documentation.md b/.github/standards/technical-documentation.md index 2ac29f4..a413657 100644 --- a/.github/standards/technical-documentation.md +++ b/.github/standards/technical-documentation.md @@ -6,9 +6,6 @@ globs: ["docs/**/*.md", "README.md", "!docs/**/generated/**"] # Technical Documentation Standards -This document defines standards for technical documentation within Continuous -Compliance environments. - # Core Principles Technical documentation serves as compliance evidence and must be structured @@ -40,8 +37,11 @@ docs/{collection}/ Without `title.txt` and `definition.yaml` the pipeline cannot generate the document. When creating a new document collection, create these three files together and use -the existing collections under `docs/` as templates - they share a consistent -structure across all collections. +the existing collections under `docs/` as templates. + +The `generated/` folder is **never committed** to the repository - it is created +locally and in CI by the build pipeline. Do not flag its absence as a conformance +issue. **`title.txt`** - YAML front matter with document metadata. Use the existing files under `docs/` as a pattern and keep fields consistent with the rest of @@ -81,8 +81,7 @@ elsewhere causes duplicate sections in the compiled PDF. ## Document Ordering -List documents in logical reading order in Pandoc configuration because -readers need coherent information flow from general to specific topics. +List documents in logical reading order in `definition.yaml`. ## Heading Depth Rule (MANDATORY) @@ -106,15 +105,10 @@ available - keep internal structure flat to avoid excessive nesting. Write technical documentation for clarity and compliance verification: - **Clear and Concise**: Use direct language and avoid unnecessary complexity. - Regulatory reviewers must understand content quickly. -- **Structured Sections**: Use consistent heading hierarchy and section - organization. Enables automated processing and review. -- **Specific Examples**: Include concrete examples with actual values rather - than placeholders. Supports implementation verification. +- **Structured Sections**: Use consistent heading hierarchy and section organization. +- **Specific Examples**: Include concrete examples with actual values rather than placeholders. - **Current Information**: Keep documentation synchronized with code changes. - Outdated documentation invalidates compliance evidence. -- **Traceable Content**: Link documentation to requirements and implementation - where applicable for audit trails. +- **Traceable Content**: Link documentation to requirements and implementation where applicable. ## References Sections @@ -134,26 +128,16 @@ Instead use **verbal references** - plain prose that identifies the target by na > > Refer to the *System Requirements* document for the full specification. -Verbal references are readable by both AI agents and humans in any rendering environment. - # Markdown Format Requirements -Markdown documentation in this repository must follow the formatting standards -defined in `.markdownlint-cli2.yaml` (subject to any exclusions configured there) -for consistency and professional presentation: - -- **120 Character Line Limit**: Keep lines 120 characters or fewer for readability. - Break long lines naturally at punctuation or logical breaks. -- **No Trailing Whitespace**: Remove all trailing spaces and tabs from line - endings to prevent formatting inconsistencies. -- **Blank Lines Around Headings**: Include a blank line both before and after - each heading to improve document structure and readability. -- **Blank Lines Around Lists**: Include a blank line both before and after - numbered and bullet lists to ensure proper rendering and visual separation. -- **ATX-Style Headers**: Use `#` syntax for headers instead of underline style - for consistency across all documentation. -- **Consistent List Indentation**: Use 2-space indentation for nested list - items to maintain uniform formatting. +Follow `.markdownlint-cli2.yaml` formatting standards: + +- **120 Character Line Limit (Guideline)**: Prefer lines 120 characters or fewer; break at punctuation or logical breaks when practical. +- **No Trailing Whitespace**: Remove all trailing spaces and tabs. +- **Blank Lines Around Headings**: Include a blank line before and after each heading. +- **Blank Lines Around Lists**: Include a blank line before and after numbered and bullet lists. +- **ATX-Style Headers**: Use `#` syntax, not underline style. +- **Consistent List Indentation**: Use 2-space indentation for nested list items. # Auto-Generated Content (CRITICAL) @@ -164,8 +148,6 @@ build outputs that are overwritten on every CI run: respective `docs/` sections, or in `docs/generated/` for final release artifacts - **Source Modification**: Update source files (requirements YAML, `.reviewmark.yaml`, tool configuration) instead of generated output -- **Tool Integration**: Generated content integrates with CI/CD pipelines and - manual changes disrupt automation # README.md Best Practices @@ -188,20 +170,12 @@ Structure README.md for both human readers and AI agent processing: - **Code Block Languages**: Specify language for syntax highlighting and tool processing - **Clear Prerequisites**: List exact version requirements and dependencies -## Quality Guidelines - -- **Scannable Structure**: Use bullet points, headings, and short paragraphs -- **Current Examples**: Verify all code examples work with current version -- **Link Validation**: Ensure all external links are accessible and current -- **Consistent Tone**: Professional, helpful tone appropriate for technical audience - # Quality Checks Before submitting technical documentation, verify: - [ ] Documentation organized under `docs/` following standard folder structure - [ ] Pandoc collections include `introduction.md` with Purpose and Scope sections -- [ ] Content follows clear and concise writing guidelines with specific examples - [ ] No modifications made to auto-generated markdown files in compliance folders - [ ] README.md includes all required sections with absolute URLs and concrete examples - [ ] Documentation integrated into ReviewMark review-sets for formal review diff --git a/.github/standards/testing-principles.md b/.github/standards/testing-principles.md index 73974ff..917463e 100644 --- a/.github/standards/testing-principles.md +++ b/.github/standards/testing-principles.md @@ -3,11 +3,6 @@ name: Testing Principles description: Follow these standards when developing any software tests. --- -# Testing Principles Standards - -This document defines universal testing principles and quality standards for test development within -Continuous Compliance environments. - # Test Dependency Boundaries (MANDATORY) Respect software item hierarchy boundaries to ensure review-sets can validate proper architectural scope. diff --git a/.github/standards/verification-documentation.md b/.github/standards/verification-documentation.md index 8eea3b7..494e40f 100644 --- a/.github/standards/verification-documentation.md +++ b/.github/standards/verification-documentation.md @@ -6,139 +6,96 @@ globs: ["docs/verification/**/*.md"] # Required Standards -Read these standards first before applying this standard: - - **`technical-documentation.md`** - General technical documentation standards -- **`software-items.md`** - Software categorization (System/Subsystem/Unit/OTS) - -# Core Principles - -Verification design is the bridge between requirements and tests - it documents HOW -requirements will be verified, enabling reviewers to confirm test completeness without -reading implementation code. - -# Required Structure and Documents +- **`software-items.md`** - Software categorization (System/Subsystem/Unit/OTS/Shared Package) -Organize under `docs/verification/` mirroring the software item hierarchy: +# Folder Structure ```text docs/verification/ -├── introduction.md # Document overview - heading depth # -├── {system-name}.md # System-level verification - heading depth # -├── {system-name}/ # System folder (one per system) -│ ├── {subsystem-name}.md # Subsystem verification - heading depth ## -│ ├── {subsystem-name}/ # Subsystem folder (kebab-case); may nest recursively -│ │ ├── {child-subsystem}.md # Child subsystem verification - heading depth ### -│ │ ├── {child-subsystem}/ # Child subsystem folder (same structure as parent) -│ │ └── {unit-name}.md # Unit verification - heading depth ### -│ └── {unit-name}.md # System-level unit verification - heading depth ## -├── ots.md # OTS section overview - heading depth # (MANDATORY if OTS items exist) -└── ots/ # OTS items - parallel to system folders (not inside them) - └── {ots-name}.md # OTS item verification evidence - heading depth ## +├── introduction.md # heading depth # +├── {system-name}.md # heading depth # +├── {system-name}/ +│ ├── {subsystem-name}.md # heading depth ## +│ ├── {subsystem-name}/ +│ │ └── {unit-name}.md # heading depth ### +│ └── {unit-name}.md # heading depth ## +├── ots.md # heading depth # (if OTS items exist) +├── ots/ +│ └── {ots-name}.md # heading depth ## +├── shared.md # heading depth # (if Shared Packages exist) +└── shared/ + └── {package-name}.md # heading depth ## ``` -Each scope's overview file lives in its **parent** folder, not inside the scope's own -subfolder - this keeps artifact locations consistent with design and requirements trees -so any item's files are deterministically locatable, and aligns heading depth with folder -depth for correct PDF structure (see Heading Depth Rule in `technical-documentation.md`). +All sections in every file are mandatory; write "N/A - {justification}" rather than removing any. +Determine subsystem vs. unit classification from `docs/design/introduction.md` — folder depth does not determine classification. -## introduction.md (MANDATORY) +# introduction.md (MANDATORY) -Follow the standard `introduction.md` format from `technical-documentation.md`. Scope -covers all software items including OTS items (via self-validation if appropriate). +Must include: -Include a Companion Artifact Structure note so agents and reviewers can navigate from any -artifact to all related files: +- **Purpose**: audience and compliance drivers +- **Scope**: items covered and explicitly excluded (no test projects) +- **Companion Artifact Structure**: parallel paths for requirements, design, verification, source, tests +- **References** _(if applicable)_: external standards or specifications - only in `introduction.md` -```text -In-house items have parallel artifacts in: -- Requirements: `docs/reqstream/{system-name}.yaml`, `docs/reqstream/{system-name}/.../{item}.yaml` -- Design: `docs/design/{system-name}.md`, `docs/design/{system-name}/.../{item}.md` -- Verification: `docs/verification/{system-name}.md`, `docs/verification/{system-name}/.../{item}.md` -- Source: `src/{SystemName}/.../{Item}.{ext}` (cased per language) -- Tests: `test/{SystemName}.Tests/.../{Item}Tests.{ext}` (cased per language) - -OTS items (no design documentation) have artifacts parallel to system folders: -- Requirements: `docs/reqstream/ots/{ots-name}.yaml` -- Verification: `docs/verification/ots/{ots-name}.md` -- Tests (if required): `test/{OtsSoftwareTests}/...` (cased per language - see `software-items.md`) - -Review-sets: defined in `.reviewmark.yaml` -``` +# System Verification Design (MANDATORY) -If the verification design references external documents (standards, specifications), include -a `## References` section in `introduction.md` only - do not add one to any other verification file. +Create `{system-name}.md` (`#` heading) and `{system-name}/` folder: -## System Verification Design (MANDATORY) +- **Verification Approach**: test types (unit, integration, end-to-end), framework, project structure +- **Test Environment**: OS, runtime, external services, files, or configuration required +- **Acceptance Criteria**: what constitutes a passing system test (IEC 62304 §5.7.2) +- **Test Scenarios**: named scenarios for each system requirement -For each system, create `{system-name}.md` at `docs/verification/` root and a -`{system-name}/` folder for subsystems. Cover: +# Subsystem Verification Design (MANDATORY) -- System verification strategy and overall test approach -- Test environments and configuration required -- External interface simulation and test-harness design -- End-to-end and integration test scenarios covering system requirements -- Acceptance criteria and pass/fail conditions at the system boundary -- Coverage mapping of system requirements to system-level test scenarios +Place `{subsystem-name}.md` in the **parent** folder; create `{subsystem-name}/` for children: -## Subsystem Verification Design (MANDATORY) +- **Verification Approach**: integration test approach and mocking at subsystem boundary +- **Test Environment**: any environment setup beyond the standard test runner +- **Acceptance Criteria**: what constitutes a passing subsystem test (IEC 62304 §5.5.2) +- **Test Scenarios**: named scenarios including boundary conditions, error paths, and normal operation -For each subsystem, place `{subsystem-name}.md` in the parent (system or subsystem) -folder and create a `{subsystem-name}/` folder for its units. Cover: +# Unit Verification Design (MANDATORY) -- Subsystem verification strategy and integration test approach -- Dependencies that must be mocked or stubbed at the subsystem boundary -- Integration test scenarios covering subsystem requirements -- Coverage mapping of subsystem requirements to subsystem-level test scenarios +Place `{unit-name}.md` in the **parent** folder: -## Unit Verification Design (MANDATORY) +- **Verification Approach**: what is mocked/stubbed and why; injected vs. real dependencies +- **Test Environment**: any environment setup beyond the standard test runner +- **Acceptance Criteria**: what constitutes passing unit tests (IEC 62304 §5.5.2) +- **Test Scenarios**: named scenarios including boundary values, error paths, and normal operation -Place `{unit-name}.md` in the parent (system or subsystem) folder. Cover: +# OTS Verification Evidence (when OTS items exist) -- Verification approach for each unit requirement -- Named test scenarios including boundary conditions, error paths, and normal-operation cases -- Which dependencies are mocked and how they are configured -- Coverage mapping of every unit requirement to at least one named test scenario +Create `docs/verification/ots.md` (`#` heading) covering the overall OTS verification strategy. -## OTS Verification Evidence (when OTS items are used) +For each OTS item, create `docs/verification/ots/{ots-name}.md` (`##` heading) covering: +verification approach (self-validation, integration tests, vendor evidence). -Create `docs/verification/ots.md` at the collection root with a `#` top-level heading. This -file introduces the OTS verification approach and ensures OTS items compile as a top-level -section in the PDF rather than as subsystems of the last in-house system. +# Shared Package Verification Evidence (when Shared Packages exist) -For each OTS item, create `docs/verification/ots/{ots-name}.md` covering: +Create `docs/verification/shared.md` (`#` heading) covering the overall Shared Package verification strategy. -- The OTS item's required functionality (reference `docs/reqstream/ots/{ots-name}.yaml`) -- Verification of each requirement (using self-validation evidence if appropriate) -- Coverage mapping of OTS requirements to test scenarios +For each Shared Package, create `docs/verification/shared/{package-name}.md` (`##` heading) covering: +verification approach. # Writing Guidelines -- **Test Coverage**: Map every requirement to at least one named test scenario so - reviewers can verify completeness without reading test code -- **Scenario Clarity**: Name each scenario clearly - "Valid input returns parsed result" not "Test 1" -- **Boundary Conditions**: Call out boundary values, error inputs, and edge cases explicitly -- **Isolation Strategy**: Describe what is mocked or stubbed and why at each level -- **Traceability**: Link to requirements where applicable using ReqStream patterns -- **Verbal Cross-References**: Reference other documents by name - do not use markdown - hyperlinks, which break in compiled PDFs - -Mermaid diagrams may supplement text descriptions where test flow benefits from visual -representation, but must not replace text content. +- Name scenarios clearly ("Valid input returns parsed result", not "Test 1") +- Use verbal cross-references - not markdown hyperlinks (break in PDF) +- Use Mermaid diagrams to supplement (not replace) text # Quality Checks -Before submitting verification documentation, verify: - -- [ ] Every requirement at each level is mapped to at least one named test scenario -- [ ] System verification documents cover end-to-end and integration scenarios -- [ ] Subsystem verification documents identify mocked boundaries and integration scenarios -- [ ] Unit verification documents identify individual scenarios including boundary and error paths -- [ ] Files organized under `docs/verification/` following the folder structure pattern above -- [ ] Each file's top-level heading depth matches its folder depth per the Heading Depth Rule -- [ ] All documentation folders use kebab-case names mirroring source code structure -- [ ] All documents follow technical documentation formatting standards -- [ ] Content is current with requirements and test implementation -- [ ] Every OTS item has `docs/verification/ots/{ots-name}.md` with requirement coverage -- [ ] `docs/verification/ots.md` exists with a `#` heading when OTS items are present -- [ ] Documents are integrated into ReviewMark review-sets for formal review +- [ ] `introduction.md` includes Companion Artifact Structure +- [ ] Each file's heading depth matches its folder depth +- [ ] All folders use kebab-case mirroring source structure +- [ ] Each system/subsystem/unit file includes all mandatory sections (Verification Approach, + Test Environment, Acceptance Criteria, Test Scenarios) +- [ ] Non-applicable mandatory sections contain "N/A - {justification}" +- [ ] Requirements-to-test coverage is tracked via the ReqStream trace matrix, not in these documents +- [ ] `docs/verification/ots.md` and `docs/verification/ots/{ots-name}.md` exist when OTS items are present +- [ ] `docs/verification/shared.md` and `docs/verification/shared/{package-name}.md` exist when Shared Packages are present +- [ ] Documents are integrated into ReviewMark review-sets diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 60ed015..6917bd5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -403,7 +403,8 @@ jobs: if: always() uses: actions/upload-artifact@v7 with: - name: artifacts-validation-${{ matrix.os }}-dotnet${{ matrix.dotnet-version }} + name: artifacts-validation-${{ matrix.os }}-dotnet${{ matrix.dotnet-version + }} path: artifacts/ # Builds the supporting documentation including user guides, requirements, diff --git a/.gitignore b/.gitignore index 7dc68dc..37e133c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,20 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. +# .gitignore for VersionMark (.NET / C# project) -# User-specific files -*.rsuser -*.suo +# Build outputs +bin/ +obj/ +artifacts/ + +# .NET *.user +*.suo +*.rsuser *.userosscache *.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) *.userprefs - -# Mono auto generated files mono_crash.* - -# Build results +.vs/ +*.slnx.user [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ @@ -30,17 +30,7 @@ bld/ [Ll]og/ [Ll]ogs/ -# Visual Studio cache/options directory -.vs/ - -# Visual Studio Code -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -# ReSharper is a .NET coding add-in +# ReSharper _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user @@ -57,53 +47,52 @@ _ReSharper*/ # Test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* - -# NUNIT *.VisualState.xml TestResult.xml nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c +TestResults/ +*.trx +*.xml +coverage.opencover.xml # Benchmark Results BenchmarkDotNet.Artifacts/ -# .NET +# .NET artifacts project.lock.json project.fragment.lock.json -artifacts/ -# Node.js -node_modules/ -package-lock.json -npm-debug.log +# Visual Studio Code (keep settings, tasks, launch, extensions) +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json -# Python +# Python virtual environment +.venv/ __pycache__/ *.py[cod] *$py.class -.venv/ -# Generated documentation +# Node +node_modules/ +npm-debug.log + +# Generated compliance documents (build outputs - never committed) **/generated/ -# Test results -TestResults/ -*.trx -*.xml -coverage.opencover.xml +# Agent logs +.agent-logs/ +AGENT_REPORT_*.md -# Temporary files +# OS +.DS_Store +Thumbs.db *.tmp *.temp *.log -# Agent report files -AGENT_REPORT_*.md -.agent-logs/ - # VersionMark captures (generated during CI/CD) versionmark-*.json +/package-lock.json diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml index 4942746..70592cb 100644 --- a/.markdownlint-cli2.yaml +++ b/.markdownlint-cli2.yaml @@ -1,12 +1,12 @@ --- -# Markdown Linting Standards +# Markdown Linting Configuration # # PURPOSE: # - Maintain professional technical documentation standards # - Ensure consistent formatting for readability and maintenance # - Support automated documentation generation and publishing # -# DO NOT MODIFY: These rules represent coding standards +# DO NOT MODIFY: These rules represent coding standards. # - If files fail linting, fix the files to meet these standards # - Do not relax rules to accommodate existing non-compliant files # - Consistency across repositories is critical for documentation quality @@ -18,20 +18,20 @@ noBanner: true noProgress: true config: - # Enable all default rules - default: true - # Require ATX-style headers (# Header) instead of Setext-style MD003: style: atx + # Consistent unordered list markers + MD004: + style: dash + # Set consistent indentation for nested lists MD007: indent: 2 - # Allow longer lines for URLs and technical content - MD013: - line_length: 120 + # Disable line length rule (no limit enforced) + MD013: false # Allow multiple top-level headers per document MD025: false @@ -39,17 +39,23 @@ config: # Allow inline HTML for enhanced documentation MD033: false - # Allow documents without top-level header (for fragments) + # Disable first-line-heading: subsystem and unit files intentionally start with ## + # because they are assembled by Pandoc into a single document via definition.yaml. + # In the assembled document the heading hierarchy is correct; markdownlint sees + # fragments in isolation and would otherwise raise false positives. MD041: false + # Disable table column alignment enforcement. + # Existing docs use valid pipe tables but do not require visually aligned columns. + MD060: false + # Exclude common build artifacts, dependencies, and vendored third-party code ignores: - - "**/.git/**" - - "**/node_modules/**" - - "**/.venv/**" - - "**/thirdparty/**" - - "**/third-party/**" - - "**/3rd-party/**" - - "**/generated/**" - - "**/AGENT_REPORT_*.md" - - "**/.agent-logs/**" + - '**/.git/**' + - '**/node_modules/**' + - '**/.venv/**' + - '**/thirdparty/**' + - '**/third-party/**' + - '**/3rd-party/**' + - '**/generated/**' + - '**/.agent-logs/**' diff --git a/.reviewmark.yaml b/.reviewmark.yaml index 7baa139..873f4a9 100644 --- a/.reviewmark.yaml +++ b/.reviewmark.yaml @@ -6,17 +6,18 @@ # Patterns identifying all files that require review. # Processed in order; prefix a pattern with '!' to exclude. needs-review: - - "README.md" # Root README file - - "requirements.yaml" # Root requirements file - - "**/*.cs" # All C# source and test files - - "docs/reqstream/**/*.yaml" # Requirements files - - "docs/design/**/*.md" # Design documentation files - - "docs/verification/**/*.md" # Verification documentation files - - "docs/user_guide/**/*.md" # User guide documentation - - "!**/obj/**" # Exclude build output - - "!**/bin/**" # Exclude build output - - "!node_modules/**" # Exclude npm dependencies - - "!**/.venv/**" # Exclude Python virtual environment + - README.md + - docs/user_guide/**/*.md + - '**/*.cs' + - requirements.yaml + - docs/reqstream/**/*.yaml + - docs/design/**/*.md + - docs/verification/**/*.md + - '!**/obj/**' + - '!**/bin/**' + - '!**/generated/**' + - '!**/node_modules/**' + - '!**/.venv/**' # Evidence source: review data and index.json are located in the 'reviews' branch # of this repository, accessed through the GitHub public HTTPS raw content access. @@ -30,237 +31,253 @@ reviews: - id: Purpose title: Review that Advertised Features Match System Design paths: - - "README.md" - - "docs/user_guide/**/*.md" - - "docs/reqstream/version-mark.yaml" # system requirements - - "docs/design/introduction.md" # design introduction and architecture - - "docs/design/version-mark.md" # system design + - README.md + - docs/user_guide/**/*.md + - docs/reqstream/version-mark.yaml + - docs/design/introduction.md + - docs/design/version-mark.md - # VersionMark - Specials + # VersionMark - Architecture (system-level) - id: VersionMark-Architecture title: Review that VersionMark Architecture Satisfies Requirements paths: - - "docs/reqstream/version-mark.yaml" # system requirements - - "docs/design/introduction.md" # design introduction and architecture - - "docs/design/version-mark.md" # system design - - "docs/verification/version-mark.md" # system verification - - "test/**/IntegrationTests.cs" # integration tests - - "test/**/AssemblyInfo.cs" # test infrastructure - - "test/**/Runner.cs" # test infrastructure + - docs/reqstream/version-mark.yaml + - docs/design/introduction.md + - docs/design/version-mark.md + - docs/verification/introduction.md + - docs/verification/version-mark.md + - test/**/IntegrationTests.cs + - test/**/AssemblyInfo.cs + - test/**/Runner.cs + # VersionMark - Design (consistency and completeness) - id: VersionMark-Design title: Review that VersionMark Design is Consistent and Complete paths: - - "docs/reqstream/version-mark.yaml" # system requirements - - "docs/reqstream/version-mark/platform-requirements.yaml" # platform requirements - - "docs/design/introduction.md" # design introduction - - "docs/design/version-mark/**/*.md" # all system design documents - - "docs/verification/introduction.md" # verification introduction - - "docs/verification/ots.md" # OTS verification overview + - docs/reqstream/version-mark.yaml + - docs/reqstream/version-mark/platform-requirements.yaml + - docs/design/introduction.md + - docs/design/version-mark.md + - docs/design/version-mark/**/*.md + - docs/verification/introduction.md + - docs/verification/ots.md + # VersionMark - Verification (consistency and completeness) + - id: VersionMark-Verification + title: Review that VersionMark Verification is Consistent and Complete + paths: + - docs/reqstream/version-mark.yaml + - docs/verification/introduction.md + - docs/verification/version-mark.md + - docs/verification/version-mark/**/*.md + - docs/verification/ots.md + + # VersionMark - All Requirements - id: VersionMark-AllRequirements title: Review that All VersionMark Requirements are Complete paths: - - "requirements.yaml" # root requirements file - - "docs/reqstream/**/*.yaml" # all requirements files + - requirements.yaml + - docs/reqstream/**/*.yaml # VersionMark - Cli - id: VersionMark-Cli title: Review that VersionMark Cli Satisfies Subsystem Requirements paths: - - "docs/reqstream/version-mark/cli.yaml" # subsystem requirements - - "docs/design/version-mark/cli.md" # subsystem design - - "docs/verification/version-mark/cli.md" # subsystem verification - - "test/**/Cli/CliTests.cs" # subsystem tests + - docs/reqstream/version-mark/cli.yaml + - docs/design/version-mark/cli.md + - docs/verification/version-mark/cli.md + - test/**/Cli/CliTests.cs - id: VersionMark-Cli-Program title: Review that VersionMark Cli Program Implementation is Correct paths: - - "docs/reqstream/version-mark/cli/program.yaml" # requirements - - "docs/design/version-mark/cli/program.md" # design - - "docs/verification/version-mark/cli/program.md" # verification - - "src/**/Program.cs" # implementation - - "test/**/ProgramTests.cs" # unit tests + - docs/reqstream/version-mark/cli/program.yaml + - docs/design/version-mark/cli/program.md + - docs/verification/version-mark/cli/program.md + - src/**/Program.cs + - test/**/ProgramTests.cs - id: VersionMark-Cli-Context title: Review that VersionMark Cli Context Implementation is Correct paths: - - "docs/reqstream/version-mark/cli/context.yaml" # requirements - - "docs/design/version-mark/cli/context.md" # design - - "docs/verification/version-mark/cli/context.md" # verification - - "src/**/Cli/Context.cs" # implementation - - "test/**/Cli/ContextTests.cs" # unit tests + - docs/reqstream/version-mark/cli/context.yaml + - docs/design/version-mark/cli/context.md + - docs/verification/version-mark/cli/context.md + - src/**/Cli/Context.cs + - test/**/Cli/ContextTests.cs # VersionMark - Configuration - id: VersionMark-Configuration title: Review that VersionMark Configuration Satisfies Subsystem Requirements paths: - - "docs/reqstream/version-mark/configuration.yaml" # subsystem requirements - - "docs/design/version-mark/configuration.md" # subsystem design - - "docs/verification/version-mark/configuration.md" # subsystem verification - - "test/**/Configuration/ConfigurationTests.cs" # subsystem tests + - docs/reqstream/version-mark/configuration.yaml + - docs/design/version-mark/configuration.md + - docs/verification/version-mark/configuration.md + - test/**/Configuration/ConfigurationTests.cs - id: VersionMark-Configuration-VersionMarkConfig - title: Review that VersionMark Configuration VersionMarkConfig Implementation is Correct + title: Review that VersionMark Configuration VersionMarkConfig Implementation + is Correct paths: - - "docs/reqstream/version-mark/configuration/version-mark-config.yaml" # requirements - - "docs/design/version-mark/configuration/version-mark-config.md" # design - - "docs/verification/version-mark/configuration/version-mark-config.md" # verification - - "src/**/Configuration/VersionMarkConfig.cs" # implementation - - "test/**/Configuration/VersionMarkConfigTests.cs" # unit tests - - "test/**/Configuration/VersionMarkConfigLoadTests.cs" # Load method unit tests + - docs/reqstream/version-mark/configuration/version-mark-config.yaml + - docs/design/version-mark/configuration/version-mark-config.md + - docs/verification/version-mark/configuration/version-mark-config.md + - src/**/Configuration/VersionMarkConfig.cs + - test/**/Configuration/VersionMarkConfigTests.cs + - test/**/Configuration/VersionMarkConfigLoadTests.cs - id: VersionMark-Configuration-ToolConfig title: Review that VersionMark Configuration ToolConfig Implementation is Correct paths: - - "docs/reqstream/version-mark/configuration/tool-config.yaml" # requirements - - "docs/design/version-mark/configuration/tool-config.md" # design - - "docs/verification/version-mark/configuration/tool-config.md" # verification - - "src/**/Configuration/VersionMarkConfig.cs" # implementation - - "test/**/Configuration/VersionMarkConfigTests.cs" # unit tests + - docs/reqstream/version-mark/configuration/tool-config.yaml + - docs/design/version-mark/configuration/tool-config.md + - docs/verification/version-mark/configuration/tool-config.md + - src/**/Configuration/VersionMarkConfig.cs + - test/**/Configuration/VersionMarkConfigTests.cs - id: VersionMark-Configuration-LintIssue title: Review that VersionMark Configuration LintIssue Implementation is Correct paths: - - "docs/reqstream/version-mark/configuration/load.yaml" # requirements - - "docs/design/version-mark/configuration/lint-issue.md" # design - - "docs/verification/version-mark/configuration/lint-issue.md" # verification - - "src/**/Configuration/LintIssue.cs" # implementation - - "test/**/Configuration/LintIssueTests.cs" # unit tests + - docs/reqstream/version-mark/configuration/load.yaml + - docs/design/version-mark/configuration/lint-issue.md + - docs/verification/version-mark/configuration/lint-issue.md + - src/**/Configuration/LintIssue.cs + - test/**/Configuration/LintIssueTests.cs # VersionMark - Capture - id: VersionMark-Capture title: Review that VersionMark Capture Satisfies Subsystem Requirements paths: - - "docs/reqstream/version-mark/capture.yaml" # subsystem requirements - - "docs/design/version-mark/capture.md" # subsystem design - - "docs/verification/version-mark/capture.md" # subsystem verification - - "test/**/Capture/CaptureTests.cs" # subsystem tests + - docs/reqstream/version-mark/capture.yaml + - docs/design/version-mark/capture.md + - docs/verification/version-mark/capture.md + - test/**/Capture/CaptureTests.cs - id: VersionMark-Capture-VersionInfo title: Review that VersionMark Capture VersionInfo Implementation is Correct paths: - - "docs/reqstream/version-mark/capture/version-info.yaml" # requirements - - "docs/design/version-mark/capture/version-info.md" # design - - "docs/verification/version-mark/capture/version-info.md" # verification - - "src/**/Capture/VersionInfo.cs" # implementation - - "test/**/Capture/VersionInfoTests.cs" # unit tests + - docs/reqstream/version-mark/capture/version-info.yaml + - docs/design/version-mark/capture/version-info.md + - docs/verification/version-mark/capture/version-info.md + - src/**/Capture/VersionInfo.cs + - test/**/Capture/VersionInfoTests.cs # VersionMark - Publishing - id: VersionMark-Publishing title: Review that VersionMark Publishing Satisfies Subsystem Requirements paths: - - "docs/reqstream/version-mark/publishing.yaml" # subsystem requirements - - "docs/design/version-mark/publishing.md" # subsystem design - - "docs/verification/version-mark/publishing.md" # subsystem verification - - "test/**/Publishing/PublishingTests.cs" # subsystem tests + - docs/reqstream/version-mark/publishing.yaml + - docs/design/version-mark/publishing.md + - docs/verification/version-mark/publishing.md + - test/**/Publishing/PublishingTests.cs - id: VersionMark-Publishing-MarkdownFormatter - title: Review that VersionMark Publishing MarkdownFormatter Implementation is Correct + title: Review that VersionMark Publishing MarkdownFormatter Implementation is + Correct paths: - - "docs/reqstream/version-mark/publishing/markdown-formatter.yaml" # requirements - - "docs/design/version-mark/publishing/markdown-formatter.md" # design - - "docs/verification/version-mark/publishing/markdown-formatter.md" # verification - - "src/**/Publishing/MarkdownFormatter.cs" # implementation - - "test/**/Publishing/MarkdownFormatterTests.cs" # unit tests + - docs/reqstream/version-mark/publishing/markdown-formatter.yaml + - docs/design/version-mark/publishing/markdown-formatter.md + - docs/verification/version-mark/publishing/markdown-formatter.md + - src/**/Publishing/MarkdownFormatter.cs + - test/**/Publishing/MarkdownFormatterTests.cs # VersionMark - SelfTest - id: VersionMark-SelfTest title: Review that VersionMark SelfTest Satisfies Subsystem Requirements paths: - - "docs/reqstream/version-mark/self-test.yaml" # subsystem requirements - - "docs/design/version-mark/self-test.md" # subsystem design - - "docs/verification/version-mark/self-test.md" # subsystem verification - - "test/**/SelfTest/SelfTestTests.cs" # subsystem tests + - docs/reqstream/version-mark/self-test.yaml + - docs/design/version-mark/self-test.md + - docs/verification/version-mark/self-test.md + - test/**/SelfTest/SelfTestTests.cs - id: VersionMark-SelfTest-Validation title: Review that VersionMark SelfTest Validation Implementation is Correct paths: - - "docs/reqstream/version-mark/self-test/validation.yaml" # requirements - - "docs/design/version-mark/self-test/validation.md" # design - - "docs/verification/version-mark/self-test/validation.md" # verification - - "src/**/SelfTest/Validation.cs" # implementation - - "test/**/SelfTest/SelfTestTests.cs" # subsystem tests (no separate unit test file) + - docs/reqstream/version-mark/self-test/validation.yaml + - docs/design/version-mark/self-test/validation.md + - docs/verification/version-mark/self-test/validation.md + - src/**/SelfTest/Validation.cs + - test/**/SelfTest/SelfTestTests.cs # VersionMark - Utilities - id: VersionMark-Utilities title: Review that VersionMark Utilities Satisfies Subsystem Requirements paths: - - "docs/reqstream/version-mark/utilities.yaml" - - "docs/design/version-mark/utilities.md" - - "docs/verification/version-mark/utilities.md" - - "test/**/Utilities/GlobMatcherTests.cs" # subsystem tests (no separate subsystem test file needed) - - "test/**/Utilities/PathHelpersTests.cs" # subsystem tests + - docs/reqstream/version-mark/utilities.yaml + - docs/design/version-mark/utilities.md + - docs/verification/version-mark/utilities.md + - test/**/Utilities/GlobMatcherTests.cs + - test/**/Utilities/PathHelpersTests.cs - id: VersionMark-Utilities-GlobMatcher title: Review that VersionMark Utilities GlobMatcher Implementation is Correct paths: - - "docs/reqstream/version-mark/utilities/glob-matcher.yaml" - - "docs/design/version-mark/utilities/glob-matcher.md" - - "docs/verification/version-mark/utilities/glob-matcher.md" - - "src/**/Utilities/GlobMatcher.cs" - - "test/**/Utilities/GlobMatcherTests.cs" + - docs/reqstream/version-mark/utilities/glob-matcher.yaml + - docs/design/version-mark/utilities/glob-matcher.md + - docs/verification/version-mark/utilities/glob-matcher.md + - src/**/Utilities/GlobMatcher.cs + - test/**/Utilities/GlobMatcherTests.cs - id: VersionMark-Utilities-PathHelpers title: Review that VersionMark Utilities PathHelpers Implementation is Correct paths: - - "docs/reqstream/version-mark/utilities/path-helpers.yaml" # requirements - - "docs/design/version-mark/utilities/path-helpers.md" # design - - "docs/verification/version-mark/utilities/path-helpers.md" # verification - - "src/**/Utilities/PathHelpers.cs" # implementation - - "test/**/Utilities/PathHelpersTests.cs" # unit tests + - docs/reqstream/version-mark/utilities/path-helpers.yaml + - docs/design/version-mark/utilities/path-helpers.md + - docs/verification/version-mark/utilities/path-helpers.md + - src/**/Utilities/PathHelpers.cs + - test/**/Utilities/PathHelpersTests.cs # OTS Items - id: OTS-BuildMark title: Review of BuildMark OTS verification evidence paths: - - "docs/reqstream/ots/buildmark.yaml" - - "docs/verification/ots/buildmark.md" + - docs/reqstream/ots/buildmark.yaml + - docs/verification/ots/buildmark.md - id: OTS-FileAssert title: Review of FileAssert OTS verification evidence paths: - - "docs/reqstream/ots/fileassert.yaml" - - "docs/verification/ots/fileassert.md" + - docs/reqstream/ots/fileassert.yaml + - docs/verification/ots/fileassert.md - id: OTS-xUnit title: Review of xUnit OTS verification evidence paths: - - "docs/reqstream/ots/xunit.yaml" - - "docs/verification/ots/xunit.md" + - docs/reqstream/ots/xunit.yaml + - docs/verification/ots/xunit.md - id: OTS-Pandoc title: Review of Pandoc OTS verification evidence paths: - - "docs/reqstream/ots/pandoc.yaml" - - "docs/verification/ots/pandoc.md" + - docs/reqstream/ots/pandoc.yaml + - docs/verification/ots/pandoc.md - id: OTS-ReqStream title: Review of ReqStream OTS verification evidence paths: - - "docs/reqstream/ots/reqstream.yaml" - - "docs/verification/ots/reqstream.md" + - docs/reqstream/ots/reqstream.yaml + - docs/verification/ots/reqstream.md - id: OTS-ReviewMark title: Review of ReviewMark OTS verification evidence paths: - - "docs/reqstream/ots/reviewmark.yaml" - - "docs/verification/ots/reviewmark.md" + - docs/reqstream/ots/reviewmark.yaml + - docs/verification/ots/reviewmark.md - id: OTS-SarifMark title: Review of SarifMark OTS verification evidence paths: - - "docs/reqstream/ots/sarifmark.yaml" - - "docs/verification/ots/sarifmark.md" + - docs/reqstream/ots/sarifmark.yaml + - docs/verification/ots/sarifmark.md - id: OTS-SonarMark title: Review of SonarMark OTS verification evidence paths: - - "docs/reqstream/ots/sonarmark.yaml" - - "docs/verification/ots/sonarmark.md" + - docs/reqstream/ots/sonarmark.yaml + - docs/verification/ots/sonarmark.md - id: OTS-WeasyPrint title: Review of WeasyPrint OTS verification evidence paths: - - "docs/reqstream/ots/weasyprint.yaml" - - "docs/verification/ots/weasyprint.md" + - docs/reqstream/ots/weasyprint.yaml + - docs/verification/ots/weasyprint.md diff --git a/.versionmark.yaml b/.versionmark.yaml index 9275778..09bb0eb 100644 --- a/.versionmark.yaml +++ b/.versionmark.yaml @@ -6,69 +6,69 @@ tools: # .NET SDK dotnet: command: dotnet --version - regex: '(?\d+\.\d+\.\d+(?:\.\d+)?)' + regex: (?\d+\.\d+\.\d+(?:\.\d+)?) # Git git: command: git --version - regex: '(?i)git version (?\d+\.\d+\.\d+)' + regex: (?i)git version (?\d+\.\d+\.\d+) # Node.js node: command: node --version - regex: '(?i)v(?\d+\.\d+\.\d+)' + regex: (?i)v(?\d+\.\d+\.\d+) # npm npm: command: npm --version - regex: '(?\d+\.\d+\.\d+)' + regex: (?\d+\.\d+\.\d+) # SonarScanner for .NET (from dotnet tool list) dotnet-sonarscanner: command: dotnet tool list - regex: '(?i)dotnet-sonarscanner\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)' + regex: (?i)dotnet-sonarscanner\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?) # Pandoc (DemaConsulting.PandocTool from dotnet tool list) pandoc: command: dotnet tool list - regex: '(?i)demaconsulting\.pandoctool\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)' + regex: (?i)demaconsulting\.pandoctool\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?) # WeasyPrint (DemaConsulting.WeasyPrintTool from dotnet tool list) weasyprint: command: dotnet tool list - regex: '(?i)demaconsulting\.weasyprinttool\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)' + regex: (?i)demaconsulting\.weasyprinttool\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?) # SarifMark (DemaConsulting.SarifMark from dotnet tool list) sarifmark: command: dotnet tool list - regex: '(?i)demaconsulting\.sarifmark\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)' + regex: (?i)demaconsulting\.sarifmark\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?) # SonarMark (DemaConsulting.SonarMark from dotnet tool list) sonarmark: command: dotnet tool list - regex: '(?i)demaconsulting\.sonarmark\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)' + regex: (?i)demaconsulting\.sonarmark\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?) # ReqStream (DemaConsulting.ReqStream from dotnet tool list) reqstream: command: dotnet tool list - regex: '(?i)demaconsulting\.reqstream\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)' + regex: (?i)demaconsulting\.reqstream\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?) # BuildMark (DemaConsulting.BuildMark from dotnet tool list) buildmark: command: dotnet tool list - regex: '(?i)demaconsulting\.buildmark\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)' + regex: (?i)demaconsulting\.buildmark\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?) # VersionMark (DemaConsulting.VersionMark from dotnet tool list) versionmark: command: dotnet tool list - regex: '(?i)demaconsulting\.versionmark\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)' + regex: (?i)demaconsulting\.versionmark\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?) # ReviewMark (DemaConsulting.ReviewMark from dotnet tool list) reviewmark: command: dotnet tool list - regex: '(?i)demaconsulting\.reviewmark\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)' + regex: (?i)demaconsulting\.reviewmark\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?) # FileAssert (DemaConsulting.FileAssert from dotnet tool list) fileassert: command: dotnet tool list - regex: '(?i)demaconsulting\.fileassert\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)' + regex: (?i)demaconsulting\.fileassert\s+(?\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?) diff --git a/.yamlfix.toml b/.yamlfix.toml index 233d73b..7b057b4 100644 --- a/.yamlfix.toml +++ b/.yamlfix.toml @@ -2,15 +2,12 @@ # # PURPOSE: # - Configure yamlfix to auto-fix common YAML issues before yamllint checks -# - Settings MUST align with .yamllint.yaml to avoid conflicts -# - Use 'pwsh ./fix.ps1' or run 'yamlfix .' before 'pwsh ./lint.ps1' to auto-fix, then verify with yamllint +# - Settings align with .yamllint.yaml to avoid conflicts +# - Use 'pwsh ./fix.ps1' or run 'yamlfix .' before 'pwsh ./lint.ps1' to auto-fix # -# RELATIONSHIP TO YAMLLINT: -# - line_length MUST match .yamllint.yaml line-length.max (120) -# - preserve_quotes prevents mangling GitHub Actions 'on:' boolean handling -# - After yamlfix auto-fix, yamllint should only fail on genuinely unresolvable issues +# MODIFICATION POLICY: +# Do not modify these rules except to align with .yamllint.yaml changes. -line_length = 120 preserve_quotes = true sequence_style = "keep_style" whitelines = 1 diff --git a/.yamllint.yaml b/.yamllint.yaml index 79c3aee..c8e1350 100644 --- a/.yamllint.yaml +++ b/.yamllint.yaml @@ -6,7 +6,7 @@ # - Support CI/CD workflows with reliable YAML parsing # - Ensure professional documentation and configuration files # -# DO NOT MODIFY: These rules represent coding standards +# DO NOT MODIFY: These rules represent coding standards. # - If files fail linting, fix the files to meet these standards # - Do not relax rules to accommodate existing non-compliant files # - Consistency across repositories is critical for maintainability @@ -27,12 +27,11 @@ ignore: | rules: # Allow 'on:' in GitHub Actions workflows (not a boolean value) truthy: - allowed-values: ['true', 'false', 'on', 'off'] + allowed-values: ['true', 'false'] + check-keys: false - # Allow longer lines for URLs and complex expressions - line-length: - max: 120 - level: error + # Disable line-length rule + line-length: disable # Ensure proper indentation indentation: diff --git a/AGENTS.md b/AGENTS.md index 6776bfe..6ab2319 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,8 @@ # Project Overview -- **name**: VersionMark +- **project-name**: VersionMark +- **organization**: DEMA Consulting +- **project-tagline**: A .NET tool for CI/CD tool version tracking - **description**: A .NET tool for capturing and publishing tool version information across CI/CD environments. It tracks which versions of build tools, compilers, and dependencies are used in different jobs and environments. @@ -27,6 +29,19 @@ └── DemaConsulting.VersionMark.Tests/ ``` +# Language and Spelling (ALL Agents) + +Always use **US English** spelling in all output (code, comments, documentation, +commit messages, and reports). + +# Reference Template + +This repository follows a reference template for structure and file conventions. + +- **Template URL**: `https://github.com/demaconsulting/Agents/raw/refs/heads/template` +- **Repository map**: `{template-url}/repository-map.md` +- **Template files**: `{template-url}/{file-path}` for files described in the map + # Codebase Navigation (ALL Agents) When working with source code, design, or requirements artifacts, read @@ -55,17 +70,15 @@ before searching the filesystem. Before performing any work, agents must read and apply the relevant standards from `.github/standards/`. Use this matrix to determine which to load: -| Work involves... | Load these standards | -|----------------------|------------------------------------------------------------------------------------| -| Any code | `coding-principles.md` | -| C# code | `coding-principles.md`, `csharp-language.md` | -| Any tests | `testing-principles.md` | -| C# tests | `testing-principles.md`, `csharp-testing.md` | -| Requirements | `requirements-principles.md`, `software-items.md`, `reqstream-usage.md` | -| Design docs | `software-items.md`, `design-documentation.md`, `technical-documentation.md` | -| Verification docs | `software-items.md`, `verification-documentation.md`, `technical-documentation.md` | -| Review configuration | `software-items.md`, `reviewmark-usage.md` | -| Any documentation | `technical-documentation.md` | +- **Any code**: `coding-principles.md` +- **C# code**: `coding-principles.md`, `csharp-language.md` +- **Any tests**: `testing-principles.md` +- **C# tests**: `testing-principles.md`, `csharp-testing.md` +- **Requirements**: `requirements-principles.md`, `software-items.md`, `reqstream-usage.md` +- **Design docs**: `software-items.md`, `design-documentation.md`, `technical-documentation.md` +- **Verification docs**: `software-items.md`, `verification-documentation.md`, `technical-documentation.md` +- **Review configuration**: `software-items.md`, `reviewmark-usage.md` +- **Any documentation**: `technical-documentation.md` Load only the standards relevant to your specific task scope. @@ -80,6 +93,7 @@ Delegate to specialized agents only for specific scenarios: - **Formal feature implementation** (complex, multi-step) → Call the implementation agent - **Formal bug resolution** (complex debugging, systematic fixes) → Call the implementation agent - **Formal reviews** (compliance verification, detailed analysis) → Call the formal-review agent +- **Structural audit**: (repository layout vs. template) → Call the template-sync agent # Agent Reporting (Specialized Agents Must Follow) diff --git a/README.md b/README.md index 62a5edd..5f81464 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,11 @@ [![Security][badge-security]][link-security] [![NuGet][badge-nuget]][link-nuget] -VersionMark is a tool for capturing and publishing tool version information across CI/CD -environments. It helps track which versions of build tools, compilers, and dependencies are -used in different jobs and environments. +## Overview + +VersionMark is a .NET tool for capturing and publishing tool version information across CI/CD environments. +It tracks which versions of build tools, compilers, and dependencies are used in different jobs and +environments, and publishes consolidated version reports to markdown documentation. ## Features @@ -21,22 +23,17 @@ used in different jobs and environments. - **Job-ID Tracking**: Associates captured versions with specific CI/CD job identifiers - **Version Consolidation**: Collapses common versions across jobs while highlighting conflicts - **OS-Specific Overrides**: Supports platform-specific version capture commands -- **Configurable**: Uses `.versionmark.yaml` config file to define tools and capture methods -- **Multi-Platform Support**: Runs on Windows, Linux, and macOS -- **Multi-Runtime Support**: Targets .NET 8, 9, and 10 -- **Self-Validation**: Runs built-in self-verification tests to confirm the tool is functioning correctly +- **Self-Validation**: Runs built-in self-verification tests to confirm correct operation - **Continuous Compliance**: Compliance evidence generated automatically on every CI run, following - the [Continuous Compliance][link-continuous-compliance] methodology + the [Continuous Compliance](https://github.com/demaconsulting/ContinuousCompliance) methodology ## Installation -Install the tool globally using the .NET CLI: - ```bash dotnet tool install -g DemaConsulting.VersionMark ``` -## Quick Start +## Usage ### 1. Create Configuration File @@ -47,7 +44,7 @@ tools: dotnet: command: dotnet --version regex: '(?\d+\.\d+\.\d+)' - + node: command: node --version regex: 'v(?\d+\.\d+\.\d+)' @@ -71,7 +68,32 @@ After all jobs complete, publish the captured versions to markdown: versionmark --publish --report versions.md ``` -This consolidates versions from all jobs and generates a markdown report. +## Building + +```pwsh +pwsh ./build.ps1 +``` + +## User Guide + +The VersionMark User Guide is available on the [VersionMark releases page](https://github.com/demaconsulting/VersionMark/releases). + +## Contributing + +See [CONTRIBUTING.md](https://github.com/demaconsulting/VersionMark/blob/main/CONTRIBUTING.md) for +guidelines on how to contribute to this project. + +## License + +Copyright (c) DEMA Consulting. Licensed under the MIT License. +See [LICENSE](https://github.com/demaconsulting/VersionMark/blob/main/LICENSE) for details. + +By contributing to this project, you agree that your contributions will be licensed under the MIT License. + +## Support + +- [Report a bug or request a feature](https://github.com/demaconsulting/VersionMark/issues) +- [Ask a question or start a discussion](https://github.com/demaconsulting/VersionMark/discussions) ## Command-Line Options @@ -116,7 +138,7 @@ tools: dotnet: command: dotnet --version regex: '(?\d+\.\d+\.\d+)' - + # Tool with OS-specific overrides gcc: command: gcc --version @@ -124,22 +146,14 @@ tools: command-linux: gcc-13 --version command-macos: gcc-14 --version regex: 'gcc \(.*\) (?\d+\.\d+\.\d+)' - regex-win: 'gcc\.exe \(.*\) (?\d+\.\d+\.\d+)' - regex-linux: 'gcc-13 \(.*\) (?\d+\.\d+\.\d+)' - - # Tool with custom output parsing - cmake: - command: cmake --version - regex: 'cmake version (?\d+\.\d+\.\d+)' ``` ### Configuration Options -Each tool in the `tools` dictionary has the following properties: +Each tool in the `tools` dictionary supports the following properties: -- **command**: Shell command to execute to get version information (required unless OS-specific variants are provided) -- **regex**: Regular expression with a named 'version' capture group using .NET syntax - `(?...)` (required unless OS-specific variants are provided) +- **command**: Shell command to execute to get version information +- **regex**: Regular expression with a named `version` capture group using .NET syntax `(?...)` - **command-win**: (Optional) Command override for Windows - **command-linux**: (Optional) Command override for Linux - **command-macos**: (Optional) Command override for macOS @@ -158,8 +172,7 @@ Capture mode creates a JSON file with the following structure: "JobId": "windows-net8", "Versions": { "dotnet": "8.0.100", - "node": "20.11.0", - "gcc": "13.2.0" + "node": "20.11.0" } } ``` @@ -172,83 +185,29 @@ Publish mode generates a markdown list consolidating versions from all jobs: # Tool Versions - **dotnet**: 8.0.100 -- **gcc**: 11.4.0 (windows-net8) -- **gcc**: 13.2.0 (linux-net8) - **node**: 20.11.0 ``` -When a tool has the same version across all jobs, it's shown without job identifiers. When versions -differ, each version is listed on a separate line with the jobs that use it shown in parentheses. +When a tool has the same version across all jobs, it is shown without job identifiers. +When versions differ, each version is listed with the jobs that use it shown in parentheses. -## Self Validation +## Self-Validation -VersionMark includes built-in self-validation tests that verify the tool is working correctly in -its current environment. Run with `--validate`: +VersionMark includes built-in self-validation tests. Run with `--validate`: ```bash versionmark --validate ``` -Use `--depth` to control the heading depth of the report (default: 1): - -```bash -versionmark --validate --depth 2 -``` - -Example output: - -```text -# DEMA Consulting VersionMark - -| Information | Value | -| :------------------ | :------------------------------------------------- | -| Tool Version | 1.2.3 | -| Machine Name | build-agent-01 | -| OS Version | Ubuntu 22.04.3 LTS | -| DotNet Runtime | .NET 10.0.0 | -| Time Stamp | 2025-01-01 12:00:00 UTC | - -✓ VersionMark_CapturesVersions - Passed -✓ VersionMark_GeneratesMarkdownReport - Passed -✓ VersionMark_LintPassesForValidConfig - Passed -✓ VersionMark_LintReportsErrorsForInvalidConfig - Passed - -Total Tests: 4 -Passed: 4 -Failed: 0 -``` - -To save results in TRX format (Visual Studio test results): +Use `--results` to save results in TRX or JUnit XML format: ```bash versionmark --validate --results results.trx -``` - -To save results in JUnit XML format: - -```bash versionmark --validate --results results.xml ``` If any tests fail, the exit code will be non-zero. -## Documentation - -Generated documentation includes: - -- **Build Notes**: Release information and changes -- **User Guide**: Comprehensive usage documentation -- **Code Quality Report**: CodeQL and SonarCloud analysis results -- **Requirements**: Functional and non-functional requirements -- **Requirements Justifications**: Detailed requirement rationale -- **Trace Matrix**: Requirements to test traceability - -## License - -Copyright (c) DEMA Consulting. Licensed under the MIT License. See [LICENSE][link-license] for details. - -By contributing to this project, you agree that your contributions will be licensed under the MIT License. - [badge-forks]: https://img.shields.io/github/forks/demaconsulting/VersionMark?style=plastic [badge-stars]: https://img.shields.io/github/stars/demaconsulting/VersionMark?style=plastic @@ -268,4 +227,3 @@ By contributing to this project, you agree that your contributions will be licen [link-quality]: https://sonarcloud.io/dashboard?id=demaconsulting_VersionMark [link-security]: https://sonarcloud.io/dashboard?id=demaconsulting_VersionMark [link-nuget]: https://www.nuget.org/packages/DemaConsulting.VersionMark -[link-continuous-compliance]: https://github.com/demaconsulting/ContinuousCompliance diff --git a/build.ps1 b/build.ps1 index 188d18f..0bbb0a8 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,8 +1,8 @@ # build.ps1 # # PURPOSE: -# Unified cross-platform build script (replaces build.bat and build.sh). -# Builds the solution in Release configuration and runs all unit tests. +# Unified cross-platform build script. Builds the solution in Release configuration +# and runs all unit tests. # # EXTENSION POINTS: # Search for "[PROJECT-SPECIFIC]" comments to find the designated locations @@ -12,18 +12,22 @@ # Only modify this file to add project-specific operations at the designated # [PROJECT-SPECIFIC] extension points. -$ErrorActionPreference = 'Stop' +$buildError = $false -Write-Host "Building project..." -dotnet build --configuration Release -if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +Write-Host "Restoring dependencies..." +dotnet restore +if ($LASTEXITCODE -ne 0) { $buildError = $true } + +Write-Host "Building..." +dotnet build --no-restore --configuration Release +if ($LASTEXITCODE -ne 0) { $buildError = $true } # [PROJECT-SPECIFIC] Add additional build steps here. -Write-Host "Running unit tests..." -dotnet test --configuration Release -if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +Write-Host "Running tests..." +dotnet test --no-build --configuration Release --logger trx --results-directory artifacts/tests +if ($LASTEXITCODE -ne 0) { $buildError = $true } # [PROJECT-SPECIFIC] Add additional test or post-build steps here. -Write-Host "Build and tests completed successfully!" +exit ($buildError ? 1 : 0) diff --git a/docs/build_notes/definition.yaml b/docs/build_notes/definition.yaml index ba1360b..99131c3 100644 --- a/docs/build_notes/definition.yaml +++ b/docs/build_notes/definition.yaml @@ -1,12 +1,10 @@ --- -resource-path: - - docs/build_notes - - docs/template +resource-path: [docs/build_notes, docs/template] input-files: - docs/build_notes/title.txt - docs/build_notes/introduction.md - - docs/build_notes/generated/build_notes.md - - docs/build_notes/generated/versions.md + - docs/build_notes/generated/build_notes.md # Generated by BuildMark (git history, release notes) + - docs/build_notes/generated/versions.md # Generated by VersionMark (tool versions) template: template.html table-of-contents: true number-sections: true diff --git a/docs/build_notes/introduction.md b/docs/build_notes/introduction.md index 934120f..6108a65 100644 --- a/docs/build_notes/introduction.md +++ b/docs/build_notes/introduction.md @@ -1,33 +1,23 @@ # Introduction -This document contains the build notes for the VersionMark project. +This document contains the build notes for VersionMark. ## Purpose -This report serves as a comprehensive record of changes and bug fixes for this -release of VersionMark. It provides transparency about what has changed since the -previous version and helps users understand the improvements and fixes included -in this build. +This document provides a record of the changes, new features, and bug fixes included +in this release of VersionMark. It also records the versions of all tools used in +the build pipeline, providing traceability between the software artifacts and the +environment that produced them. ## Scope This build notes report covers: -- Version information and commit details -- Changes and new features implemented +- Version information and commit details for this release +- Changes and new features implemented since the previous version - Bugs fixed in this release +- Versions of all tools used in the build and compliance pipeline -## Generation Source +## References -This report is automatically generated by the BuildMark tool, analyzing the -Git repository history and issue tracking information. It serves as evidence of -changes and improvements included in this release. - -## Audience - -This document is intended for: - -- Software developers working on VersionMark -- Users evaluating what has changed in this release -- Project stakeholders tracking progress -- Contributors understanding recent changes +N/A diff --git a/docs/build_notes/title.txt b/docs/build_notes/title.txt index 17706b9..11fe254 100644 --- a/docs/build_notes/title.txt +++ b/docs/build_notes/title.txt @@ -1,14 +1,13 @@ --- -title: VersionMark -subtitle: Build Notes -author: DEMA Consulting -description: Build notes for the VersionMark reference implementation +title: "VersionMark Build Notes" +subtitle: "A .NET tool for CI/CD tool version tracking" +author: "DEMA Consulting" +description: "Build Notes for VersionMark" lang: en-US keywords: - - VersionMark - Build Notes - - Release Notes + - VersionMark - C# - .NET - - Documentation ---- + - Release Notes +--- \ No newline at end of file diff --git a/docs/code_quality/definition.yaml b/docs/code_quality/definition.yaml index fed5f02..f1dde0b 100644 --- a/docs/code_quality/definition.yaml +++ b/docs/code_quality/definition.yaml @@ -1,7 +1,5 @@ --- -resource-path: - - docs/code_quality - - docs/template +resource-path: [docs/code_quality, docs/template] input-files: - docs/code_quality/title.txt - docs/code_quality/introduction.md diff --git a/docs/code_quality/introduction.md b/docs/code_quality/introduction.md index 01962e4..43edd84 100644 --- a/docs/code_quality/introduction.md +++ b/docs/code_quality/introduction.md @@ -1,35 +1,17 @@ # Introduction -This document contains the code quality analysis report for the VersionMark project. +This document records the static analysis results for VersionMark. ## Purpose -This report provides a comprehensive analysis of the VersionMark codebase including quality gate status, -code issues, and security hot-spots. The analysis helps ensure code quality and security standards are -maintained throughout development. +To provide evidence that VersionMark has been analyzed for code quality issues +and that any findings have been reviewed and resolved or accepted. ## Scope -This code quality report covers: +Covers static analysis of all source code in `src/` for VersionMark. +Test code is excluded from static analysis requirements. -- Quality gate status and conditions -- Code issues categorized by type and severity -- Security hot-spots requiring review -- Security analysis results -- Technical debt assessment -- Code coverage and duplication metrics +## References -## Analysis Source - -This report contains quality analysis results captured at the time this version of VersionMark was built. -It serves as evidence that the code maintains good quality standards and provides transparency about the -project's code health. The analysis includes results from various quality tools run during the build process. - -## Audience - -This document is intended for: - -- Software developers working on VersionMark -- Quality assurance teams reviewing code quality -- Project stakeholders evaluating project health -- Contributors understanding quality standards +N/A diff --git a/docs/code_quality/title.txt b/docs/code_quality/title.txt index d76e906..061ebae 100644 --- a/docs/code_quality/title.txt +++ b/docs/code_quality/title.txt @@ -1,13 +1,13 @@ --- -title: VersionMark Code Quality Report -subtitle: Code Quality Analysis Report -author: DEMA Consulting -description: Code Quality Analysis Report for VersionMark +title: "VersionMark Code Quality Report" +subtitle: "A .NET tool for CI/CD tool version tracking" +author: "DEMA Consulting" +description: "Code Quality Report for VersionMark" lang: en-US keywords: - - VersionMark - Code Quality - - Analysis + - Static Analysis + - VersionMark + - C# - .NET - - Documentation ---- +--- \ No newline at end of file diff --git a/docs/code_review_plan/definition.yaml b/docs/code_review_plan/definition.yaml index 56989bf..5b612b8 100644 --- a/docs/code_review_plan/definition.yaml +++ b/docs/code_review_plan/definition.yaml @@ -1,11 +1,9 @@ --- -resource-path: - - docs/code_review_plan - - docs/template +resource-path: [docs/code_review_plan, docs/template] input-files: - docs/code_review_plan/title.txt - docs/code_review_plan/introduction.md - - docs/code_review_plan/generated/plan.md + - docs/code_review_plan/generated/plan.md # Generated by ReviewMark (formal review plan) template: template.html table-of-contents: true number-sections: true diff --git a/docs/code_review_plan/introduction.md b/docs/code_review_plan/introduction.md index d5f3321..76e3d2c 100644 --- a/docs/code_review_plan/introduction.md +++ b/docs/code_review_plan/introduction.md @@ -1,31 +1,16 @@ # Introduction -This document contains the review plan for the VersionMark project. +This document defines the formal code review plan for VersionMark. ## Purpose -This review plan provides a comprehensive overview of all files requiring formal review -in the VersionMark project. It identifies which review-sets cover which files and serves -as evidence that every file requiring review is covered by at least one named review-set. +To define review-sets and the evidence required to demonstrate that each software item +has been reviewed according to the project's compliance requirements. ## Scope -This review plan covers: +This document covers all review-sets defined in `.reviewmark.yaml` for VersionMark. -- C# source code files requiring formal review -- Mapping of C# source files to named review-sets +## References -## Generation Source - -This plan is automatically generated by the ReviewMark tool, analyzing the -`.reviewmark.yaml` configuration and the review evidence store. It serves as evidence -that every file requiring review is covered by a current, valid review. - -## Audience - -This document is intended for: - -- Software developers working on VersionMark -- Quality assurance teams validating review coverage -- Project stakeholders reviewing compliance status -- Auditors verifying that all required files have been reviewed +N/A diff --git a/docs/code_review_plan/title.txt b/docs/code_review_plan/title.txt index 6a3332d..fa84df0 100644 --- a/docs/code_review_plan/title.txt +++ b/docs/code_review_plan/title.txt @@ -1,13 +1,12 @@ --- -title: VersionMark Review Plan -subtitle: File Review Plan for VersionMark -author: DEMA Consulting -description: File Review Plan for VersionMark +title: "VersionMark Code Review Plan" +subtitle: "A .NET tool for CI/CD tool version tracking" +author: "DEMA Consulting" +description: "Code Review Plan for VersionMark" lang: en-US keywords: + - Code Review - VersionMark - - Review Plan - - File Reviews + - C# - .NET - - Tool ---- +--- \ No newline at end of file diff --git a/docs/code_review_report/definition.yaml b/docs/code_review_report/definition.yaml index b238d43..bc8c94d 100644 --- a/docs/code_review_report/definition.yaml +++ b/docs/code_review_report/definition.yaml @@ -1,11 +1,9 @@ --- -resource-path: - - docs/code_review_report - - docs/template +resource-path: [docs/code_review_report, docs/template] input-files: - docs/code_review_report/title.txt - docs/code_review_report/introduction.md - - docs/code_review_report/generated/report.md + - docs/code_review_report/generated/report.md # Generated by ReviewMark (completed review records) template: template.html table-of-contents: true number-sections: true diff --git a/docs/code_review_report/introduction.md b/docs/code_review_report/introduction.md index 24408fe..3731faa 100644 --- a/docs/code_review_report/introduction.md +++ b/docs/code_review_report/introduction.md @@ -1,32 +1,16 @@ # Introduction -This document contains the review report for the VersionMark project. +This document records the completed formal code reviews for VersionMark. ## Purpose -This review report provides evidence that each review-set is current — the review -evidence matches the current file fingerprints. It confirms that all formal reviews -conducted for VersionMark remain valid for the current state of the reviewed files. +To provide compliance evidence that all required review-sets have been reviewed +and approved according to the project's review plan. ## Scope -This review report covers: +This document covers all completed reviews for review-sets defined in `.reviewmark.yaml`. -- Current review-set status (current, stale, or missing) -- File fingerprints and review evidence matching -- Review coverage verification +## References -## Generation Source - -This report is automatically generated by the ReviewMark tool, comparing the current -file fingerprints against the review evidence store. It serves as evidence that all -review-sets are current and no reviewed file has changed since its review was conducted. - -## Audience - -This document is intended for: - -- Software developers working on VersionMark -- Quality assurance teams validating review currency -- Project stakeholders reviewing compliance status -- Auditors verifying that all reviews remain valid for the current release +[VersionMark releases](https://github.com/demaconsulting/VersionMark/releases) diff --git a/docs/code_review_report/title.txt b/docs/code_review_report/title.txt index eb6a451..33a8d6b 100644 --- a/docs/code_review_report/title.txt +++ b/docs/code_review_report/title.txt @@ -1,13 +1,12 @@ --- -title: VersionMark Review Report -subtitle: File Review Report for VersionMark -author: DEMA Consulting -description: File Review Report for VersionMark +title: "VersionMark Code Review Report" +subtitle: "A .NET tool for CI/CD tool version tracking" +author: "DEMA Consulting" +description: "Code Review Report for VersionMark" lang: en-US keywords: + - Code Review - VersionMark - - Review Report - - File Reviews + - C# - .NET - - Tool ---- +--- \ No newline at end of file diff --git a/docs/design/definition.yaml b/docs/design/definition.yaml index 4874a70..9a3d307 100644 --- a/docs/design/definition.yaml +++ b/docs/design/definition.yaml @@ -10,9 +10,9 @@ input-files: - docs/design/version-mark/cli/program.md - docs/design/version-mark/cli/context.md - docs/design/version-mark/configuration.md + - docs/design/version-mark/configuration/lint-issue.md - docs/design/version-mark/configuration/tool-config.md - docs/design/version-mark/configuration/version-mark-config.md - - docs/design/version-mark/configuration/lint-issue.md - docs/design/version-mark/capture.md - docs/design/version-mark/capture/version-info.md - docs/design/version-mark/publishing.md diff --git a/docs/design/introduction.md b/docs/design/introduction.md index e56fad0..b620db8 100644 --- a/docs/design/introduction.md +++ b/docs/design/introduction.md @@ -1,38 +1,47 @@ # Introduction -This document describes the internal design of the VersionMark .NET tool. It provides a -structured account of the key components, their responsibilities, and how they interact to -deliver the tool's capabilities. +VersionMark is a .NET global tool that captures tool version information from CI/CD job +environments and publishes consolidated version reports as markdown. This document describes +its internal design, organized as a single system with six subsystems — Cli, Configuration, +Capture, Publishing, SelfTest, and Utilities — each containing one or more units. ## Purpose -The purpose of this document is to: - -- Describe the design decisions and structure of the VersionMark tool -- Provide a reference for developers contributing to or reviewing the tool -- Establish traceability between requirements and the components that fulfil them -- Document each conceptual group in sufficient detail to support code review +This document defines the full architectural and detailed design for every local software +item in VersionMark — the VersionMark system, its six subsystems, and their units. A +reviewer should be able to understand how each item fulfills its requirements without +reading source code. This document is intended for software developers implementing features +or fixing defects, reviewers conducting formal design and code reviews, and quality assurance +engineers tracing requirements to implementation. Readers are assumed to be familiar with C# +and .NET development and general concepts of command-line tool design. ## Scope -This document covers the design of six subsystems within VersionMark: +This document covers the design of the VersionMark system and its six subsystems: -- The **Cli Subsystem**: the `Program` entry point and `Context` class - that handle argument parsing, output routing, and program flow control -- The **Configuration Subsystem**: the `VersionMarkConfig`, `ToolConfig`, and `LintIssue` classes that - read, validate, and interpret `.versionmark.yaml` configuration files +- The **Cli Subsystem**: the `Program` entry point and `Context` class that handle argument + parsing, output routing, and program flow control +- The **Configuration Subsystem**: the `VersionMarkConfig`, `ToolConfig`, and `LintIssue` + classes that read, validate, and interpret `.versionmark.yaml` configuration files - The **Capture Subsystem**: the `VersionInfo` record that serializes and deserializes captured version data to and from JSON - The **Publishing Subsystem**: the `MarkdownFormatter` class that generates the markdown version report from captured data - The **SelfTest Subsystem**: the `Validation` class that provides built-in verification of the tool's core functionality -- The **Utilities Subsystem**: the `GlobMatcher` class that provides glob-pattern file matching - and the `PathHelpers` class that provides safe path combination for use by other subsystems +- The **Utilities Subsystem**: the `GlobMatcher` class that provides glob-pattern file + matching and the `PathHelpers` class that provides safe path combination + +This document does not cover installation, end-user usage patterns, CI/CD pipeline +configuration, or the internal design of OTS dependencies. Those topics are addressed in +the *VersionMark User Guide*, the *VersionMark Requirements Document*, and the respective +OTS package documentation. -This document does not cover installation, end-user usage patterns, or the CI/CD pipeline -configuration. Those topics are addressed in the *VersionMark User Guide* and the -*VersionMark Requirements Document*. +Each component described here corresponds to one or more requirements defined in the +`docs/reqstream/` files. The source code in `src/DemaConsulting.VersionMark/` is the +authoritative implementation. Any discrepancy between this document and the code should be +resolved by updating this document to reflect the actual implementation, or by raising a +defect against the code. ## Software Structure @@ -59,8 +68,6 @@ VersionMark (System) Version capture/publish tool └── PathHelpers (Unit) Safe path combination ``` -Each unit is described in detail in its own chapter within this document. - ## Folder Layout The source code folder structure mirrors the top-level subsystem breakdown above, giving @@ -79,32 +86,35 @@ src/DemaConsulting.VersionMark/ ├── Publishing/ │ └── MarkdownFormatter.cs — markdown report generation ├── SelfTest/ -│ └── Validation.cs — self-validation test runner +│ └── Validation.cs — self-validation test runner └── Utilities/ - ├── GlobMatcher.cs — glob-pattern file matching - └── PathHelpers.cs — safe path utilities + ├── GlobMatcher.cs — glob-pattern file matching + └── PathHelpers.cs — safe path utilities ``` -The test project mirrors the same layout under `test/DemaConsulting.VersionMark.Tests/`. - -## Audience +`Program.cs` resides at the project root rather than inside `Cli/` because .NET uses the +presence of a top-level `Program.cs` at the project root as the conventional entry-point +file. It is conceptually part of the Cli Subsystem, as shown in the Software Structure tree +above. The test project mirrors the same layout under `test/DemaConsulting.VersionMark.Tests/`. -This document is intended for: +## Companion Artifact Structure -- Software developers implementing features or fixing defects in the tool -- Reviewers conducting formal design and code reviews -- Quality assurance engineers tracing requirements to implementation +Each local software item has corresponding artifacts in parallel directory trees: -Readers are assumed to be familiar with C# and .NET development and general concepts of -command-line tool design. +- Requirements: `docs/reqstream/version-mark.yaml`, + `docs/reqstream/version-mark/{subsystem}.yaml`, + `docs/reqstream/version-mark/{subsystem}/{unit}.yaml` +- Design: `docs/design/version-mark.md`, + `docs/design/version-mark/{subsystem}.md`, + `docs/design/version-mark/{subsystem}/{unit}.md` +- Verification: `docs/verification/version-mark.md`, + `docs/verification/version-mark/{subsystem}.md`, + `docs/verification/version-mark/{subsystem}/{unit}.md` +- Source: `src/DemaConsulting.VersionMark/{Subsystem}/{Unit}.cs` +- Tests: `test/DemaConsulting.VersionMark.Tests/{Subsystem}/{Unit}Tests.cs` -## Relationship to Requirements and Code +Review-sets: defined in `.reviewmark.yaml` -Each component described here corresponds to one or more requirements defined in the -`docs/reqstream/` files. Requirements identifiers are referenced inline where relevant to -make traceability explicit. +## References -The source code in `src/DemaConsulting.VersionMark/` is the authoritative implementation. -This document describes the intent and structure of that code; any discrepancy between -this document and the code should be resolved by updating this document to reflect the -actual implementation, or by raising a defect against the code. +- [VersionMark releases](https://github.com/demaconsulting/VersionMark/releases) diff --git a/docs/design/ots.md b/docs/design/ots.md new file mode 100644 index 0000000..5ca02ad --- /dev/null +++ b/docs/design/ots.md @@ -0,0 +1,26 @@ +# OTS Integration Design + +VersionMark consumes three runtime Off-The-Shelf (OTS) libraries: +**YamlDotNet**, **Microsoft.Extensions.FileSystemGlobbing**, and +**DemaConsulting.TestResults**. This document describes the overall integration +strategy and provides a cross-reference to the per-item design files. + +## Integration Strategy + +Each OTS library is consumed only through a single local unit that acts as an +adapter boundary. No OTS type leaks beyond the unit that owns it: + +| OTS Item | Consuming Unit | Subsystem | +|-------------------------------------------|---------------------|---------------| +| YamlDotNet | VersionMarkConfig | Configuration | +| Microsoft.Extensions.FileSystemGlobbing | GlobMatcher | Utilities | +| DemaConsulting.TestResults | Validation | SelfTest | + +Restricting each OTS item to a single consuming unit minimizes the blast radius +of an API change during an upgrade: only the adapter unit needs to change. See +the individual per-item design files for details of features used and +initialization requirements: + +- See _YamlDotNet Integration Design_ +- See _Microsoft.Extensions.FileSystemGlobbing Integration Design_ +- See _DemaConsulting.TestResults Integration Design_ diff --git a/docs/design/ots/dema-consulting-test-results.md b/docs/design/ots/dema-consulting-test-results.md new file mode 100644 index 0000000..811d6d7 --- /dev/null +++ b/docs/design/ots/dema-consulting-test-results.md @@ -0,0 +1,40 @@ +## DemaConsulting.TestResults + +### Purpose + +`DemaConsulting.TestResults` is an OTS library developed by DEMA Consulting that +provides in-process test result collection and serialization to TRX (MSTest) and +JUnit XML formats. VersionMark uses it inside the `Validation` class (SelfTest +subsystem) to accumulate self-validation test outcomes and write them to a +results file when `--results` or `--results-xml` is specified. It is chosen +because it produces the same TRX format consumed by the CI/CD traceability +pipeline, enabling self-validation results to be included in the compliance +evidence set alongside the xUnit results. + +### Features Used + +| Feature | Usage in VersionMark | +|----------------------------|---------------------------------------------------------------| +| `TestResults` (class) | Collection of test case results named `"VersionMark Self-Validation"` | +| `TestResults.AddResult` | Record the pass/fail outcome of each self-validation scenario | +| `TrxSerializer.Serialize` | Write the collection to a `.trx` file | +| `JUnitSerializer.Serialize`| Write the collection to a JUnit XML `.xml` file | + +### Integration Pattern + +`DemaConsulting.TestResults` is consumed entirely within `Validation` and its +private helpers. No library types are exposed in the `Validation` public +signature (`Run` takes and returns only `Context`). + +1. `Validation.Run` creates a `TestResults` instance named + `"VersionMark Self-Validation"` before executing any test helper. +2. Each private test helper (`RunCaptureTest`, `RunPublishTest`, + `RunLintValidTest`, `RunLintInvalidTest`) calls `testResults.AddResult` with + the test name, pass/fail status, and an optional failure message. +3. After all helpers complete, `Validation.WriteResultsFile` inspects + `context.ResultsFile`: + - `.trx` extension → `TrxSerializer.Serialize(results, path)`. + - `.xml` extension → `JUnitSerializer.Serialize(results, path)`. + - Any other extension → `context.WriteError` is called; no file is written. +4. Both serializers write directly to disk; no intermediate stream management is + required by the caller. No `Dispose` is required on `TestResults`. diff --git a/docs/design/ots/microsoft-extensions-file-system-globbing.md b/docs/design/ots/microsoft-extensions-file-system-globbing.md new file mode 100644 index 0000000..5715e6e --- /dev/null +++ b/docs/design/ots/microsoft-extensions-file-system-globbing.md @@ -0,0 +1,44 @@ +## Microsoft.Extensions.FileSystemGlobbing + +### Purpose + +`Microsoft.Extensions.FileSystemGlobbing` is a glob-pattern file matching +library provided by Microsoft as part of the ASP.NET Core extensions ecosystem. +VersionMark uses it inside `GlobMatcher.FindMatchingFiles` to evaluate relative +and absolute glob patterns against the file system and return the matching file +paths. It is chosen because it handles cross-platform path separators correctly +and supports the standard `*`, `**`, and `?` wildcard syntax expected by CI/CD +pipeline authors. + +### Features Used + +| Feature | Usage in VersionMark | +|----------------------|--------------------------------------------------------------------| +| `Matcher` | Accumulate one or more patterns and execute the match | +| `Matcher.AddInclude` | Register each glob pattern supplied by the caller | +| `Matcher.Execute` | Run the match against a `DirectoryInfoWrapper` rooted at a base path | +| `PatternMatchingResult.Files` | Enumerate matched relative file paths and resolve to absolute paths | + +Only `AddInclude` and `Execute` are used; exclude patterns are not consumed by +VersionMark. + +### Integration Pattern + +`Microsoft.Extensions.FileSystemGlobbing` is consumed entirely within +`GlobMatcher.FindMatchingFiles`. No library types are exposed in public +signatures. + +1. Each pattern supplied by the caller is inspected to determine whether it is + absolute or relative. `Path.IsPathRooted` is used for this check. +2. For relative patterns, a single `Matcher` is created, all relative patterns + are added via `AddInclude`, and `Execute` is called with a + `DirectoryInfoWrapper` wrapping `new DirectoryInfo(Environment.CurrentDirectory)`. +3. For absolute patterns, the pattern is split at the last path separator before + the first wildcard character (`SplitAbsolutePattern`). A separate `Matcher` is + created for each distinct root directory, the relative portion is added via + `AddInclude`, and `Execute` is called with a `DirectoryInfoWrapper` wrapping + the absolute root directory. +4. Results from all `Matcher` instances are collected into a `HashSet` + with a file-system-appropriate comparer (to deduplicate overlapping + patterns), then returned as a sorted `List`. +5. No `Dispose` is required; `Matcher` does not hold unmanaged resources. diff --git a/docs/design/ots/yaml-dot-net.md b/docs/design/ots/yaml-dot-net.md new file mode 100644 index 0000000..1283299 --- /dev/null +++ b/docs/design/ots/yaml-dot-net.md @@ -0,0 +1,39 @@ +## YamlDotNet + +### Purpose + +YamlDotNet is a YAML parsing and serialization library for .NET. VersionMark +uses it to deserialize `.versionmark.yaml` configuration files into C# object +graphs inside `VersionMarkConfig.Load`. It is chosen because it exposes +source-location information (`Mark`) on every YAML node, which VersionMark uses +to report lint issues with precise file, line, and column coordinates. + +### Features Used + +| Feature | Usage in VersionMark | +|----------------------------|---------------------------------------------------------------| +| `YamlStream` / `YamlDocument` | Load the raw YAML document from a `TextReader` | +| `MappingNode` | Traverse the top-level mapping and each tool's mapping | +| `ScalarNode` | Read string values (command, regex, OS override keys/values) | +| `YamlNode.Start` (`Mark`) | Extract line and column for lint issue location reporting | +| `YamlException` | Catch YAML parse errors and convert to error-level LintIssue | + +YamlDotNet's object-model API (not the serializer API) is used so that unknown +keys can be detected and the source location of each node can be preserved. + +### Integration Pattern + +YamlDotNet is initialized and consumed entirely within `VersionMarkConfig.Load`. +No YamlDotNet types are exposed in public method signatures or return types. + +1. A `YamlStream` is created and `Load` is called with a `StreamReader` opened + on the configuration file path. +2. If the file is missing, the `FileNotFoundException` is caught before YamlDotNet + is invoked; a `LintIssue` of severity Error is returned. +3. If the YAML is syntactically invalid, `YamlException` is caught and converted + to a `LintIssue` of severity Error using `YamlException.Start` for location. +4. The document root is expected to be a `MappingNode`. Each child key is + extracted as a `ScalarNode` and its `Value` property is compared against the + known key set (`tools`, plus OS-override suffixes). Unknown keys produce a + `LintIssue` of severity Warning. +5. No `Dispose` is required; the `YamlStream` does not hold unmanaged resources. diff --git a/docs/design/title.txt b/docs/design/title.txt index 911bc17..6480f9f 100644 --- a/docs/design/title.txt +++ b/docs/design/title.txt @@ -1,12 +1,13 @@ --- -title: VersionMark Software Design -subtitle: Design Documentation -author: DEMA Consulting -description: Software design document for the VersionMark .NET tool for capturing and publishing tool version information +title: "VersionMark Software Design Document" +subtitle: "Tool version tracking for CI/CD environments" +author: "DEMA Consulting" +description: "Software Design Document for VersionMark" lang: en-US keywords: - VersionMark - Design + - Software Design Document - C# - .NET - CLI Tool diff --git a/docs/design/version-mark.md b/docs/design/version-mark.md index f7fb723..4025c53 100644 --- a/docs/design/version-mark.md +++ b/docs/design/version-mark.md @@ -1,70 +1,59 @@ -# System Design - -## Overview +# VersionMark + +## Architecture + +VersionMark is structured as a single system comprising six subsystems. There is no +system-level code — the system is a collection of subsystems and units. + +```mermaid +graph TD + Cli["Cli Subsystem\n(Program, Context)"] + Configuration["Configuration Subsystem\n(VersionMarkConfig, ToolConfig, LintIssue)"] + Capture["Capture Subsystem\n(VersionInfo)"] + Publishing["Publishing Subsystem\n(MarkdownFormatter)"] + SelfTest["SelfTest Subsystem\n(Validation)"] + Utilities["Utilities Subsystem\n(GlobMatcher, PathHelpers)"] + + Cli --> Configuration + Cli --> Capture + Cli --> Publishing + Cli --> SelfTest + Cli --> Utilities + SelfTest --> Capture + SelfTest --> Utilities + Publishing --> Capture +``` -VersionMark is a .NET global tool that captures tool version information from CI/CD job -environments and publishes consolidated version reports as markdown. It is designed to run -in any CI/CD pipeline that has the .NET runtime available. +| Subsystem | Units | Responsibility | +|---------------|------------------------------------------|-----------------------------------------------------| +| Cli | Program, Context | Argument parsing, mode dispatch, output routing | +| Configuration | VersionMarkConfig, ToolConfig, LintIssue | YAML config loading, validation, version capture | +| Capture | VersionInfo | JSON serialization of captured version data | +| Publishing | MarkdownFormatter | Markdown report generation from captured data | +| SelfTest | Validation | Built-in self-verification of all operational modes | +| Utilities | GlobMatcher, PathHelpers | Glob-pattern matching and safe path combination | The tool operates in four distinct modes, selected by command-line flags: -| Mode | Flag | Description | -|------------|-------------|----------------------------------------------------------| -| Capture | `--capture` | Runs configured commands and saves versions to JSON | -| Publish | `--publish` | Reads JSON files and generates a markdown version report | -| Lint | `--lint` | Validates the `.versionmark.yaml` configuration file | -| Validate | `--validate`| Runs built-in self-verification tests | - -## Typical Workflow - -In a multi-job CI/CD pipeline, VersionMark is used as follows: - -1. Each build job runs `versionmark --capture --job-id ` to capture tool versions - into a `versionmark-.json` file and uploads it as an artifact. -2. A final reporting job downloads all capture artifacts and runs - `versionmark --publish --report report.md -- versionmark-*.json` to generate the report. - -The JSON capture files are the interface between the capture and publish modes. They are -human-readable and can be inspected or consumed by other tools. - -## System Boundaries +| Mode | Flag | Description | +|----------|--------------|----------------------------------------------------------| +| Capture | `--capture` | Runs configured commands and saves versions to JSON | +| Publish | `--publish` | Reads JSON files and generates a markdown version report | +| Lint | `--lint` | Validates the `.versionmark.yaml` configuration file | +| Validate | `--validate` | Runs built-in self-verification tests | -VersionMark interacts with the following external entities: +### Inter-Subsystem Interactions -- **File system**: reads `.versionmark.yaml`, reads and writes JSON version files, writes - the markdown report, and optionally writes a log file and a TRX/JUnit results file. -- **Shell environment**: executes tool commands through the OS shell (`cmd.exe /c` on - Windows, `/bin/sh -c` on Linux and macOS). -- **Console**: writes progress and error output to `stdout` and `stderr`. -- **CI/CD pipeline**: consumes the tool via `dotnet tool install` and the command-line - interface; artifact upload/download is the pipeline's responsibility. +The subsystems interact as follows during the four operational modes. -## Inter-Subsystem Interactions - -The subsystems interact as follows during the four operational modes: - -### Capture Mode - -```text -Cli Subsystem → Configuration Subsystem → (shell) - ↓ - Capture Subsystem (VersionInfo.SaveToFile) -``` +#### Capture Mode 1. The Cli Subsystem (Program) parses arguments and calls `RunCapture`. 2. `RunCapture` uses the Configuration Subsystem to load `.versionmark.yaml` and call `FindVersions`, which executes shell commands and extracts version strings. 3. The result is saved to disk by `VersionInfo.SaveToFile` (Capture Subsystem). -### Publish Mode - -```text -Cli Subsystem → Utilities Subsystem (GlobMatcher.FindMatchingFiles) - ↓ - Capture Subsystem (VersionInfo.LoadFromFile) → Publishing Subsystem - ↓ - markdown report file -``` +#### Publish Mode 1. The Cli Subsystem (Program) parses arguments and calls `RunPublish`. 2. `RunPublish` uses `GlobMatcher.FindMatchingFiles` (Utilities Subsystem) to resolve glob @@ -73,90 +62,190 @@ Cli Subsystem → Utilities Subsystem (GlobMatcher.FindMatchingFiles) 4. The Publishing Subsystem (`MarkdownFormatter.Format`) converts the loaded records into a markdown string, which is written to the report file. -### Lint Mode - -```text -Cli Subsystem → Configuration Subsystem (VersionMarkConfig.Load) -``` +#### Lint Mode 1. The Cli Subsystem (Program) calls `RunLint`, which resolves the config file path, defaulting to `.versionmark.yaml`. 2. `RunLint` delegates to `VersionMarkConfig.Load`, which validates the YAML structure and returns a `VersionMarkLoadResult` containing all `LintIssue` records found. -3. `RunLint` calls `result.ReportIssues` to write all issues to the context, then exits - silently with exit code 0 when no issues are found. +3. `RunLint` calls `result.ReportIssues` to write all issues to the context. When no issues + are found, no console output is produced — consistent with the integration tests asserting + `string.IsNullOrEmpty(output)` for a clean lint run. -### Validate Mode - -```text -Cli Subsystem → SelfTest Subsystem - ↓ - (exercises Capture, Publish, and Lint modes internally) -``` +#### Validate Mode 1. The Cli Subsystem (Program) calls `Validation.Run`. 2. The SelfTest Subsystem exercises capture, publish, and lint modes end-to-end, using `PathHelpers` to safely construct paths inside a temporary directory. -The self-validation suite includes the following named tests, which serve as evidence -that the tool is functioning correctly after installation: - -| Test Name | What it Verifies | -| --------- | ---------------- | -| `VersionMark_CapturesVersions` | Capture mode correctly runs commands and saves version JSON | -| `VersionMark_GeneratesMarkdownReport` | Publish mode correctly reads JSON and produces a markdown report | -| `VersionMark_LintPassesForValidConfig` | Lint mode passes for a valid `.versionmark.yaml` configuration | -| `VersionMark_LintReportsErrorsForInvalidConfig` | Lint mode reports errors for an invalid configuration | - -These test names appear in requirements files (e.g., `version-mark.yaml`, -`platform-requirements.yaml`) as traceability evidence. When `--validate` is run in CI, -each matrix job runs on a specific platform/runtime and produces a TRX results file whose -filename and CI job context (e.g., `artifacts/validation-windows-latest-dotnet8.x.trx`) -provide the platform/runtime linkage used by filters, together with the test names, allowing -requirements to be verified per platform using source filters such as -`windows@VersionMark_CapturesVersions` or `dotnet8.x@VersionMark_GeneratesMarkdownReport`. - -## Configuration File - -The `.versionmark.yaml` configuration file defines which tools to capture and how to -extract their version strings: - -```yaml -tools: - dotnet: - command: dotnet --version - regex: '(?\d+\.\d+[\d.]*)' - node: - command: node --version - regex: 'v(?\d+\.\d+[\d.]*)' - command-win: node.exe --version -``` +The self-validation suite includes the following named tests: + +| Test Name | What it Verifies | +|----------------------------------------------|--------------------------------------------------------------------| +| `VersionMark_CapturesVersions` | Capture mode correctly runs commands and saves version JSON | +| `VersionMark_GeneratesMarkdownReport` | Publish mode correctly reads JSON and produces a markdown report | +| `VersionMark_LintPassesForValidConfig` | Lint mode passes for a valid `.versionmark.yaml` configuration | +| `VersionMark_LintReportsErrorsForInvalidConfig` | Lint mode reports errors for an invalid configuration | + +## External Interfaces + +**Command-line arguments**: The primary input interface for all operational modes. + +- *Type*: CLI (POSIX-style flags). +- *Role*: Consumer — VersionMark reads arguments from the OS process argument array. +- *Contract*: Parsed by `Context.Create`; mode flags (`--capture`, `--publish`, `--lint`, + `--validate`) select the operational mode; `--` separates mode-specific positional + arguments (tool names for capture, glob patterns for publish). +- *Constraints*: Unknown flags cause `ArgumentException`. The full flag set is defined in + the Cli Subsystem design. + +**`.versionmark.yaml`**: The tool configuration file. + +- *Type*: File (YAML, UTF-8). +- *Role*: Consumer — VersionMark reads this file in capture and lint modes. +- *Contract*: Must contain a `tools` mapping with at least one entry. Each tool entry must + have a `command` and a `regex` with a named `version` capture group. OS-specific overrides + (`command-win`, `regex-linux`, etc.) are optional. +- *Constraints*: File must exist when capture or lint mode is invoked. Validation issues are + reported with file name and line/column location. -OS-specific overrides (`command-win`, `command-linux`, `command-macos`, `regex-win`, -`regex-linux`, `regex-macos`) allow platform-specific command variations. See the -Configuration Subsystem design for full details. +**`versionmark-.json`**: Capture output / publish input file. -## Platform Support +- *Type*: File (JSON indented UTF-8). +- *Role*: Provider (written by capture mode) and Consumer (read by publish mode). +- *Contract*: Contains `JobId` (string) and `Versions` (object mapping tool names to version + strings). Property names match C# property names directly. Default filename is + `versionmark-.json`; overridden with `--output`. +- *Constraints*: Written by capture mode; read by publish mode. Overwritten if already exists. -VersionMark targets .NET 8, .NET 9, and .NET 10. It runs on Windows, Linux, and macOS. -OS-specific command execution uses `cmd.exe /c` on Windows and `/bin/sh -c` elsewhere. -Platform-specific requirements are documented in `platform-requirements.yaml`. +**`.md`**: Markdown version report. -## Error Handling +- *Type*: File (Markdown UTF-8). +- *Role*: Provider — written by publish mode. +- *Contract*: Contains a `Tool Versions` section with markdown bullet items. Heading level + controlled by `--report-depth` (default: value of `--depth`, which defaults to `1`). + Conflicting versions across jobs are listed with contributing job IDs in parentheses. +- *Constraints*: Requires `--report` flag. Overwritten if the file already exists. -VersionMark uses a consistent error handling strategy across all operational modes: +**`.log`**: Optional log file. -- **Exit codes**: The tool exits with code `0` on success and code `1` on any error condition. -- **Error output**: Error messages are written to `stderr`. When the source location is known - (such as a configuration file lint issue), the message is prefixed with the filename and - position in the format `filename(line,column): error: `. -- **Error propagation**: Errors detected in any subsystem are surfaced to the Cli Subsystem - via `Context.WriteError`, which writes the message to `stderr` and sets the process exit - code to a non-zero value. See the CLI Subsystem design for details. +- *Type*: File (plain text UTF-8, auto-flush). +- *Role*: Provider — optionally written when `--log ` is specified. +- *Contract*: Contains the same output as stdout, including error messages. +- *Constraints*: Created or overwritten when `--log` is specified; absent otherwise. -## Command-Line Interface +**`.trx` / `.xml`**: Optional validation results file. + +- *Type*: File (TRX XML or JUnit XML UTF-8). +- *Role*: Provider — optionally written when `--results ` is specified in validate mode. +- *Contract*: `.trx` extension → TRX format (MSTest compatible); `.xml` extension → JUnit + XML format. Other extensions produce an error via `context.WriteError`. +- *Constraints*: Only written when `--validate` is also specified. + +**Shell environment**: OS command execution for version capture. + +- *Type*: OS shell. +- *Role*: Consumer — VersionMark invokes the OS shell to execute tool commands. +- *Contract*: `cmd.exe /c` on Windows; `/bin/sh -c` on Linux and macOS. Commands and + regex patterns are defined in `.versionmark.yaml`. stdout and stderr are captured + asynchronously to prevent pipe deadlock. +- *Constraints*: Non-zero exit code from a shell command raises `InvalidOperationException`. + +**Console (`stdout`)**: Standard output for progress and tool version messages. + +- *Type*: Console (plain text). +- *Role*: Provider — VersionMark writes to stdout. +- *Contract*: Progress messages, captured versions, and report confirmation are written here. +- *Constraints*: All stdout output is suppressed when `--silent` is specified. + +**Console (`stderr`)**: Standard error for error messages. + +- *Type*: Console (plain text, red). +- *Role*: Provider — VersionMark writes to stderr. +- *Contract*: Error messages always written in red. When source location is known (e.g. a + lint issue), the message is prefixed as `filename(line,column): error: description`. +- *Constraints*: Suppressed when '--silent' is set; callers detect failures via the process exit code. + +## Dependencies + +- **YamlDotNet** — YAML deserialization of `.versionmark.yaml`; see *YamlDotNet + Integration Design* for details. +- **Microsoft.Extensions.FileSystemGlobbing** — glob-pattern file matching in publish mode; + see *Microsoft.Extensions.FileSystemGlobbing Integration Design* for details. +- **DemaConsulting.TestResults** — TRX and JUnit XML serialization of self-validation + results; see *DemaConsulting.TestResults Integration Design* for details. + +## Risk Control Measures + +N/A — VersionMark is a build-tool and reporting aid with no patient-safety, +financial-transaction, or safety-critical responsibilities. No software item segregation +for risk control purposes is required. + +## Data Flow + +**Capture mode** (user invokes `versionmark --capture --job-id `): + +```text +Command-line args + ↓ + Context (Cli) parses flags and arguments + ↓ + VersionMarkConfig.Load reads and validates .versionmark.yaml + ↓ + VersionMarkConfig.FindVersions executes shell commands via OS shell + ↓ + VersionInfo.SaveToFile writes versionmark-.json to disk +``` + +**Publish mode** (user invokes `versionmark --publish --report -- `): + +```text +Command-line args + ↓ + Context (Cli) parses flags and glob patterns + ↓ + GlobMatcher.FindMatchingFiles resolves patterns to JSON file paths + ↓ + VersionInfo.LoadFromFile (×N) deserializes each JSON file + ↓ + MarkdownFormatter.Format consolidates versions into markdown string + ↓ + .md written to disk +``` + +**Lint mode** (user invokes `versionmark --lint []`): + +```text +Command-line args + ↓ + Context (Cli) parses flags and optional config path + ↓ + VersionMarkConfig.Load validates .versionmark.yaml; collects LintIssue records + ↓ + LintIssue.ReportIssues writes issues to stdout/stderr +``` + +**Validate mode** (user invokes `versionmark --validate`): + +```text +Command-line args + ↓ + Context (Cli) + ↓ + Validation.Run exercises capture, publish, and lint modes in temp directories + ↓ + results summary + optional TRX/JUnit file +``` -The table above lists the four primary mode-selection flags. The full command-line interface — -including `--version`, `--help`, `--silent`, `--log`, `--job-id`, `--output`, `--results`, -`--report-depth`, and `--depth` — is defined in the CLI Subsystem design (see -`docs/design/version-mark/cli/`). +## Design Constraints + +| Constraint | Value / Description | +|--------------------------|----------------------------------------------------------------------------------| +| Target frameworks | .NET 8, .NET 9, .NET 10 (multi-targeted) | +| Supported platforms | Windows, Linux, macOS | +| Shell execution | `cmd.exe /c` on Windows; `/bin/sh -c` on Linux and macOS | +| Nullable reference types | Enabled; all public APIs annotated | +| Warnings as errors | Enabled; build fails on any warning | +| Distribution | NuGet global tool (`dotnet tool install -g DemaConsulting.VersionMark`) | +| No network access | No network calls at runtime; all I/O is local file system and shell | +| Regex timeout | User-supplied regex patterns have a 1-second evaluation timeout | diff --git a/docs/design/version-mark/capture.md b/docs/design/version-mark/capture.md index fe96c04..ec5b6eb 100644 --- a/docs/design/version-mark/capture.md +++ b/docs/design/version-mark/capture.md @@ -1,25 +1,60 @@ -## Capture Subsystem +## Capture ### Overview -The capture subsystem is responsible for persisting tool version information for the +The Capture subsystem is responsible for persisting tool version information for the current CI/CD job environment. It receives structured version results produced by the -configuration subsystem (for example, by `VersionMarkConfig.FindVersions`) and saves -them to a JSON file. The captured data is later consumed by the publish subsystem to -generate the version report. +Configuration subsystem and saves them to a JSON file. The captured data is later consumed +by the Publishing subsystem to generate the version report. It consists of a single unit: +`VersionInfo`, which is the data transfer record for captured version data. -The capture subsystem consists of a single unit: `VersionInfo`, which is the data transfer -record for captured version data. +### Interfaces -The `VersionInfo` unit is described in full in the *VersionInfo Unit Design* section of this document. +**`VersionInfo.SaveToFile(string path)`**: Serializes the record to JSON and writes it to +disk. -### Cross-Subsystem Dependencies +- *Type*: In-process .NET public API (instance method). +- *Role*: Provider. +- *Contract*: Serializes the record to indented JSON using UTF-8 encoding and writes it to + `path`. Non-`InvalidOperationException` errors are caught and re-thrown as + `InvalidOperationException` with context. +- *Constraints*: Overwrites the file if it already exists. -- **`Cli.Context`** (Caller → Capture): Provides `JobId`, `OutputFile`, `ToolNames` to - `RunCapture` -- **`Configuration.VersionMarkConfig`** (Caller → Capture): `FindVersions` produces the - `VersionInfo` record saved by `RunCapture` -- **`Program.RunCapture`** (Caller → Capture): Orchestrates load-config → find-versions → - save pipeline -- **`Program.RunPublish`** (Caller → Capture): Calls `VersionInfo.LoadFromFile` for each - matched JSON file +**`VersionInfo.LoadFromFile(string path)`**: Reads and deserializes a JSON file into a +`VersionInfo` record. + +- *Type*: In-process .NET public API (static method). +- *Role*: Provider. +- *Contract*: Checks file existence, reads the file as UTF-8, deserializes via + `JsonSerializer.Deserialize`, and validates the result is not null. +- *Constraints*: Throws `ArgumentException` if the file does not exist, the JSON is + invalid, or deserialization produces a null result. + +**`VersionInfo.JobId`**: The CI/CD job identifier that produced this record. + +- *Type*: In-process .NET public API (record property). +- *Role*: Provider. +- *Contract*: String value identifying the CI/CD job; set during capture mode. +- *Constraints*: Immutable after construction. + +**`VersionInfo.Versions`**: Maps tool names to their captured version strings. + +- *Type*: In-process .NET public API (record property). +- *Role*: Provider. +- *Contract*: `Dictionary` mapping tool names to version strings as + extracted by the Configuration subsystem. +- *Constraints*: Immutable after construction. + +### Design + +The Capture subsystem consists of the single `VersionInfo` record, which serves as the +data transfer object between the capture and publish operational modes: + +- In **capture mode**, `VersionMarkConfig.FindVersions` produces a `VersionInfo` record. + `Program.RunCapture` calls `VersionInfo.SaveToFile` to persist it as a JSON artifact. +- In **publish mode**, `Program.RunPublish` calls `VersionInfo.LoadFromFile` for each JSON + artifact discovered by `GlobMatcher`. The resulting records are passed to + `MarkdownFormatter.Format` for report generation. + +The `VersionInfo` record has no dependencies on other VersionMark subsystems; it depends +only on `System.Text.Json` from the .NET BCL. diff --git a/docs/design/version-mark/capture/version-info.md b/docs/design/version-mark/capture/version-info.md index 1e66bf5..c6e7dd2 100644 --- a/docs/design/version-mark/capture/version-info.md +++ b/docs/design/version-mark/capture/version-info.md @@ -1,44 +1,21 @@ -### VersionInfo Unit +### VersionInfo -#### Overview +#### Purpose -The `VersionInfo` record (`VersionInfo.cs`) is a positional record with two properties: +`VersionInfo` is the data transfer record between the capture and publish operational modes. +In capture mode, it is produced by `VersionMarkConfig.FindVersions` and saved as a JSON +artifact by `VersionInfo.SaveToFile`. In publish mode, it is loaded from those artifacts +by `VersionInfo.LoadFromFile` and passed to `MarkdownFormatter.Format` for report +generation. -| Property | Type | Description | -|------------|-------------------------------|-------------------------------------------------------| -| `JobId` | `string` | Identifies the CI/CD job that captured these versions | -| `Versions` | `Dictionary` | Maps tool names to their version strings | +#### Data Model -`VersionInfo` is the interface between the capture mode and the publish mode: capture -produces it by executing commands and saving to JSON; publish reads it back and passes it -to `MarkdownFormatter`. +| Property | Type | Description | +|------------|------------------------------|-------------------------------------------------------| +| `JobId` | `string` | Identifies the CI/CD job that captured these versions | +| `Versions` | `Dictionary` | Maps tool names to their captured version strings | -#### SaveToFile Method - -`SaveToFile` serializes the record to indented JSON using `JsonSerializer.Serialize` with -`WriteIndented = true` and writes it to the specified path using UTF-8 encoding. -Non-`InvalidOperationException` errors are wrapped and re-thrown as -`InvalidOperationException` with context. This satisfies requirement -`VersionMark-VersionInfo-Save`. The default output filename (`versionmark-.json`) -is determined by the CLI layer and contributes to satisfying `VersionMark-Capture-DefaultOutput`. - -#### LoadFromFile Method - -`LoadFromFile` is the symmetric counterpart to `SaveToFile`. It: - -1. Checks that the file exists; throws `ArgumentException` if not. -2. Reads the file content as UTF-8. -3. Deserializes using `JsonSerializer.Deserialize`. -4. Validates the result is not null. - -`JsonException` is caught and re-thrown as `ArgumentException`. Other -non-`ArgumentException` errors are wrapped similarly. This satisfies -`VersionMark-VersionInfo-Load`, `VersionMark-VersionInfo-Error`, -`VersionMark-Publish-Consolidate`, and `VersionMark-Publish-FileError`. - -#### JSON Schema - -The JSON file produced by `SaveToFile` has this structure: +JSON representation produced by `SaveToFile`: ```json { @@ -50,6 +27,39 @@ The JSON file produced by `SaveToFile` has this structure: } ``` -The property names in JSON match the C# property names exactly because no custom -`JsonPropertyName` attributes are applied. The file is UTF-8 encoded with indentation -for human readability and diff-friendliness in version control. +Property names in JSON match C# property names exactly because no `JsonPropertyName` +attributes are applied. Files are UTF-8 encoded with indentation for human readability +and diff-friendliness. + +#### Key Methods + +**`SaveToFile(string path)`** — Serializes the record to indented JSON using +`JsonSerializer.Serialize` with `WriteIndented = true` and writes it to `path` using UTF-8 +encoding. Overwrites the file if it already exists. + +**`LoadFromFile(string path)` (static)** — Symmetric counterpart to `SaveToFile`. Checks +file existence, reads the file as UTF-8, deserializes via +`JsonSerializer.Deserialize`, and validates the result is not null. Returns +the deserialized `VersionInfo` record. + +#### Error Handling + +| Condition | Method | Behavior | +|-----------------------------------|-----------------|--------------------------------------------------| +| File does not exist | `LoadFromFile` | `ArgumentException` thrown | +| JSON is invalid | `LoadFromFile` | `JsonException` caught, re-thrown as `ArgumentException` | +| Deserialization returns null | `LoadFromFile` | `ArgumentException` thrown | +| Other non-`ArgumentException` error | `LoadFromFile` | Wrapped and re-thrown as `ArgumentException` with context | +| Other non-`InvalidOperationException` error | `SaveToFile` | Wrapped and re-thrown as `InvalidOperationException` with context | + +#### Dependencies + +- `System.Text.Json` (BCL) — JSON serialization and deserialization. + +#### Callers + +- `VersionMarkConfig.FindVersions` — constructs a `VersionInfo` record and returns it. +- `Program.RunCapture` — calls `VersionInfo.SaveToFile` to persist the capture artifact. +- `Program.RunPublish` — calls `VersionInfo.LoadFromFile` for each JSON artifact. +- `Validation.RunPublishTest` — constructs `VersionInfo` records and writes them as JSON + for the self-validation publish test. diff --git a/docs/design/version-mark/cli.md b/docs/design/version-mark/cli.md index 7d1481e..30b32df 100644 --- a/docs/design/version-mark/cli.md +++ b/docs/design/version-mark/cli.md @@ -1,8 +1,110 @@ -## Cli Subsystem +## Cli ### Overview -The command-line interface subsystem is responsible for parsing command-line arguments, -routing program flow to the appropriate subsystem, and managing all output (console, -error, and log file). It consists of two units: `Program` (the entry point) and `Context` -(the argument and output container). +The Cli subsystem is responsible for parsing command-line arguments, routing program flow +to the appropriate subsystem, and managing all output (console, error, and log file). It +consists of two units: `Program` (the entry point and mode dispatcher) and `Context` (the +command-line argument and output container). + +### Interfaces + +**`Context.Create(string[] args)`**: Factory method that constructs a fully-parsed Context +instance. + +- *Type*: In-process .NET public API. +- *Role*: Provider. +- *Contract*: Parses the command-line argument array; returns a Context with all properties + populated. Opens the log file if `--log` was specified. Caller must dispose the returned + Context. +- *Constraints*: Throws `ArgumentException` for unknown arguments. + +**`Context.WriteLine(string message)`**: Writes a message to stdout and the log file. + +- *Type*: In-process .NET public API. +- *Role*: Provider. +- *Contract*: Writes `message` to `Console.Out` unless `Silent` is set; also writes to the + log file if one is open. +- *Constraints*: No output is produced when `Context.Silent` is `true`. + +**`Context.WriteError(string message)`**: Writes an error message and sets the exit code to 1. + +- *Type*: In-process .NET public API. +- *Role*: Provider. +- *Contract*: Writes `message` to `Console.Error` in red; sets `ExitCode` to `1`; writes + to the log file if one is open. +- *Constraints*: stderr is suppressed when `Silent` is `true`; `_hasErrors` is always set and `ExitCode` always reflects the failure. The exit code cannot be reset once + set to `1`. + +**`Context.ExitCode`**: Returns the process exit code for the current invocation. + +- *Type*: In-process .NET public API. +- *Role*: Provider. +- *Contract*: Returns `0` when no errors have been reported; `1` after any call to + `WriteError`. +- *Constraints*: Read-only computed property. + +**`Context.Dispose()`**: Releases the log file writer. + +- *Type*: In-process .NET public API. +- *Role*: Provider. +- *Contract*: Closes and disposes the log file writer if one was opened. +- *Constraints*: Must be called to avoid resource leaks when `--log` was specified. + +**`Program.Run(Context context)`**: Executes the appropriate operational mode. + +- *Type*: In-process .NET public API. +- *Role*: Provider. +- *Contract*: Implements priority-ordered dispatch: Version → (banner) → Help → Validate → Lint → + Capture → Publish → default. The application banner is printed after the version check and before + all other modes; it is suppressed when lint is the dispatched action (i.e. `--lint` is set and + neither `--help` nor `--validate` is set) to produce clean, issue-only output. Called re-entrantly + by `Validation.Run` for self-tests. +- *Constraints*: Does not throw; all errors are routed through `context.WriteError`. + +**`Program.Main(string[] args)`**: Process entry point. + +- *Type*: .NET process entry point. +- *Role*: Provider. +- *Contract*: Creates `Context`, calls `Run`, returns `context.ExitCode`. Catches + `ArgumentException` and `InvalidOperationException`; writes to `Console.Error`; returns + exit code `1`. Re-throws other exceptions. +- *Constraints*: Called once by the .NET runtime. + +The accepted CLI flags recognized by `Context.Create` are: + +- **`--version`** (`-v`): Print version string and exit. +- **`--help`** (`-?`, `-h`): Print usage and exit. +- **`--silent`**: Suppress stdout output. +- **`--validate`**: Run built-in self-verification. +- **`--log `**: Open log file for all output. +- **`--result`** / **`--results `**: Write TRX (`.trx`) or JUnit (`.xml`) results file. +- **`--lint []`**: Validate config; defaults to `.versionmark.yaml`. +- **`--capture`**: Activate capture mode. +- **`--job-id `**: Set job identifier for capture output filename. +- **`--output `**: Override default capture output filename. +- **`--publish`**: Activate publish mode. +- **`--report `**: Set the markdown report output file. +- **`--report-depth <1-6>`**: Markdown heading depth for the report section. +- **`--depth <1-6>`**: Default heading depth; used for validate and as fallback for + `--report-depth`. +- **`--`**: Separator; subsequent tokens are tool names (capture mode) or glob patterns + (publish mode). + +### Design + +The Cli subsystem contains two collaborating units: + +1. **`Program`** — the entry point. It constructs `Context` via `Context.Create`, dispatches + to the appropriate mode helper (`RunCapture`, `RunPublish`, `RunLint`, or + `Validation.Run`), catches top-level `ArgumentException` and `InvalidOperationException`, + and returns `context.ExitCode` to the OS. + +2. **`Context`** — the state container. It encapsulates all parsed command-line arguments + as strongly-typed properties and owns all output channels (stdout writer, stderr, log + file). Output routing (`WriteLine`, `WriteError`) is centralized here so that all + subsystems receiving a `Context` have a uniform interface for messaging and error + recording. + +The two units are coupled through `Program` constructing `Context` and passing it to all +mode helpers. `Context` has no dependency on `Program`. diff --git a/docs/design/version-mark/cli/context.md b/docs/design/version-mark/cli/context.md index b02d7c1..970a07b 100644 --- a/docs/design/version-mark/cli/context.md +++ b/docs/design/version-mark/cli/context.md @@ -1,54 +1,81 @@ -### Context Unit - -#### Overview - -The `Context` class (`Context.cs`) is a sealed, disposable container for all parsed -command-line state and output routing. It is constructed via the `Create` factory method. - -#### Properties - -| Property | Type | Default | Description | -|---------------|------------|---------|-------------------------------------------| -| `Version` | `bool` | `false` | `-v` / `--version` flag | -| `Help` | `bool` | `false` | `-?`, `-h`, `--help` flag | -| `Silent` | `bool` | `false` | `--silent` flag | -| `Validate` | `bool` | `false` | `--validate` flag | -| `ResultsFile` | `string?` | `null` | `--results ` | -| `Lint` | `bool` | `false` | `--lint` flag | -| `LintFile` | `string?` | `null` | Optional file argument for `--lint` | -| `Capture` | `bool` | `false` | `--capture` flag | -| `JobId` | `string?` | `null` | `--job-id ` | -| `OutputFile` | `string?` | `null` | `--output ` | -| `ToolNames` | `string[]` | `[]` | Tool names after `--` separator in capture| -| `Publish` | `bool` | `false` | `--publish` flag | -| `ReportFile` | `string?` | `null` | `--report ` | -| `Depth` | `int` | `1` | `--depth ` (heading depth, def: 1) | -| `ReportDepth` | `int` | `Depth` | `--report-depth ` (def: `Depth`) | -| `GlobPatterns`| `string[]` | `[]` | Patterns after `--` separator in publish | -| `ExitCode` | `int` | `0`/`1` | 0 for success, 1 if errors reported | - -This satisfies requirements `VersionMark-CommandLine-Context`, `VersionMark-CommandLine-Version`, -`VersionMark-CommandLine-Help`, `VersionMark-CommandLine-Silent`, `VersionMark-CommandLine-Validate`, -`VersionMark-CommandLine-Results`, `VersionMark-CommandLine-Log`, `VersionMark-CommandLine-ExitCode`, -`VersionMark-CommandLine-Lint`, and `VersionMark-Context-Create`. - -#### ArgumentParser - -The private `ArgumentParser` class performs the actual token-by-token parsing. It handles -the `--` separator, which switches subsequent tokens to either tool names (capture mode) or -glob patterns (publish mode). Unknown arguments throw `ArgumentException`, satisfying -`VersionMark-CommandLine-InvalidArgs`. - -#### WriteLine and WriteError - -`WriteLine` writes to `Console.Out` unless `Silent` is set, and also writes to the log -file if one was opened. `WriteError` additionally sets `_hasErrors = true` (making -`ExitCode` return 1) and writes to `Console.Error` in red. This satisfies -`VersionMark-CommandLine-Silent`, `VersionMark-CommandLine-ErrorOutput`, `VersionMark-CommandLine-ExitCode`, -`VersionMark-Context-WriteLine`, `VersionMark-Context-WriteError`, and `VersionMark-Context-WriteErrorExitCode`. - -#### Log File - -The `OpenLogFile` method opens a `StreamWriter` with `AutoFlush = true`. If opening fails, -an `InvalidOperationException` is thrown with contextual information. The writer is -disposed when `Context` is disposed, satisfying `VersionMark-CommandLine-Log`. +### Context + +#### Purpose + +`Context` (`Context.cs`) is a sealed, disposable container for all parsed command-line +state and output routing. It is constructed via the `Create` factory method, which +tokenizes the argument array and populates the relevant flag and value properties. All +other subsystems receive a `Context` instance from `Program` and use it to query parsed +flags and to write output or errors. + +#### Data Model + +| Property | Type | Default | Description | +|----------------|------------|-------------|----------------------------------------------------| +| `Version` | `bool` | `false` | `-v` / `--version` flag | +| `Help` | `bool` | `false` | `-?`, `-h`, `--help` flag | +| `Silent` | `bool` | `false` | `--silent` flag | +| `Validate` | `bool` | `false` | `--validate` flag | +| `ResultsFile` | `string?` | `null` | `--results ` / `--result ` (legacy alias) | +| `Lint` | `bool` | `false` | `--lint` flag | +| `LintFile` | `string?` | `null` | Optional file argument for `--lint` | +| `Capture` | `bool` | `false` | `--capture` flag | +| `JobId` | `string?` | `null` | `--job-id ` | +| `OutputFile` | `string?` | `null` | `--output ` | +| `ToolNames` | `string[]` | `[]` | Tool names after `--` separator in capture mode | +| `Publish` | `bool` | `false` | `--publish` flag | +| `ReportFile` | `string?` | `null` | `--report ` | +| `Depth` | `int` | `1` | `--depth ` (heading depth, default: 1) | +| `ReportDepth` | `int` | `Depth` | `--report-depth ` (defaults to `Depth`) | +| `GlobPatterns` | `string[]` | `[]` | Patterns after `--` separator in publish mode | +| `ExitCode` | `int` | `0` / `1` | 0 for success; 1 if any errors have been reported | + +The private `_hasErrors` field, set by `WriteError`, controls whether `ExitCode` returns 1. +An optional `StreamWriter` opened by `OpenLogFile` is disposed when `Context` is disposed. + +#### Key Methods + +**`Create(string[] args)` (static factory)** — Constructs a `Context` by delegating to the +private `ArgumentParser` class, which performs token-by-token parsing. The `--` separator +switches subsequent tokens to either tool names (capture mode) or glob patterns (publish +mode); using `--` outside capture or publish mode throws `ArgumentException`. Both +`--results` and its legacy alias `--result` set `ResultsFile`; the factory throws +`InvalidOperationException` if the log file specified via `--log` cannot be opened. Returns +the populated `Context`. + +**`OpenLogFile(string path)`** — Opens a `StreamWriter` with `AutoFlush = true` to `path`. +Subsequent `WriteLine` and `WriteError` calls also write to this stream. If opening fails, +`InvalidOperationException` is thrown with contextual information. + +**`WriteLine(string message)`** — Writes to `Console.Out` unless `Silent` is set, and +also writes to the log file if one was opened. + +**`WriteError(string message)`** — Sets `_hasErrors = true` (making `ExitCode` return 1), +and also writes to the log file if one was opened. Writes to `Console.Error` in red unless +`Silent` is set. In silent mode all output is suppressed and callers detect failures via the +process exit code. This supports self-validation scenarios where the tool deliberately +triggers errors without producing unwanted output. + +#### Error Handling + +| Condition | Behavior | +|-----------------------------------|--------------------------------------------------------| +| `null` `args` passed to `Create` | `Create` throws `ArgumentNullException` | +| Unknown argument token | `ArgumentParser` throws `ArgumentException` | +| `--` used outside capture/publish | `ArgumentParser` throws `ArgumentException` | +| Log file cannot be opened | `OpenLogFile` throws `InvalidOperationException` | + +#### Dependencies + +- `System.Console` (BCL) — output to stdout and stderr. +- `System.IO.StreamWriter` (BCL) — log file output. + +#### Callers + +- `Program.Main` — constructs `Context` via `Context.Create` and passes it to `Program.Run`. +- `Program.Run`, `Program.RunCapture`, `Program.RunPublish`, `Program.RunLint` — query + flags and call `WriteLine` / `WriteError`. +- `Validation.Run` and test helpers — construct a fresh `Context` for each self-validation + test. +- `VersionMarkLoadResult.ReportIssues` — calls `context.WriteLine` and `context.WriteError` + to report lint issues. diff --git a/docs/design/version-mark/cli/program.md b/docs/design/version-mark/cli/program.md index 518298c..4a10a04 100644 --- a/docs/design/version-mark/cli/program.md +++ b/docs/design/version-mark/cli/program.md @@ -1,27 +1,25 @@ -### Program Unit +### Program -#### Overview +#### Purpose -The `Program` class (`Program.cs`) is the top-level entry point for the tool. It owns the -`Main` method, constructs the `Context`, dispatches to the appropriate mode, and handles -top-level exception translation. +`Program` (`Program.cs`) is the top-level entry point for VersionMark. It owns the `Main` +method, constructs the `Context` from command-line arguments, dispatches to the appropriate +operational mode, and handles top-level exception translation. It also contains the private +helpers `RunCapture`, `RunPublish`, and `RunLint` that implement each operational mode. -#### Version Property +#### Data Model -The static `Version` property reads the assembly's `AssemblyInformationalVersionAttribute` -at runtime and falls back to `AssemblyVersion` or `"0.0.0"` if neither is available. This -satisfies requirement `VersionMark-Program-Version`. +`Program` has no instance state. The single static `Version` property reads the +assembly's `AssemblyInformationalVersionAttribute` at runtime, falling back to +`AssemblyVersion` or `"0.0.0"` if neither is available. -#### Main Method +#### Key Methods -`Main` creates a `Context` from the command-line arguments, calls `Run`, and returns -`context.ExitCode`. `ArgumentException` and `InvalidOperationException` are caught and -written to `Console.Error`, returning exit code 1. Unexpected exceptions are re-thrown to -generate event-log entries. This satisfies requirement `VersionMark-Program-Dispatch`. +**`Main(string[] args)` (static)** — Constructs a `Context` from command-line arguments, +calls `Run`, and returns `context.ExitCode`. `ArgumentException` and +`InvalidOperationException` are caught and written to `Console.Error`, returning exit code 1. Unexpected exceptions are re-thrown to generate event-log entries. -#### Run Method - -`Run` implements priority-ordered dispatch: +**`Run(Context context)` (static)** — Implements the following priority-ordered dispatch: | Priority | Condition | Action | |----------|--------------------|----------------------------------------------------------------| @@ -32,23 +30,58 @@ generate event-log entries. This satisfies requirement `VersionMark-Program-Disp | 4 | `context.Lint` | Run lint mode and return | | 5 | `context.Capture` | Run capture mode and return | | 6 | `context.Publish` | Run publish mode and return | -| 7 | Default | Run placeholder tool logic | +| 7 | Default | Print placeholder message | + +**`RunCapture(Context context)` (private static)** — Validates required arguments +(`--job-id`), resolves the default output file name (`versionmark-.json`), loads +the configuration via `VersionMarkConfig.Load`, reports lint issues, calls +`VersionMarkConfig.FindVersions`, and saves the result with `VersionInfo.SaveToFile`. + +**`RunPublish(Context context)` (private static)** — Validates required arguments +(`--report`), resolves capture files via `GlobMatcher.FindMatchingFiles`, loads each with +`VersionInfo.LoadFromFile`, generates the report with `MarkdownFormatter.Format`, and +writes it to the report file. + +**`RunLint(Context context)` (private static)** — Resolves the configuration file path +(defaulting to `.versionmark.yaml` when `context.LintFile` is null), calls +`VersionMarkConfig.Load`, and reports all discovered issues via `result.ReportIssues`. + +**`PrintBanner(Context context)` (private static)** — Writes the application name, version +string, and copyright line to the context output. Raises no exceptions; output is suppressed +automatically by `context.WriteLine` when `--silent` is set. + +**`RunToolLogic(Context context)` (private static)** — Executes the default tool logic when +no specific operational mode flag is given. Currently prints a placeholder message; intended +to be replaced with the main tool implementation. + +**`LoadVersionInfoFiles(List jsonFiles)` (private static)** — Maps a list of JSON +file paths to `VersionInfo` instances by calling `VersionInfo.LoadFromFile` on each entry. +Throws `ArgumentException` (propagated from `LoadFromFile`) when a file cannot be read or +parsed. + +#### Error Handling -This dispatch order satisfies requirement `VersionMark-Program-Dispatch`. +| Condition | Behavior | +|----------------------------------------|--------------------------------------------------------------------| +| `ArgumentException` or `InvalidOperationException` from any mode | `context.WriteError`; `ExitCode` set to 1 | +| `ArgumentException` or `InvalidOperationException` from `Context.Create` | Written directly to `Console.Error` (no context object exists yet); exit code `1` returned | +| Unexpected exception from `Main` | Re-thrown to propagate as unhandled exception | +| `--job-id` missing in capture mode | `context.WriteError`; return | +| `--report` missing in publish mode | `context.WriteError`; return | +| No files match glob patterns | `context.WriteError` listing the patterns; return | +| Lint errors found | `context.WriteError` per issue; `ExitCode` set to 1 | -#### Capture and Publish Orchestration +#### Dependencies -`RunCapture` and `RunPublish` are private helpers called from `Run`. They validate required -arguments, invoke configuration loading and version capture/report generation, and delegate -error handling to `context.WriteError`. These methods satisfy requirements -`VersionMark-Program-RunCapture` and `VersionMark-Program-RunPublish`. +- `Context` (Cli subsystem) — command-line state and output routing. +- `VersionMarkConfig` (Configuration subsystem) — configuration loading and version capture. +- `VersionInfo` (Capture subsystem) — JSON serialization of captured versions. +- `MarkdownFormatter` (Publishing subsystem) — markdown report generation. +- `GlobMatcher` (Utilities subsystem) — glob-pattern resolution of capture files. +- `Validation` (SelfTest subsystem) — self-validation test runner. -#### RunLint +#### Callers -`RunLint` is a private helper called from `Run`. It resolves the configuration file path, -defaulting to `.versionmark.yaml` when `context.LintFile` is `null`, then calls -`VersionMarkConfig.Load` to validate the configuration. It reports all discovered issues via -`result.ReportIssues`. The application banner is suppressed when lint is the dispatched action -(i.e., `--lint` is specified and neither `--help` nor `--validate` takes priority) so that -the output contains only the actual issue lines, making it easy to consume in scripts. This -satisfies requirement `VersionMark-Program-RunLint`. +- `Main` is called by the .NET runtime as the application entry point. +- `Validation.RunCaptureTest`, `Validation.RunPublishTest`, `Validation.RunLintValidTest`, + and `Validation.RunLintInvalidTest` call `Program.Run` re-entrantly for self-validation. diff --git a/docs/design/version-mark/configuration.md b/docs/design/version-mark/configuration.md index f868f79..5911c3e 100644 --- a/docs/design/version-mark/configuration.md +++ b/docs/design/version-mark/configuration.md @@ -1,9 +1,86 @@ -## Configuration Subsystem +## Configuration ### Overview -The configuration subsystem reads and interprets the `.versionmark.yaml` file that defines +The Configuration subsystem reads and interprets the `.versionmark.yaml` file that defines which tools to capture and how to extract their versions, and reports any validation issues -found during loading. It consists of three units: `ToolConfig` (per-tool settings), -`VersionMarkConfig` (the top-level configuration container and validation entry point), and -`LintIssue` (the types used to surface validation issues to callers). +found during loading. It consists of three units: `LintIssue` (the shared validation types), +`ToolConfig` (per-tool settings), and `VersionMarkConfig` (the top-level configuration +container and validation entry point). + +### Interfaces + +**`VersionMarkConfig.ReadFromFile(string filePath)`**: Backward-compatibility wrapper that +loads and validates a `.versionmark.yaml` file, throwing on the first error. + +- *Type*: In-process .NET public API (static method). +- *Role*: Provider. +- *Contract*: Delegates to `Load`; throws `ArgumentException` if any error-level issues are + found. Returns the loaded `VersionMarkConfig` when all checks pass. +- *Constraints*: Use `Load` directly when access to the full lint-issue list is needed + (e.g. for the `--lint` command). `ReadFromFile` is intended for callers that only need + the configuration and do not require access to individual issues. + +**`VersionMarkConfig.Load(string filePath)`**: Reads and validates a `.versionmark.yaml` +file, returning a compound result containing the parsed configuration and all validation +issues. + +- *Type*: In-process .NET public API (static method). +- *Role*: Provider. +- *Contract*: Checks file existence; parses YAML via YamlDotNet; validates the root node + is a mapping; locates the `tools` key; calls `ValidateTool` for each entry; returns a + `VersionMarkLoadResult` containing the parsed `VersionMarkConfig` (or `null` on error) + and all `LintIssue` records. Uses an accumulate-and-continue strategy — all issues are + collected before returning. +- *Constraints*: `Result.Config` is `null` when any error-level issue exists. Callers must + check `Result.Config` before proceeding with capture or other operations. + +**`VersionMarkConfig.FindVersions(IEnumerable toolNames, string jobId, string? os)`**: +Executes the configured shell commands for the specified tools and returns a `VersionInfo` +record. + +- *Type*: In-process .NET public API (instance method). +- *Role*: Provider. +- *Contract*: Resolves the OS once upfront (`os ?? ToolConfig.GetCurrentOs()`); for each + named tool calls `GetEffectiveCommand`, `GetEffectiveRegex`, executes the command in a + shell, and applies the regex to extract the version string. Returns a `VersionInfo` record + with `JobId` and a `Versions` dictionary. +- *Constraints*: Throws `ArgumentException` for unknown tool names. Passing an OS for which + no command or regex is defined raises `InvalidOperationException`. + +**`VersionMarkLoadResult.ReportIssues(Context context)`**: Writes all collected validation +issues to the context. + +- *Type*: In-process .NET public API (instance method). +- *Role*: Provider. +- *Contract*: Routes `LintSeverity.Error` issues to `context.WriteError` and + `LintSeverity.Warning` issues to `context.WriteLine`. +- *Constraints*: Internal method; called only from `Program.RunLint` and `Program.RunCapture`. + +**`LintIssue`**: Record carrying a single validation issue found during configuration loading. + +- *Type*: In-process .NET public API (record type). +- *Role*: Provider. +- *Contract*: Carries `FilePath`, `Line`, `Column`, `Severity`, and `Description`. + `ToString()` formats as `"file(line,col): severity: description"` where severity is + lowercase (`warning` or `error`). +- *Constraints*: Immutable record; no failure-prone logic. + +### Design + +The Configuration subsystem contains three collaborating units. The dependency direction +is `VersionMarkConfig` → `ToolConfig` and `VersionMarkConfig` → `LintIssue`; `LintIssue` +and `ToolConfig` have no dependencies on each other or on other VersionMark subsystems. + +1. **`LintIssue`** — defines the shared data types (`LintSeverity`, `LintIssue`, + `VersionMarkLoadResult`) used throughout the loading pipeline. + +2. **`ToolConfig`** — an immutable record representing a single tool's command and regex + settings with OS-specific overrides. Constructed by `VersionMarkConfig.ValidateTool`. + Exposes `GetEffectiveCommand` and `GetEffectiveRegex` for OS-resolved lookup. + +3. **`VersionMarkConfig`** — the orchestrator. Its `Load` method drives YAML parsing using + YamlDotNet, calls `ValidateTool` for each tool entry, and accumulates all `LintIssue` + records into a single `VersionMarkLoadResult`. Its `FindVersions` method iterates the + loaded `ToolConfig` entries, calls `GetEffectiveCommand` and `GetEffectiveRegex`, + executes each command in an OS shell, and returns a `VersionInfo` record. diff --git a/docs/design/version-mark/configuration/lint-issue.md b/docs/design/version-mark/configuration/lint-issue.md index a339d3c..a26f87b 100644 --- a/docs/design/version-mark/configuration/lint-issue.md +++ b/docs/design/version-mark/configuration/lint-issue.md @@ -1,23 +1,23 @@ -### LintIssue Unit +### LintIssue -#### Overview +#### Purpose -`LintIssue.cs` contains the types used to surface validation issues found while loading a -`.versionmark.yaml` configuration file. It defines three public types: the `LintSeverity` -enumeration, the `LintIssue` record, and the `VersionMarkLoadResult` record. +`LintIssue.cs` defines the types used to surface validation issues found while loading a +`.versionmark.yaml` configuration file. It provides `LintSeverity` (severity +classification), `LintIssue` (a single located issue), and `VersionMarkLoadResult` (the +return value of `VersionMarkConfig.Load` bundling the loaded configuration with all issues +found). -#### LintSeverity Enumeration +#### Data Model -`LintSeverity` classifies the severity of a validation issue: +**`LintSeverity` enumeration** | Value | Meaning | |-----------|----------------------------------------------------------------------------| | `Warning` | Non-fatal advisory message; loading continues. | -| `Error` | Fatal validation failure that prevents the configuration from being built. | +| `Error` | Fatal validation failure that prevents the configuration from being used. | -#### LintIssue Record - -`LintIssue` represents a single issue found during configuration loading. It carries: +**`LintIssue` record** | Property | Type | Description | |---------------|----------------|------------------------------------------------| @@ -27,21 +27,40 @@ enumeration, the `LintIssue` record, and the `VersionMarkLoadResult` record. | `Severity` | `LintSeverity` | Severity classification. | | `Description` | `string` | Human-readable description of the issue. | -`ToString` formats the record as `"{FilePath}({Line},{Column}): {severity}: {Description}"`, -where `{severity}` is emitted in lowercase (`warning` or `error`), producing output in -the familiar `file(line,col): error: message` format understood by CI systems and editors. - -#### VersionMarkLoadResult Record +`ToString` formats the record as `"{FilePath}({Line},{Column}): {severity}: {Description}"` +where `{severity}` is lowercase (`warning` or `error`), producing output in the familiar +`file(line,col): level: message` format understood by CI systems and editors. -`VersionMarkLoadResult` is the return type of `VersionMarkConfig.Load`. It bundles two -properties: +**`VersionMarkLoadResult` record** -| Property | Type | Description | -|----------|----------------------------|-----------------------------------------------------------------------------| -| `Config` | `VersionMarkConfig?` | The loaded configuration, or `null` when errors prevented building it. | +| Property | Type | Description | +|----------|----------------------------|----------------------------------------------------------------------------| +| `Config` | `VersionMarkConfig?` | Loaded configuration; `null` when any error-level issues were found. | | `Issues` | `IReadOnlyList` | All validation issues; may contain warnings even when `Config` is non-null. | -The `ReportIssues` method writes all issues to a `Context`, routing `LintSeverity.Error` -issues to `context.WriteError` and `LintSeverity.Warning` issues to `context.WriteLine`. -This method is `internal` and is called only within the library (by `RunLint` and -`RunCapture` in `Program`). +#### Key Methods + +**`VersionMarkLoadResult.ReportIssues(Context context)` (internal)** — Iterates all issues +and routes each to `context.WriteError` (for `Error` severity) or `context.WriteLine` (for +`Warning` severity). + +#### Error Handling + +These types are primarily value-carrying data records with minimal internal error handling. +All error accumulation is performed by `VersionMarkConfig.Load`, which populates the `Issues` +list. Callers inspect the returned `VersionMarkLoadResult` to determine whether the +configuration is usable. + +`VersionMarkLoadResult.ReportIssues` guards against a null `context` argument by calling +`ArgumentNullException.ThrowIfNull(context)` before iterating issues. + +#### Dependencies + +- `Context` (Cli subsystem) — used by `ReportIssues` to route issue output. + +#### Callers + +- `VersionMarkConfig.Load` — creates `LintIssue` records and returns them in a + `VersionMarkLoadResult`. +- `Program.RunCapture` and `Program.RunLint` — call `result.ReportIssues` to write + discovered issues to the context output. diff --git a/docs/design/version-mark/configuration/tool-config.md b/docs/design/version-mark/configuration/tool-config.md index e14c6a3..e32d3e6 100644 --- a/docs/design/version-mark/configuration/tool-config.md +++ b/docs/design/version-mark/configuration/tool-config.md @@ -1,32 +1,53 @@ -### ToolConfig Unit - -#### Overview - -The `ToolConfig` record (`VersionMarkConfig.cs`) represents the configuration for a single -tool entry. It holds two dictionaries keyed by OS name: - -| Dictionary | Key values | Purpose | -|------------|-------------------------------------------------|------------------------------------| -| `Command` | `""` (default), `"win"`, `"linux"`, `"macos"` | Shell command to run | -| `Regex` | `""` (default), `"win"`, `"linux"`, `"macos"` | Regex pattern with `version` group | - -#### OS-Specific Overrides - -`GetEffectiveCommand(string os)` and `GetEffectiveRegex(string os)` both take a concrete, -already-resolved OS string — they do not accept `null` and do not call `GetCurrentOs` -internally. The caller is responsible for resolving the OS once (e.g. `os ?? ToolConfig.GetCurrentOs()` -in `FindVersions`) and passing the resulting string. Each method looks up the OS-specific key -first, falling back to the default (`""`) key. When no default (`""`) key is present either, an -`InvalidOperationException` is thrown. This satisfies requirements `VersionMark-ToolConfig-EffectiveCommand` -and `VersionMark-ToolConfig-EffectiveRegex`. - -#### YAML Parsing - -Tool YAML parsing is performed by the private `VersionMarkConfig.ValidateTool` method. -It reads a `YamlMappingNode` and populates the command and regex dictionaries. Known keys -are `command`, `command-win`, `command-linux`, `command-macos`, `regex`, `regex-win`, -`regex-linux`, and `regex-macos`. Unknown keys produce a warning lint issue but do not -prevent loading. At least one `command` (either the default `command` or an OS-specific -`command-win`/`command-linux`/`command-macos`) and at least one `regex` (either the default -`regex` or an OS-specific `regex-win`/`regex-linux`/`regex-macos`) are required; their -complete absence produces an error lint issue. This satisfies `VersionMark-Configuration-ToolDefinition`. +### ToolConfig + +#### Purpose + +`ToolConfig` (`VersionMarkConfig.cs`) represents the configuration for a single tool +entry in a `.versionmark.yaml` file. It holds per-OS command and regex dictionaries, +and provides helper methods to resolve the effective command and regex for a given +operating system, applying OS-specific overrides where present and falling back to the +default entry. + +#### Data Model + +| Dictionary | Key values | Purpose | +|------------|------------------------------------------------|-------------------------------------| +| `Command` | `""` (default), `"win"`, `"linux"`, `"macos"` | Shell command to execute | +| `Regex` | `""` (default), `"win"`, `"linux"`, `"macos"` | Regex pattern with `version` group | + +Both dictionaries are populated by `VersionMarkConfig.ValidateTool` during YAML parsing. +The empty-string key `""` holds the default (OS-independent) value. + +#### Key Methods + +**`GetEffectiveCommand(string os)`** — Returns the command for the specified OS string. +Looks up the OS-specific key first; if absent, falls back to the default `""` key. Throws +`InvalidOperationException` if neither key is present. + +**`GetEffectiveRegex(string os)`** — Identical lookup logic to `GetEffectiveCommand`, but +applied to the `Regex` dictionary. + +**`GetCurrentOs()` (static)** — Returns the lowercase OS identifier string: `"win"` on +Windows, `"linux"` on Linux, `"macos"` on macOS. Used by callers that need to resolve the +OS once before calling `GetEffectiveCommand` / `GetEffectiveRegex`. + +#### Error Handling + +| Condition | Behavior | +|--------------------------------------------|---------------------------------------------| +| No matching OS key and no default `""` key | `InvalidOperationException` thrown | +| No `command` field (`command`, `command-win`, `command-linux`, or `command-macos`) is defined | Error `LintIssue` added by `ValidateTool` | +| No `regex` field (`regex`, `regex-win`, `regex-linux`, or `regex-macos`) is defined | Error `LintIssue` added by `ValidateTool` | +| Unknown YAML key for a tool entry | Warning `LintIssue` added by `ValidateTool` | +| Empty value for a known YAML key | Error `LintIssue` added by `ValidateTool` | + +#### Dependencies + +- `System.Runtime.InteropServices.RuntimeInformation` (BCL) — OS detection in + `GetCurrentOs`. + +#### Callers + +- `VersionMarkConfig.ValidateTool` — constructs `ToolConfig` instances from parsed YAML. +- `VersionMarkConfig.FindVersions` — calls `GetEffectiveCommand` and `GetEffectiveRegex` + with the already-resolved OS string. diff --git a/docs/design/version-mark/configuration/version-mark-config.md b/docs/design/version-mark/configuration/version-mark-config.md index dc1cb82..4ce435d 100644 --- a/docs/design/version-mark/configuration/version-mark-config.md +++ b/docs/design/version-mark/configuration/version-mark-config.md @@ -1,98 +1,101 @@ -### VersionMarkConfig Unit - -#### Overview - -The `VersionMarkConfig` record holds a `Dictionary` mapping tool names -to their configurations. It is the top-level entry point for loading configuration from -the `.versionmark.yaml` file. - -#### Load Method - -`Load` is the primary entry point for loading configuration with integrated linting. It: - -1. Checks that the file exists; adds an error issue if not. -2. Parses the YAML stream and validates the root node is a mapping. -3. Locates the `tools` mapping key; adds an error issue if absent. -4. Iterates tool entries, calling the private `ValidateTool` method for each. -5. Validates that at least one tool is present. - -The method returns a `VersionMarkLoadResult` containing both the parsed configuration and -a list of `LintIssue` records. YAML parse errors are captured as error-level issues with -source location. This satisfies requirements `VersionMark-Configuration-YamlConfig`, -`VersionMark-Configuration-ValidateTools`, and `VersionMark-Configuration-ParseError`. - -##### Error-Handling Strategy - -`Load` uses an accumulate-and-continue approach: rather than aborting on the first error, -all warnings and errors are collected in a single `issues` list across the entire file. -This allows a single validation pass to surface all problems. - -The `toolIssuesBefore` snapshot pattern isolates per-tool validation: `ValidateTool` -records the list length before processing a tool entry and compares it after. When any -new error-severity issue was added, `toolConfig` is set to `null` for that tool, ensuring -a partially valid tool does not contribute broken entries to the result. - -The returned `VersionMarkLoadResult` carries a `null` `Config` property when any -error-severity issue exists, so callers can distinguish a warnings-only load from a -failure without iterating the issue list themselves. - -#### ValidateTool Helper - -The private `ValidateTool` method processes a single tool's `YamlMappingNode`. It: - -1. Records the current issue count (`toolIssuesBefore`) as a snapshot. -2. Iterates all key-value pairs, populating `commands` and `regexes` dictionaries. -3. Reports unknown keys as warnings and empty values as errors. -4. Calls `TryCompileRegex` for `regex` and OS-specific regex values to validate compilation - and the presence of the required `version` named capture group. -5. Reports missing required `command` or `regex` fields after scanning all entries. -6. Sets `toolConfig` to `null` when any new errors were added since the snapshot; - otherwise returns a `ToolConfig` constructed from the validated dictionaries. - -#### TryCompileRegex Helper - -The private `TryCompileRegex` method attempts to compile a regex pattern with -`RegexOptions.Multiline | RegexOptions.IgnoreCase` and a one-second timeout. If -compilation fails (invalid pattern syntax), it appends an error-level `LintIssue` to -the shared list and returns `null`. On success it returns the compiled `Regex` for -group-name inspection by `ValidateTool`. - -#### ReadFromFile Method - -`ReadFromFile` is a backward-compatibility wrapper that delegates to `Load`. It throws -`ArgumentException` if any error-level lint issues are present. Use `Load` directly when -you need access to lint issues. - -#### FindVersions Method - -`FindVersions` accepts a list of tool names, a job ID, and an optional OS name. It resolves -the OS once up-front — `os ?? ToolConfig.GetCurrentOs()` — so that all tools use the same -consistent platform for the entire call. For each named tool it: - -1. Looks up the `ToolConfig` (throws `ArgumentException` for unknown tools). -2. Calls `GetEffectiveCommand(resolvedOs)` and `GetEffectiveRegex(resolvedOs)` using the - already-resolved OS string. -3. Calls the private `RunCommand` helper to execute the command in a shell. -4. Calls the private `ExtractVersion` helper to apply the regex. -5. Stores the result in a `versions` dictionary. - -The optional `os` parameter (`null` by default) enables callers to override OS detection, -which is particularly useful for unit testing cross-OS scenarios without depending on the -actual runtime platform. Passing an OS name for which no tool command or regex is defined -(and no default entry exists) raises `InvalidOperationException`. The method returns a -`VersionInfo` record. This satisfies requirements `VersionMark-Capture-Command` and -`VersionMark-Capture-MultipleTools`. - -#### RunCommand Helper - -`RunCommand` runs the command through the OS shell (`cmd.exe /c` on Windows, `/bin/sh -c` -on other platforms) using `Process.Start` with redirected stdout and stderr. Output and -error streams are read asynchronously to prevent pipe-deadlock. A non-zero exit code -raises `InvalidOperationException`. This satisfies `VersionMark-Capture-Command`. - -#### ExtractVersion Helper - -`ExtractVersion` compiles the regex with `Multiline | IgnoreCase` and a 1-second timeout, -matches against the command output, and returns the value of the named `version` capture -group. Missing match or missing group raises `InvalidOperationException`. This satisfies -`VersionMark-Capture-Command`. +### VersionMarkConfig + +#### Purpose + +`VersionMarkConfig` is the top-level record for a loaded `.versionmark.yaml` configuration +file. It holds the full set of tool definitions and exposes the `Load` method, which +parses and validates the YAML file in a single pass, accumulating all warnings and errors. +It also provides `FindVersions`, which executes the configured commands to capture tool +version strings for the current CI/CD job. + +#### Data Model + +| Property | Type | Description | +|----------|-----------------------------------|-----------------------------------------------| +| `Tools` | `Dictionary` | Maps tool names to their per-OS configuration | + +The dictionary is populated during `Load`. Tools with error-level issues are excluded; +tools with only warnings are included. + +#### Key Methods + +**`Load(string filePath)` (static)** — Primary entry point for loading and validating the +configuration file. Performs the following steps: + +1. Checks file existence; adds an error issue if absent. +2. Parses the YAML stream; captures parse errors as error-level issues with source location. +3. Validates the root node is a mapping and locates the `tools` key. +4. Iterates all tool entries, calling the private `ValidateTool` for each. +5. Validates at least one tool is present. +6. Returns a `VersionMarkLoadResult` with the loaded `VersionMarkConfig` (or `null` if any + errors were found) and the complete issue list. + +The accumulate-and-continue strategy collects all issues in a single pass. A +`toolIssuesBefore` snapshot isolates per-tool validation: when any new error-severity +issues are added while processing a tool, that tool is excluded from the result rather +than contributing a broken entry. + +**`ReadFromFile(string filePath)` (static)** — Backward-compatibility wrapper that calls +`Load` and throws `ArgumentException` if any error-level issues are present. Use `Load` +directly when access to lint issues is needed. + +**`FindVersions(IEnumerable toolNames, string jobId, string? os = null)`** — +Resolves the OS once (`os ?? ToolConfig.GetCurrentOs()`), then for each named tool: +looks up the `ToolConfig`, calls `GetEffectiveCommand` and `GetEffectiveRegex` with the +resolved OS, calls the private `RunCommand` helper, calls `ExtractVersion`, and stores +the result. Returns a `VersionInfo` record. + +**`ValidateTool(string name, YamlMappingNode node, ...)` (private)** — Processes a single +tool's `YamlMappingNode`. Iterates all key-value pairs, populating command and regex +dictionaries. Reports unknown keys as warnings, empty values as errors, and calls +`TryCompileRegex` to validate each regex entry and verify the `version` named capture +group is present. Returns `null` for that tool if any new errors were added. + +**`TryCompileRegex(string pattern, ...)` (private)** — Compiles the pattern with +`RegexOptions.Multiline | RegexOptions.IgnoreCase` and a one-second timeout. Appends an +error-level issue on compilation failure and returns `null`; returns the compiled `Regex` +on success. + +**`RunCommand(string command)` (private)** — Runs the command through the OS shell +(`cmd.exe /c` on Windows, `/bin/sh -c` on other platforms) using `Process.Start` with +redirected stdout and stderr. Streams are read asynchronously to prevent pipe-deadlock. +Throws `InvalidOperationException` on non-zero exit code. + +**`ExtractVersion(string output, string regexPattern, string toolName)` (private)** — +Compiles the regex, matches against the command output, and returns the value of the named +`version` capture group. The `toolName` parameter is included solely to produce actionable +error messages that identify which tool's version could not be extracted. Throws +`InvalidOperationException` when no match or group is found. + +#### Error Handling + +| Condition | Behavior | +|---------------------------------------------|------------------------------------------------------| +| File does not exist | Error `LintIssue` added; `Config` returns null | +| YAML parse error | Error `LintIssue` with source location; parse stops | +| Root node is not a mapping | Error `LintIssue`; `Config` returns null | +| Missing `tools` key | Error `LintIssue`; `Config` returns null | +| No tools defined | Error `LintIssue`; `Config` returns null | +| Unknown YAML key in tool entry | Warning `LintIssue`; tool otherwise valid | +| Invalid regex pattern | Error `LintIssue`; tool excluded from result | +| Missing `version` capture group in regex | Error `LintIssue`; tool excluded from result | +| Command exits with non-zero code | `InvalidOperationException` in `FindVersions` | +| Version group not found in command output | `InvalidOperationException` in `FindVersions` | +| Error-level issues present in `ReadFromFile`| `ArgumentException` thrown | + +#### Dependencies + +- `ToolConfig` (this unit's companion in the same file) — per-tool configuration. +- `LintIssue`, `VersionMarkLoadResult` (Configuration subsystem) — issue and result types. +- `VersionInfo` (Capture subsystem) — return type of `FindVersions`. +- `YamlDotNet` (OTS) — YAML parsing. +- `System.Text.RegularExpressions` (BCL) — regex compilation in `TryCompileRegex` and + `ExtractVersion`. +- `System.Diagnostics.Process` (BCL) — shell command execution in `RunCommand`. + +#### Callers + +- `Program.RunCapture` — calls `VersionMarkConfig.Load` then `FindVersions`. +- `Program.RunLint` — calls `VersionMarkConfig.Load` to validate configuration. +- `Validation.RunCaptureTest`, `Validation.RunLintValidTest`, + `Validation.RunLintInvalidTest` — exercise `Load` indirectly via `Program.Run`. diff --git a/docs/design/version-mark/publishing.md b/docs/design/version-mark/publishing.md index 613c3f2..529d4c1 100644 --- a/docs/design/version-mark/publishing.md +++ b/docs/design/version-mark/publishing.md @@ -1,71 +1,56 @@ -## Publishing Subsystem +## Publishing ### Overview -The publish subsystem is responsible for generating a human-readable markdown version -report from captured JSON files. It reads the version data produced by the capture -subsystem and consolidates identical versions across jobs, flagging any conflicts. +The Publishing subsystem is responsible for generating a human-readable markdown version +report from captured JSON files. It reads the version data produced by the Capture +subsystem and consolidates identical versions across jobs, flagging any conflicts. It +consists of a single unit: `MarkdownFormatter`. -The publish subsystem consists of a single unit: `MarkdownFormatter`, which converts -a collection of `VersionInfo` records into a markdown string. +### Interfaces -### MarkdownFormatter.Format Interface +**`MarkdownFormatter.Format(IEnumerable versionInfos, int reportDepth = 2)`**: +Converts a collection of captured version records into a markdown string. -`MarkdownFormatter.Format` is an `internal static` method with the signature: - -```csharp -public static string Format(IEnumerable versionInfos, int reportDepth = 2) -``` +- *Type*: In-process .NET public API (static method). +- *Role*: Provider. +- *Contract*: Accepts a sequence of `VersionInfo` records and returns a complete markdown + string containing a `Tool Versions` section. `reportDepth` controls the heading level: + `reportDepth = 2` produces `## Tool Versions`, `reportDepth = 1` produces + `# Tool Versions`, and so on. When all jobs report the same version for a tool, a single + bullet is emitted with no job IDs; when versions differ, one bullet per distinct version + is emitted with contributing job IDs in parentheses. +- *Constraints*: `reportDepth` must be greater than zero; passing `0` or a negative value + throws `ArgumentOutOfRangeException`. An empty `versionInfos` sequence produces valid + markdown with only the heading. | Parameter | Type | Description | |----------------|----------------------------|---------------------------------------------------------| | `versionInfos` | `IEnumerable` | The captured version records to include in the report | | `reportDepth` | `int` | Heading depth for the section title (default: 2) | -**Returns**: A markdown-formatted string ready to be written to the report file. +### Design -`reportDepth` must be greater than zero. A value of `0` or less causes -`ArgumentOutOfRangeException` to be thrown. +The Publishing subsystem consists of the single `MarkdownFormatter` static class, which +implements a three-step pipeline: -### Normal-Operation Walkthrough +1. **`BuildToolVersionsDictionary`** — iterates all `VersionInfo` records and builds a + `Dictionary>` mapping tool names to the + `(jobId, version)` pairs observed across all input records. -The `--publish` command follows this pipeline in `Program.RunPublish`: +2. **`GenerateMarkdown`** — writes the `Tool Versions` heading (using + `new string('#', reportDepth)`), sorts tool names alphabetically, then calls + `FormatVersionEntries` for each. -1. Validate that `--report` was specified; report an error and return if not. -2. Resolve glob patterns (default `versionmark-*.json` when none supplied). -3. Scan the current directory for files matching the glob patterns using - `Microsoft.Extensions.FileSystemGlobbing.Matcher`. -4. If no files match, write an error message via `context.WriteError` and return. -5. Call `VersionInfo.LoadFromFile` for each matched file to deserialize the JSON. -6. Pass the resulting `IEnumerable` to `MarkdownFormatter.Format` - together with `context.ReportDepth`. -7. Write the returned markdown string to the file specified by `--report`. +3. **`FormatVersionEntries`** — applies the consolidation rule: if all job entries for a + tool share the same version, emits a single `- **tool**: version` bullet; if versions + differ, emits one bullet per distinct version with contributing job IDs in parentheses. -### Error Handling +The subsystem depends on `VersionInfo` (Capture subsystem) as its input data model. The +following error conditions are handled before the pipeline runs: -- **`--report` not specified**: `context.WriteError` with mention of `--report`; exit 1 -- **No files match the glob patterns**: `context.WriteError` listing the patterns; exit 1 +- **`--report` not specified**: `context.WriteError` in `Program.RunPublish`; exit 1. +- **No files match the glob patterns**: `context.WriteError` listing the patterns; exit 1. - **`VersionInfo.LoadFromFile` throws**: `context.WriteError` with the exception message; - exit 1 -- **`reportDepth <= 0`**: `ArgumentOutOfRangeException` thrown by - `MarkdownFormatter.Format` - -### reportDepth Configuration and Conflict-Display Logic - -`context.ReportDepth` is populated from the `--report-depth` CLI argument -(default: the value of `--depth`, which itself defaults to `1`). - -When different jobs report different versions for the same tool, `MarkdownFormatter` -displays one bullet per distinct version, with the contributing job IDs listed in -parentheses after the version string. For example: - -```markdown -- **dotnet**: 8.0.0 (job-linux, job-windows) -- **dotnet**: 9.0.0 (job-preview) -``` - -When all jobs report the same version the job IDs are suppressed: - -```markdown -- **dotnet**: 8.0.0 -``` + exit 1. +- **`reportDepth <= 0`**: `ArgumentOutOfRangeException` thrown by `MarkdownFormatter.Format`. diff --git a/docs/design/version-mark/publishing/markdown-formatter.md b/docs/design/version-mark/publishing/markdown-formatter.md index f204aff..7944eed 100644 --- a/docs/design/version-mark/publishing/markdown-formatter.md +++ b/docs/design/version-mark/publishing/markdown-formatter.md @@ -1,63 +1,60 @@ -### MarkdownFormatter Unit +### MarkdownFormatter -#### Overview +#### Purpose -The `MarkdownFormatter` class (`MarkdownFormatter.cs`) provides the `Format` static method -that converts a collection of `VersionInfo` records into a markdown string. This satisfies -requirements `VersionMark-Formatter-Structure`, `VersionMark-Formatter-JobId`, -`VersionMark-Formatter-Versions`, `VersionMark-Formatter-MarkdownList`, and -`VersionMark-Formatter-MarkdownConsolidation`. +`MarkdownFormatter` (`MarkdownFormatter.cs`) converts a collection of `VersionInfo` records +into a markdown string. It consolidates version data across multiple CI/CD jobs: when all +jobs report the same version for a tool, a single bullet is emitted; when versions differ, +one bullet per distinct version is emitted with contributing job IDs. The result is written +to a file by `Program.RunPublish`. -#### Format Method +#### Data Model -`Format` accepts an `IEnumerable` and an optional `reportDepth` (default 2), -and returns a markdown string. +`MarkdownFormatter` is a static class with no instance state. It uses a +`Dictionary>` internally to accumulate +per-tool version observations before generating output. -The method delegates to two helpers: +#### Key Methods -1. **`BuildToolVersionsDictionary`**: iterates all `VersionInfo` records and builds a - `Dictionary>` mapping each tool name to - the list of `(jobId, version)` pairs seen across all input files. -2. **`GenerateMarkdown`**: writes the heading, sorts tools alphabetically, and calls - `FormatVersionEntries` for each tool. +**`Format(IEnumerable versionInfos, int reportDepth = 2)` (static)** — +Accepts a sequence of `VersionInfo` records and returns a complete markdown string. The +`Tool Versions` section heading level is controlled by `reportDepth` (e.g., `reportDepth = +2` produces `## Tool Versions`). Delegates to `BuildToolVersionsDictionary` and +`GenerateMarkdown`. -#### Output Structure +**`BuildToolVersionsDictionary(IEnumerable versionInfos)` (private static)** +— Iterates all records and builds a `Dictionary>` mapping each tool name to its observed `(jobId, version)` pairs. -The markdown output begins with a `Tool Versions` section heading whose level is -controlled by `reportDepth`. For example, with `reportDepth = 2` the heading is -`## Tool Versions`; with `reportDepth = 3` it is `### Tool Versions`. +**`GenerateMarkdown(Dictionary<...>, int reportDepth)` (private static)** — Writes the +heading using `new string('#', reportDepth)`, sorts tool names alphabetically, then calls +`FormatVersionEntries` for each tool. -Each tool is then listed as one or more markdown bullet items below the heading. +**`FormatVersionEntries(StringBuilder markdown, string tool, List<(string JobId, string Version)> versions)` (private static)** — Applies the +consolidation rule. Version groups are ordered alphabetically (case-insensitive) before +per-group bullet lines are emitted: -#### Version Consolidation Logic +- If all entries share the same version: emit `- **tool**: version` (no job IDs). +- If versions differ: emit one `- **tool**: version (job1, job2)` per distinct version + with alphabetically sorted contributing job IDs. -`FormatVersionEntries` implements the consolidation rule: - -- If all `(jobId, version)` pairs for a tool share the **same** version, a single bullet - is emitted: `- **tool**: version` (no job IDs shown). This satisfies - `VersionMark-Formatter-JobId`. -- If versions **differ**, one bullet per distinct version is emitted with the contributing - job IDs in parentheses: `- **tool**: version (job1, job2)`. This satisfies - `VersionMark-Formatter-Versions` and `VersionMark-Formatter-MarkdownList`. - -Both cases use bold tool names. In the multi-version case, each unique version appears on -its own line with the alphabetically-sorted job IDs that produced it enclosed in -parentheses. +#### Error Handling -#### Heading Depth +| Condition | Behavior | +|------------------------|--------------------------------------------------------------| +| `reportDepth <= 0` | `ArgumentOutOfRangeException` thrown before any output | +| `versionInfos` is empty | Returns valid markdown with heading and no bullets | +| Tool name is empty | Emitted as `- ****: version` | +| `JobId` is null/empty | Appears as an empty string in the parenthesised list | -The heading prefix is constructed as `new string('#', reportDepth)`, so `reportDepth = 2` -yields `## Tool Versions`, `reportDepth = 1` yields `# Tool Versions`, and so on. This -satisfies `VersionMark-Formatter-MarkdownConsolidation`. +#### Dependencies -`reportDepth` must be greater than zero. Passing `0` or a negative value causes -`ArgumentOutOfRangeException` to be thrown before any output is generated. +- `VersionInfo` (Capture subsystem) — input data model. +- `System.Text.StringBuilder` (BCL) — output string construction. -#### Error Handling +#### Callers -| Input condition | Behavior | -|----------------------------------|---------------------------------------------------| -| `reportDepth <= 0` | `ArgumentOutOfRangeException` thrown | -| `versionInfos` is empty | Returns valid markdown with heading but no bullets| -| Tool name is empty string | Tool appears in output as `- ****: version` | -| `JobId` is null or empty | Job ID appears as empty string in parentheses | +- `Program.RunPublish` — calls `MarkdownFormatter.Format` with the loaded `VersionInfo` + records and writes the returned string to the report file. +- `Validation.RunPublishTest` — indirectly exercises `MarkdownFormatter.Format` via + `Program.Run`. diff --git a/docs/design/version-mark/self-test.md b/docs/design/version-mark/self-test.md index 47f81c6..d40180e 100644 --- a/docs/design/version-mark/self-test.md +++ b/docs/design/version-mark/self-test.md @@ -1,34 +1,43 @@ -## SelfTest Subsystem +## SelfTest ### Overview The SelfTest subsystem provides built-in verification of the tool's core functionality. -It consists of one unit: `Validation` (the self-validation test runner). - -The validation subsystem is invoked when the `--validate` flag is passed and can write -results to a TRX or JUnit XML file when `--results` is also provided. This satisfies -requirements `VersionMark-CommandLine-Validate` and `VersionMark-CommandLine-Results`. - -### Units - -#### Validation - -The `Validation` class (`Validation.cs`) is the self-validation test runner. It exposes a -single public method, `Run`, which orchestrates all internal self-tests against the tool's -core modes (capture, publish, lint), collects results, prints a summary, and optionally -writes a structured results file. - -See *Validation Unit Design* for the full unit design. - -### Subsystem Interactions - -`Validation.Run` creates temporary directories via the private `TemporaryDirectory` helper -class and uses `PathHelpers.SafePathCombine` from the Utilities subsystem for all path -construction within those directories. - -The subsystem depends on: - -- `DemaConsulting.VersionMark.Cli.Context` — command-line arguments and output -- `DemaConsulting.VersionMark.Capture.VersionInfo` — capture output model -- `DemaConsulting.VersionMark.Program` — re-entrant entry point for internal test runs -- `DemaConsulting.TestResults` — test result collection and serialization +It is invoked when the `--validate` flag is passed and can write results to a TRX or JUnit +XML file when `--results` is also provided. It consists of a single unit: `Validation`. + +### Interfaces + +**`Validation.Run(Context context)`**: Executes the full self-validation suite. + +- *Type*: In-process .NET public API (static method). +- *Role*: Provider. +- *Contract*: Runs four functional tests exercising capture, publish, lint-valid, and + lint-invalid modes. Prints a results summary via `context.WriteLine`. Writes individual + test failures via `context.WriteError`, setting `ExitCode` to `1` if any tests fail. + Optionally writes a TRX or JUnit results file when `context.ResultsFile` is set. +- *Constraints*: Each test runs in an isolated temporary directory using a real re-entrant + call to `Program.Run`. Requires write access to `Path.GetTempPath()`. + +### Design + +The SelfTest subsystem consists of the single `Validation` class, which orchestrates four +independent functional tests against a real (re-entrant) `Program.Run` call inside isolated +temporary directories: + +1. **`RunCaptureTest`** — verifies that `--capture` saves a valid JSON file with the + expected `JobId` and a non-empty `dotnet` version. +2. **`RunPublishTest`** — verifies that `--publish` reads two pre-written JSON files and + produces a markdown report containing the expected headings and version strings. +3. **`RunLintValidTest`** — verifies that `--lint` exits with code `0` for a valid config + file. +4. **`RunLintInvalidTest`** — verifies that `--lint` exits with a non-zero code for an + invalid config file. + +Each test creates a `TemporaryDirectory` (a private disposable helper that cleans up after +itself), constructs a fresh `Context`, changes the current working directory into the temp +directory, and calls `Program.Run`. Results are accumulated in a `TestResults` collection +and serialized by `WriteResultsFile` if `context.ResultsFile` is set. + +The subsystem depends on `Context` (Cli), `VersionInfo` (Capture), `Program` (Cli, called +re-entrantly), `PathHelpers` (Utilities), and `DemaConsulting.TestResults` (OTS). diff --git a/docs/design/version-mark/self-test/validation.md b/docs/design/version-mark/self-test/validation.md index b5d27a1..36c70ff 100644 --- a/docs/design/version-mark/self-test/validation.md +++ b/docs/design/version-mark/self-test/validation.md @@ -1,105 +1,83 @@ -### Validation Unit +### Validation -#### Overview +#### Purpose -The `Validation` class (`Validation.cs`) exposes a single public method, `Run`, and -organizes all test execution internally. +`Validation` (`Validation.cs`) implements the built-in self-validation suite for +VersionMark. It is invoked when `--validate` is passed to the tool. It runs four +functional tests — capture, publish, lint-valid, and lint-invalid — each in an isolated +temporary directory using a re-entrant call to `Program.Run`. Results are printed to the +context output and optionally written to a TRX or JUnit XML file. -#### Run Method +#### Data Model -`Run` orchestrates the self-validation sequence: +`Validation` is a static class with no instance state. Each test is named and tracked in a +`TestResults` collection (from `DemaConsulting.TestResults`) named +`"VersionMark Self-Validation"`. -1. Calls `PrintValidationHeader` to emit a markdown heading (using `context.Depth` to set - the heading level) followed by a table with tool version, machine name, OS version, - .NET runtime, and timestamp. -2. Creates a `TestResults` collection named `"VersionMark Self-Validation"`. -3. Calls `RunCaptureTest`, `RunPublishTest`, `RunLintValidTest`, and `RunLintInvalidTest` - to execute the functional tests. -4. Prints a summary of passed and failed tests, calling `context.WriteError` for the - failed count if any tests failed (which also sets the process exit code to 1). -5. If `context.ResultsFile` is set, calls `WriteResultsFile` to persist the results. +Test names: -#### RunCaptureTest +| Test name | Validates | +|----------------------------------------------------|----------------------------------| +| `VersionMark_CapturesVersions` | Capture mode end-to-end | +| `VersionMark_GeneratesMarkdownReport` | Publish mode end-to-end | +| `VersionMark_LintPassesForValidConfig` | Lint mode: valid config exit 0 | +| `VersionMark_LintReportsErrorsForInvalidConfig` | Lint mode: invalid config exit 1 | -`RunCaptureTest` verifies the capture mode end-to-end: +#### Key Methods -1. Creates a `TemporaryDirectory` (see below). -2. Writes a minimal `.versionmark.yaml` containing only the `dotnet` tool. -3. Constructs a `Context` with `--silent`, `--log `, `--capture`, `--job-id test-job`, - and `--output `. -4. Changes the current directory to the temp directory and calls `Program.Run`. -5. Verifies exit code is 0, output file exists, `JobId` equals `"test-job"`, and `dotnet` - version was captured and is non-empty. +**`Run(Context context)` (static)** — Orchestrates the self-validation sequence: -The test name is `VersionMark_CapturesVersions`, satisfying `VersionMark-Validation-Capture`. +1. Calls `PrintValidationHeader` to emit a markdown heading and environment info table. +2. Creates a `TestResults` collection. +3. Calls all four test helpers in order. +4. Prints a pass/fail summary; calls `context.WriteError` if any tests failed. +5. If `context.ResultsFile` is set, calls `WriteResultsFile`. -#### RunPublishTest +**`RunCaptureTest(Context context, TestResults results)` (private static)** — Creates a +`TemporaryDirectory`, writes a minimal `.versionmark.yaml` containing only `dotnet`, +constructs a fresh `Context` with `--silent`, `--capture`, `--job-id test-job`, and +`--output`, changes the current directory to the temp directory, calls `Program.Run`, then +verifies exit code is 0, the output file exists, `JobId` equals `"test-job"`, and the +`dotnet` version is non-empty. -`RunPublishTest` verifies the publish mode end-to-end: +**`RunPublishTest(Context context, TestResults results)` (private static)** — Creates a +`TemporaryDirectory`, writes two `VersionInfo` JSON files with known content, constructs a +fresh `Context` with `--silent`, `--publish`, `--report`, and `-- versionmark-*.json`, +calls `Program.Run`, then verifies exit code is 0, the report file exists, and contains +`## Tool Versions`, `**dotnet**`, `**node**`, `8.0.0`, and `20.0.0`. -1. Creates a `TemporaryDirectory`. -2. Writes two `VersionInfo` JSON files with known content. -3. Constructs a `Context` with `--silent`, `--log `, `--publish`, `--report `, - `--report-depth 2`, and `-- versionmark-*.json`. -4. Changes the current directory to the temp directory and calls `Program.Run`. -5. Verifies exit code is 0, report file exists, and contains `## Tool Versions`, - `**dotnet**`, `**node**`, `8.0.0`, and `20.0.0`. +**`RunLintValidTest(Context context, TestResults results)` (private static)** — Creates a +`TemporaryDirectory`, writes a minimal `.versionmark.yaml` with a valid `dotnet` tool +entry (including a `(?...)` capture group), constructs a fresh `Context` with +`--silent` and `--lint `, calls `Program.Run`, and verifies exit code is 0. -The test name is `VersionMark_GeneratesMarkdownReport`, satisfying `VersionMark-Validation-Publish`. +**`RunLintInvalidTest(Context context, TestResults results)` (private static)** — Creates +a `TemporaryDirectory`, writes a `bad.versionmark.yaml` with a tool entry that has only a +`command` field and no `regex`, constructs a fresh `Context`, calls `Program.Run`, and +verifies exit code is non-zero. -#### RunLintValidTest +**`WriteResultsFile(Context context, TestResults results)` (private static)** — Inspects +the extension of `context.ResultsFile`: `.trx` → `TrxSerializer.Serialize`; `.xml` → +`JUnitSerializer.Serialize`; other → `context.WriteError`. -`RunLintValidTest` verifies that lint mode exits successfully for a valid configuration file: +#### Error Handling -1. Creates a `TemporaryDirectory`. -2. Writes a minimal `.versionmark.yaml` containing the `dotnet` tool with a valid `command` - and `regex` that includes the `(?...)` capture group. -3. Constructs a `Context` with `--silent`, `--log `, and `--lint `. -4. Calls `Program.Run` and checks that the exit code is 0. +| Condition | Behavior | +|-----------------------------------------------|--------------------------------------------------| +| Any test assertion fails | Failure recorded in `TestResults`; `context.WriteError` called; `ExitCode` set to 1 | +| `TemporaryDirectory` creation fails | `InvalidOperationException` wrapping the original exception | +| `context.ResultsFile` extension unrecognized | `context.WriteError`; no file written | +| `Dispose` on `TemporaryDirectory` fails | `IOException`/`UnauthorizedAccessException` silently suppressed | -The test name is `VersionMark_LintPassesForValidConfig`, satisfying `VersionMark-Validation-Lint`. +#### Dependencies -#### RunLintInvalidTest +- `Context` (Cli subsystem) — output routing and parsed flag values. +- `Program` (Cli subsystem) — called re-entrantly for each functional test. +- `VersionInfo` (Capture subsystem) — constructs and reads JSON data in publish test. +- `PathHelpers` (Utilities subsystem) — used by `TemporaryDirectory` to construct safe + paths under `Path.GetTempPath()`. +- `DemaConsulting.TestResults` (OTS) — `TestResults`, `TrxSerializer`, `JUnitSerializer`. -`RunLintInvalidTest` verifies that lint mode reports errors for an invalid configuration file: +#### Callers -1. Creates a `TemporaryDirectory`. -2. Writes a `bad.versionmark.yaml` containing a tool entry with only a `command` field and - no `regex` field (deliberately invalid). -3. Constructs a `Context` with `--silent`, `--log `, and `--lint `. -4. Calls `Program.Run` and checks that the exit code is non-zero. - -The test name is `VersionMark_LintReportsErrorsForInvalidConfig`, satisfying -`VersionMark-Validation-Lint`. - -#### WriteResultsFile - -`WriteResultsFile` inspects the file extension of `context.ResultsFile`: - -- `.trx` → `TrxSerializer.Serialize` -- `.xml` → `JUnitSerializer.Serialize` -- Other → writes an error via `context.WriteError` - -The method is only called when `context.ResultsFile` is non-null. The null check at the -start of the method is a defensive guard that prevents errors if the call contract changes -in the future. - -This satisfies requirement `VersionMark-Validation-WriteResults`. - -#### TemporaryDirectory - -`TemporaryDirectory` is a private nested class that implements `IDisposable`. It creates -a uniquely named directory under `Path.GetTempPath()` using `PathHelpers.SafePathCombine` -with a `Guid`-based name. - -**Constructor**: Creates the directory with `Directory.CreateDirectory`. Any -`IOException`, `UnauthorizedAccessException`, or `ArgumentException` thrown during -creation is caught and re-thrown as `InvalidOperationException` with a descriptive -message, so callers receive a consistent exception type regardless of the underlying cause. - -**Dispose**: Deletes the directory tree with `Directory.Delete(path, recursive: true)`, -silently suppressing `IOException` and `UnauthorizedAccessException` to avoid exceptions -during finalization or test teardown. - -The serialized content is written with `File.WriteAllText`. This satisfies -`VersionMark-Validate-Results`. +- `Program.Run` (validate dispatch, priority 3) — calls `Validation.Run` when `--validate` is specified. diff --git a/docs/design/version-mark/utilities.md b/docs/design/version-mark/utilities.md index d7ba00a..9a4b5cb 100644 --- a/docs/design/version-mark/utilities.md +++ b/docs/design/version-mark/utilities.md @@ -1,40 +1,66 @@ -## Utilities Subsystem +## Utilities ### Overview The Utilities subsystem provides general-purpose helper classes used by other subsystems within VersionMark. It consists of two units: `GlobMatcher`, which implements glob-pattern -file matching for the Publish mode, and `PathHelpers`, which provides safe path combination -to protect against path-traversal attacks. +file matching for publish mode, and `PathHelpers`, which provides safe path combination to +protect against path-traversal attacks. The subsystem has no dependencies on other +VersionMark subsystems. -This subsystem satisfies requirements `VersionMark-Utilities-GlobMatch` and -`VersionMark-Utilities-SafePath`. +### Interfaces -### Units +**`GlobMatcher.FindMatchingFiles(string[] globPatterns)`**: Resolves an array of glob +patterns into a sorted, deduplicated list of full file paths. -#### GlobMatcher +- *Type*: In-process .NET public API (internal static method). +- *Role*: Provider. +- *Contract*: Accepts an array of glob patterns (relative or absolute). Relative patterns + are evaluated against `Directory.GetCurrentDirectory()`; absolute patterns are each + evaluated against their own root directory. Returns a sorted, deduplicated + `List` of full file paths. Deduplication uses a `HashSet` with a + file-system-appropriate comparer (ordinal ignore-case on Windows, ordinal elsewhere). +- *Constraints*: No explicit error handling; invalid patterns or inaccessible directories + are handled by the BCL and FileSystemGlobbing. Callers must handle an empty result. -The `GlobMatcher` class (`GlobMatcher.cs`) provides glob-pattern file matching. It exposes -two methods: `FindMatchingFiles`, which accepts an array of glob patterns and returns a -sorted, deduplicated list of matching file paths; and `SplitAbsolutePattern`, which splits -an absolute glob pattern into its root directory and relative pattern components. +**`GlobMatcher.SplitAbsolutePattern(string absolutePattern)`**: Splits an absolute glob +pattern into its root directory and relative pattern components. -See *GlobMatcher Unit Design* for the full unit design. +- *Type*: In-process .NET public API (internal static method). +- *Role*: Provider. +- *Contract*: Returns `(string rootDir, string relativePattern)` by locating the first + wildcard character and finding the last directory separator before it. +- *Constraints*: Input must be an absolute (rooted) path. -#### PathHelpers +**`PathHelpers.SafePathCombine(string basePath, string relativePath)`**: Combines two paths +while verifying the result remains within the base directory. -The `PathHelpers` class (`PathHelpers.cs`) provides a single static method, -`SafePathCombine`, which safely combines a base path and a relative path while -preventing path-traversal attacks. It is used by `SelfTest.Validation` when -constructing paths inside temporary directories. +- *Type*: In-process .NET public API (internal static method). +- *Role*: Provider. +- *Contract*: Combines `basePath` and `relativePath` using `Path.Combine`, resolves both to + absolute form, then uses `Path.GetRelativePath` to verify the result lies within + `basePath`. Returns the combined path on success. +- *Constraints*: Throws `ArgumentNullException` for null inputs. Throws `ArgumentException` + (identifying `relativePath`) when the combined path escapes the base directory. -See *PathHelpers Unit Design* for the full unit design. +### Design -### Subsystem Interactions +The Utilities subsystem contains two independent, stateless units with no dependency on +each other or on any other VersionMark subsystem: -`GlobMatcher.FindMatchingFiles` is called by the Cli Subsystem (`Program.RunPublish`) to -resolve the glob patterns supplied on the command line into a concrete list of JSON capture -files. `PathHelpers.SafePathCombine` is called by the SelfTest subsystem (`Validation.Run`) -when constructing paths inside temporary directories. The Utilities subsystem has no -dependencies on other VersionMark subsystems; it depends only on -`Microsoft.Extensions.FileSystemGlobbing` for pattern evaluation. +- **`GlobMatcher`** — delegates pattern evaluation to + `Microsoft.Extensions.FileSystemGlobbing`. Relative patterns are batched into a single + `Matcher` run against `Directory.GetCurrentDirectory()`; absolute patterns are each + evaluated against their own root directory obtained via `SplitAbsolutePattern`. Results + are deduplicated with a `HashSet` using a file-system-appropriate comparer + and returned sorted. + `GlobMatcher.FindMatchingFiles` is called by `Program.RunPublish` to resolve command-line + glob patterns into a concrete list of JSON capture files. + +- **`PathHelpers`** — uses `Path.GetFullPath` to canonicalize both the base path and the + combined candidate, then uses `Path.GetRelativePath` to verify the candidate lies within + the base. The containment check treats only a relative result of exactly `".."` or one + starting with `".." + separator` (or a rooted result) as an escape, avoiding false + positives for legitimate in-base names that begin with `..`. + `PathHelpers.SafePathCombine` is called by `Validation.TemporaryDirectory` (SelfTest + subsystem) when constructing paths inside temporary directories. diff --git a/docs/design/version-mark/utilities/glob-matcher.md b/docs/design/version-mark/utilities/glob-matcher.md index 00d0302..79e1950 100644 --- a/docs/design/version-mark/utilities/glob-matcher.md +++ b/docs/design/version-mark/utilities/glob-matcher.md @@ -1,65 +1,64 @@ -### GlobMatcher Unit +### GlobMatcher -#### Overview +#### Purpose -`GlobMatcher` is a static utility class that provides glob-pattern file matching. It -supports both relative patterns (evaluated against the current directory) and absolute -patterns (evaluated from their own root directory), and returns a sorted, deduplicated -list of full file paths. It uses `Microsoft.Extensions.FileSystemGlobbing` for pattern -evaluation. +`GlobMatcher` (`GlobMatcher.cs`) is a static utility class that resolves an array of glob +patterns into a sorted, deduplicated list of full file paths. It supports both relative +patterns (evaluated against the current working directory) and absolute patterns (evaluated +from their own root directory). `GlobMatcher.FindMatchingFiles` is called by +`Program.RunPublish` to resolve command-line glob patterns into a concrete list of JSON +capture files. -#### FindMatchingFiles Method +#### Data Model -```csharp -internal static List FindMatchingFiles(string[] globPatterns) -``` +`GlobMatcher` is a static class with no instance state. Internally it accumulates +absolute patterns into separate `Matcher` invocations and collects relative patterns for a +single batched `Matcher` run. Results are deduplicated using a file-system-appropriate comparer: ordinal case-insensitive on Windows, ordinal case-sensitive on other platforms. -Finds all files matching the specified glob patterns and returns them as a sorted list of -full paths. +#### Key Methods -**Processing steps:** +**`FindMatchingFiles(string[] globPatterns)` (internal static)** — Resolves all patterns +and returns a sorted `List` of full file paths. -1. Iterate over each pattern in `globPatterns`. -2. If a pattern is rooted (`Path.IsPathRooted`), call `SplitAbsolutePattern` to obtain the - root directory and relative pattern, then use a `Matcher` against that directory. -3. If the pattern is relative, collect it into a separate list. -4. After iterating, if any relative patterns were collected, run a single `Matcher` against - `Directory.GetCurrentDirectory()` covering all relative patterns. -5. Combine all matches into a `HashSet` (case-insensitive) to deduplicate, then - return the sorted result. +Processing steps: -#### SplitAbsolutePattern Helper +1. Iterate each pattern in `globPatterns`. +2. For rooted patterns, call `SplitAbsolutePattern` to obtain `(rootDir, + relativePattern)`, then run a `Matcher` against that `rootDir`. +3. Collect non-rooted patterns into a list. +4. If any relative patterns were collected, run a single `Matcher` against + `Directory.GetCurrentDirectory()` covering all relative patterns at once. +5. Combine all matches into a `HashSet` using the file-system-appropriate + comparer (ordinal case-insensitive on Windows, ordinal case-sensitive on other + platforms) for deduplication, then return the sorted result. -```csharp -internal static (string rootDir, string relativePattern) SplitAbsolutePattern(string absolutePattern) -``` +Batching relative patterns into one `Matcher` run reduces directory enumeration overhead +compared to one run per pattern. Sorted output makes results deterministic. -Splits an absolute glob pattern into its root directory and the relative pattern to be -passed to the `Matcher`. +**`SplitAbsolutePattern(string absolutePattern)` (internal static)** — Splits an absolute +glob pattern into `(string rootDir, string relativePattern)`. -**Algorithm:** +Algorithm: -1. Determine the path root via `Path.GetPathRoot`. -2. Find the index of the first wildcard character (`*`, `?`, or `[`). -3. If no wildcard is found, return `(Path.GetDirectoryName, Path.GetFileName)`. -4. Find the last directory separator before the wildcard using `LastIndexOfAny` searching - backwards from the wildcard position. -5. Split at that separator, handling the drive-root edge case where the separator is the - first character (e.g. `/`) or where the root segment lacks a trailing separator (e.g. +1. Find the index of the first wildcard character (`*`, `?`, or `[`). +2. If no wildcard, return `(Path.GetDirectoryName, Path.GetFileName)`. +3. Find the last directory separator before the wildcard. +4. Split at that separator, handling edge cases where the separator is the first + character (e.g., `/`) or where the root segment has no trailing separator (e.g., `C:` on Windows). -#### Design Decisions - -- **Separate absolute and relative handling**: Absolute patterns are rooted at a specific - directory and must be evaluated there, while relative patterns are evaluated relative to - the current directory. Separating the two cases avoids incorrect matches. -- **Single Matcher for relative patterns**: Collecting all relative patterns into one - `Matcher` run reduces directory enumeration overhead compared to one run per pattern. -- **Case-insensitive deduplication**: Using a case-insensitive `HashSet` prevents - duplicates when patterns overlap or when the file system is case-insensitive. -- **Sorted output**: Returning a sorted list makes the output deterministic, simplifying - testing and producing a consistent report order. - -`GlobMatcher` is used by `Program.RunPublish` to resolve command-line glob patterns into -a concrete file list. This satisfies requirements `VersionMark-GlobMatcher-FindFiles` and -`VersionMark-GlobMatcher-AbsolutePaths`. +#### Error Handling + +`GlobMatcher` contains no explicit error handling. Invalid patterns or inaccessible +directories are handled by the BCL and `Microsoft.Extensions.FileSystemGlobbing`. Callers +must handle an empty result list. + +#### Dependencies + +- `Microsoft.Extensions.FileSystemGlobbing` (OTS) — `Matcher` for pattern evaluation. +- `System.IO` (BCL) — `Directory.GetCurrentDirectory`, `Path.IsPathRooted`, etc. + +#### Callers + +- `Program.RunPublish` — calls `GlobMatcher.FindMatchingFiles` with the glob patterns + parsed from `context.GlobPatterns`. diff --git a/docs/design/version-mark/utilities/path-helpers.md b/docs/design/version-mark/utilities/path-helpers.md index 4d41df9..cabad57 100644 --- a/docs/design/version-mark/utilities/path-helpers.md +++ b/docs/design/version-mark/utilities/path-helpers.md @@ -1,48 +1,57 @@ -### PathHelpers Unit +### PathHelpers -#### Overview +#### Purpose -`PathHelpers` is a static utility class that provides a safe path-combination method. It -protects callers against path-traversal attacks by verifying the resolved combined path stays -within the base directory. Note that `Path.GetFullPath` normalizes `.`/`..` segments but does -not resolve symlinks or reparse points, so this check guards against string-level traversal -only. +`PathHelpers` (`PathHelpers.cs`) is a static utility class that provides safe path +combination, protecting callers against path-traversal attacks. It verifies that the +resolved combined path remains within the specified base directory before returning it. +`PathHelpers.SafePathCombine` is used by `Validation.TemporaryDirectory` when constructing +file paths inside isolated temporary directories for self-validation tests. -#### SafePathCombine Method +**Note**: `Path.GetFullPath` normalizes `.`/`..` segments but does not resolve symlinks +or reparse points, so this check guards against string-level traversal only. -```csharp -internal static string SafePathCombine(string basePath, string relativePath) -``` +#### Data Model -Combines `basePath` and `relativePath` safely, ensuring the resulting path remains within -the base directory. +`PathHelpers` is a static class with no instance state. -**Validation steps:** +#### Key Methods + +**`SafePathCombine(string basePath, string relativePath)` (internal static)** — Combines +`basePath` and `relativePath` safely, verifying the result lies within `basePath`. + +Validation steps: 1. Reject null inputs via `ArgumentNullException.ThrowIfNull`. -2. Combine the paths with `Path.Combine` to produce the candidate path (preserving the - caller's relative/absolute style). +2. Combine the paths with `Path.Combine` to produce the candidate path. 3. Resolve both `basePath` and the candidate to absolute form with `Path.GetFullPath`. -4. Compute `Path.GetRelativePath(absoluteBase, absoluteCombined)` and reject the input if - the result is exactly `".."`, starts with `".."` followed by `Path.DirectorySeparatorChar` - or `Path.AltDirectorySeparatorChar`, or is itself rooted (absolute), which would indicate - the combined path escapes the base directory. - -#### Design Decisions - -- **`Path.GetRelativePath` for containment check**: Using `GetRelativePath` to verify - containment handles root paths (e.g. `/`, `C:\`), platform case-sensitivity, and - directory-separator normalization natively. The containment test should treat `..` as an - escaping segment only when it is the entire relative result or is followed by a directory - separator, avoiding false positives for valid in-base names such as `..data`. -- **Post-combine canonical-path check**: Resolving paths after combining handles all traversal - patterns — `../`, embedded `/../`, absolute-path overrides, and platform edge cases — - without fragile pre-combine string inspection of `relativePath`. -- **ArgumentException on invalid input**: Callers receive a specific `ArgumentException` - identifying `relativePath` as the problematic parameter, making debugging straightforward. -- **No logging or error accumulation**: `SafePathCombine` is a pure utility method that throws - on invalid input; it does not interact with the `Context` or any output mechanism. - -`PathHelpers` is used by `SelfTest.Validation` when constructing paths inside temporary -directories for self-validation tests. This satisfies requirement -`VersionMark-PathHelpers-SafeCombine`. +4. Compute `Path.GetRelativePath(absoluteBase, absoluteCombined)` and reject the input + when the result is `".."`, starts with `".."` followed by a directory separator, or + is itself rooted — all of which indicate the combined path escapes the base directory. +5. Return the combined path on success. + +Using `Path.GetRelativePath` for the containment check handles root paths, platform +case-sensitivity, and directory-separator normalization natively. The test is applied only +to `".."` as the entire result or followed by a directory separator, avoiding false +positives for valid in-base names that begin with `..` (such as `..data`). Resolving paths +after combining handles all traversal patterns — `../`, embedded `/../`, absolute-path +overrides, and platform edge cases — without fragile pre-combine string inspection. + +#### Error Handling + +| Condition | Behavior | +|----------------------------------------------|-------------------------------------------------------------| +| `basePath` is null | `ArgumentNullException` thrown | +| `relativePath` is null | `ArgumentNullException` thrown | +| Combined path escapes the base directory | `ArgumentException` thrown, identifying `relativePath` | + +#### Dependencies + +- `System.IO` (BCL) — `Path.Combine`, `Path.GetFullPath`, `Path.GetRelativePath`, + `Path.DirectorySeparatorChar`, `Path.AltDirectorySeparatorChar`. + +#### Callers + +- `Validation.TemporaryDirectory` (SelfTest subsystem) — calls `SafePathCombine` to + construct paths inside temporary directories, using a `Guid`-based relative name under + `Path.GetTempPath()`. diff --git a/docs/reqstream/ots/buildmark.yaml b/docs/reqstream/ots/buildmark.yaml index 66bf0a8..7b51221 100644 --- a/docs/reqstream/ots/buildmark.yaml +++ b/docs/reqstream/ots/buildmark.yaml @@ -9,7 +9,8 @@ sections: - title: BuildMark Requirements requirements: - id: VersionMark-OTS-BuildMark - title: BuildMark shall generate build-notes documentation from GitHub Actions metadata. + title: BuildMark shall generate build-notes documentation from GitHub + Actions metadata. justification: | DemaConsulting.BuildMark queries the GitHub API to capture workflow run details and renders them as a markdown build-notes document included in the release artifacts. diff --git a/docs/reqstream/ots/fileassert.yaml b/docs/reqstream/ots/fileassert.yaml index 57dcb1f..ed2d602 100644 --- a/docs/reqstream/ots/fileassert.yaml +++ b/docs/reqstream/ots/fileassert.yaml @@ -9,7 +9,8 @@ sections: - title: FileAssert Requirements requirements: - id: VersionMark-OTS-FileAssert - title: FileAssert shall validate generated documents against acceptance criteria. + title: FileAssert shall validate generated documents against acceptance + criteria. justification: | DemaConsulting.FileAssert validates HTML and PDF documents produced during the build, asserting that each document exists, has a non-trivial size, is structurally @@ -20,3 +21,17 @@ sections: tests: - FileAssert_VersionDisplay - FileAssert_HelpDisplay + - Pandoc_BuildNotesHtml + - WeasyPrint_BuildNotesPdf + - Pandoc_CodeQualityHtml + - WeasyPrint_CodeQualityPdf + - Pandoc_ReviewPlanHtml + - WeasyPrint_ReviewPlanPdf + - Pandoc_ReviewReportHtml + - WeasyPrint_ReviewReportPdf + - Pandoc_DesignHtml + - WeasyPrint_DesignPdf + - Pandoc_VerificationHtml + - WeasyPrint_VerificationPdf + - Pandoc_UserGuideHtml + - WeasyPrint_UserGuidePdf diff --git a/docs/reqstream/ots/pandoc.yaml b/docs/reqstream/ots/pandoc.yaml index 01cec2b..5bd4a5b 100644 --- a/docs/reqstream/ots/pandoc.yaml +++ b/docs/reqstream/ots/pandoc.yaml @@ -23,4 +23,5 @@ sections: - Pandoc_ReviewPlanHtml - Pandoc_ReviewReportHtml - Pandoc_DesignHtml + - Pandoc_VerificationHtml - Pandoc_UserGuideHtml diff --git a/docs/reqstream/ots/reqstream.yaml b/docs/reqstream/ots/reqstream.yaml index 3107e49..f618139 100644 --- a/docs/reqstream/ots/reqstream.yaml +++ b/docs/reqstream/ots/reqstream.yaml @@ -9,7 +9,8 @@ sections: - title: ReqStream Requirements requirements: - id: VersionMark-OTS-ReqStream - title: ReqStream shall enforce that every requirement is linked to passing test evidence. + title: ReqStream shall enforce that every requirement is linked to passing + test evidence. justification: | DemaConsulting.ReqStream processes requirements.yaml and the TRX test-result files to produce a requirements report, justifications document, and traceability matrix. When diff --git a/docs/reqstream/ots/reviewmark.yaml b/docs/reqstream/ots/reviewmark.yaml index 001a64d..01be293 100644 --- a/docs/reqstream/ots/reviewmark.yaml +++ b/docs/reqstream/ots/reviewmark.yaml @@ -8,14 +8,23 @@ sections: sections: - title: ReviewMark Requirements requirements: - - id: VersionMark-OTS-ReviewMark - title: ReviewMark shall generate a review plan and review report from the review configuration. + - id: VersionMark-OTS-ReviewMark-PlanGeneration + title: ReviewMark shall generate a review plan from the review configuration. justification: | DemaConsulting.ReviewMark reads the .reviewmark.yaml configuration and the review - evidence store to produce a review plan and review report documenting file review - coverage and currency. It runs in the same CI pipeline that produces the TRX test - results, so a successful pipeline run is evidence that ReviewMark executed without error. + evidence store to produce a review plan documenting file review coverage and + currency. It runs in the same CI pipeline that produces the TRX test results, so + a successful pipeline run is evidence that ReviewMark executed without error. tags: [ots] tests: - ReviewMark_ReviewPlanGeneration + - id: VersionMark-OTS-ReviewMark-ReportGeneration + title: ReviewMark shall generate a review report from the review configuration. + justification: | + DemaConsulting.ReviewMark reads the .reviewmark.yaml configuration and the review + evidence store to produce a review report documenting file review coverage and + currency. It runs in the same CI pipeline that produces the TRX test results, so + a successful pipeline run is evidence that ReviewMark executed without error. + tags: [ots] + tests: - ReviewMark_ReviewReportGeneration diff --git a/docs/reqstream/ots/weasyprint.yaml b/docs/reqstream/ots/weasyprint.yaml index 84093eb..5e3149b 100644 --- a/docs/reqstream/ots/weasyprint.yaml +++ b/docs/reqstream/ots/weasyprint.yaml @@ -23,4 +23,5 @@ sections: - WeasyPrint_ReviewPlanPdf - WeasyPrint_ReviewReportPdf - WeasyPrint_DesignPdf + - WeasyPrint_VerificationPdf - WeasyPrint_UserGuidePdf diff --git a/docs/reqstream/version-mark.yaml b/docs/reqstream/version-mark.yaml index bd217df..5ac9fd5 100644 --- a/docs/reqstream/version-mark.yaml +++ b/docs/reqstream/version-mark.yaml @@ -1,6 +1,6 @@ --- sections: - - title: VersionMark System Requirements + - title: VersionMark Requirements requirements: - id: VersionMark-System-Capture title: The tool shall capture tool version information from CI/CD environments. @@ -11,6 +11,7 @@ sections: children: - VersionMark-Capture-Capture - VersionMark-Capture-JobId + - VersionMark-Capture-MissingJobId - VersionMark-Capture-Output - VersionMark-Capture-DefaultOutput - VersionMark-Capture-ToolFilter @@ -21,11 +22,13 @@ sections: - VersionMark-Capture-Display - VersionMark-Capture-ConfigError - VersionMark-Capture-CommandFailure + - VersionMark-VersionMarkConfig-FindVersions tests: - IntegrationTest_CaptureCommand_CapturesToolVersions - id: VersionMark-System-Publish - title: The tool shall publish collected version information as a markdown report. + title: The tool shall publish collected version information as a markdown + report. justification: | Teams need a human-readable summary of tool versions across all jobs. Publishing to markdown enables the report to be included in release @@ -44,7 +47,8 @@ sections: - VersionMark_PublishCommand_GeneratesMarkdownReport - id: VersionMark-System-Lint - title: The tool shall validate configuration files and report issues before capture or publish is run. + title: The tool shall validate configuration files and report issues before + capture or publish is run. justification: | Detecting configuration errors early, before a CI/CD run proceeds, reduces wasted build time and provides precise error locations so users can fix @@ -52,6 +56,7 @@ sections: children: - VersionMark-Load-FileExistence - VersionMark-Load-YamlParsing + - VersionMark-Load-RootMapping - VersionMark-Load-ToolsSection - VersionMark-Load-ToolCommand - VersionMark-Load-ToolRegex @@ -59,8 +64,14 @@ sections: - VersionMark-Load-RegexVersion - VersionMark-Load-OsOverrides - VersionMark-Load-UnknownKeys - - VersionMark-Load-ErrorLocation + - VersionMark-Load-IssueLocation - VersionMark-Load-AllIssues + - VersionMark-Load-Return + - VersionMark-Load-OsRegexValid + - VersionMark-Load-OsRegexVersion + - VersionMark-Load-VersionMarkLoadResult-Bundle + - VersionMark-Load-VersionMarkLoadResult-ErrorRouting + - VersionMark-Load-VersionMarkLoadResult-WarnRouting tests: - IntegrationTest_LintFlag_ValidConfig_ReturnsSuccess - IntegrationTest_LintFlag_InvalidConfig_ReturnsError @@ -74,7 +85,8 @@ sections: children: - VersionMark-Validate-Capture - VersionMark-Validate-Publish - - VersionMark-Validate-Lint + - VersionMark-Validate-LintValid + - VersionMark-Validate-LintInvalid - VersionMark-Validate-Results tests: - IntegrationTest_ValidateFlag_RunsValidation diff --git a/docs/reqstream/version-mark/capture.yaml b/docs/reqstream/version-mark/capture.yaml index 4f1906a..64f6e88 100644 --- a/docs/reqstream/version-mark/capture.yaml +++ b/docs/reqstream/version-mark/capture.yaml @@ -2,7 +2,7 @@ sections: - title: VersionMark Requirements sections: - - title: Capture Command + - title: Capture Requirements requirements: - id: VersionMark-Capture-Capture title: The tool shall support --capture flag to enable capture mode. @@ -15,7 +15,8 @@ sections: - Capture_Context_CaptureFlag_SetsCaptureMode - id: VersionMark-Capture-JobId - title: The tool shall require --job-id parameter when capture mode is enabled. + title: The tool shall require --job-id parameter when capture mode is + enabled. justification: | The job ID uniquely identifies the capture session and is used in the default output filename, ensuring traceability of captured versions. @@ -24,18 +25,33 @@ sections: tests: - Capture_Context_WithJobId_SetsJobId + - id: VersionMark-Capture-MissingJobId + title: >- + The tool shall report an error and return a non-zero exit code when + --capture is used without --job-id. + justification: | + The job ID is required to name the output file and identify the capture session. + A missing job ID must be caught immediately with a clear error rather than + producing an output file with an empty or ambiguous name. + tags: + - capture + tests: + - Capture_Run_MissingJobId_ReportsErrorAndNonZeroExitCode + - id: VersionMark-Capture-Output - title: The tool shall support --output parameter to specify the JSON output file path. + title: The tool shall support --output parameter to specify the JSON output + file path. justification: | Allows users to control where captured version information is saved, enabling integration with existing workflows and file structures. tags: - capture tests: - - Capture_SaveAndLoad_PreservesAllVersionData + - Capture_Run_NoToolFilter_CapturesAllConfiguredTools - id: VersionMark-Capture-DefaultOutput - title: The tool shall default output filename to versionmark-.json when --output is not specified. + title: The tool shall default output filename to versionmark-.json + when --output is not specified. justification: | Provides a predictable default naming convention that includes the job ID, making it easy to identify and organize capture files. @@ -45,7 +61,8 @@ sections: - Capture_Run_NoOutputFlagSpecified_UsesDefaultFilename - id: VersionMark-Capture-ToolFilter - title: The tool shall accept an optional list of tool names after -- separator in capture mode. + title: The tool shall accept an optional list of tool names after -- separator + in capture mode. justification: | Allows users to capture versions for specific tools rather than all configured tools, improving performance and reducing unnecessary captures. @@ -55,7 +72,8 @@ sections: - Capture_Context_WithToolFilter_SetsToolNames - id: VersionMark-Capture-MultipleTools - title: The tool shall capture all tools defined in configuration when no tool names are specified. + title: The tool shall capture all tools defined in configuration when + no tool names are specified. justification: | Provides a convenient default behavior where all configured tools are captured, suitable for comprehensive version tracking scenarios. @@ -65,7 +83,8 @@ sections: - Capture_Run_NoToolFilter_CapturesAllConfiguredTools - id: VersionMark-Capture-Config - title: The tool shall read .versionmark.yaml from the current directory in capture mode. + title: The tool shall read .versionmark.yaml from the current directory + in capture mode. justification: | Uses the standard configuration file location to determine which tools to capture and how to capture them. @@ -75,7 +94,8 @@ sections: - Capture_Config_ReadFromFile_LoadsToolDefinitions - id: VersionMark-Capture-Command - title: The tool shall execute configured commands and extract version information using regex patterns. + title: The tool shall execute configured commands and extract version + information using regex patterns. justification: | Implements the core capture functionality by running tool commands and parsing their output to extract version numbers. @@ -85,18 +105,22 @@ sections: - Capture_FindVersions_ExecutesCommandAndExtractsVersion - id: VersionMark-Capture-JsonOutput - title: The tool shall save captured version information to a JSON file with job ID and version mappings. + title: The tool shall save captured version information to a JSON file + with job ID and version mappings. justification: | Stores captured versions in a structured format that can be easily consumed by other tools and systems. tags: - capture + children: + - VersionMark-VersionInfo-Save tests: - Capture_SaveAndLoad_PreservesAllVersionData - Capture_MultipleCaptures_EachFileHasDistinctJobId - id: VersionMark-Capture-Display - title: The tool shall display captured tool names and versions to the user. + title: The tool shall display captured tool names and versions to the + user. justification: | Provides immediate feedback to users about what versions were captured, enabling verification of the capture operation. @@ -106,12 +130,15 @@ sections: - Capture_Run_DisplaysCapturedVersionsAfterCapture - id: VersionMark-Capture-ConfigError - title: The tool shall report errors when .versionmark.yaml cannot be found or read in capture mode. + title: The tool shall report errors when .versionmark.yaml cannot be found + or read in capture mode. justification: | Provides clear error messages to users when the configuration file is missing or inaccessible, preventing silent failures. tags: - capture + children: + - VersionMark-Load-FileExistence tests: - Capture_Run_MissingConfig_ReportsError @@ -128,3 +155,5 @@ sections: tests: - VersionMarkConfig_FindVersions_InvalidCommand_ThrowsInvalidOperationException - VersionMarkConfig_FindVersions_RegexNoMatch_ThrowsInvalidOperationException + - Capture_Run_InvalidCommand_ReportsErrorAndNonZeroExitCode + - Capture_Run_RegexNoMatch_ReportsErrorAndNonZeroExitCode diff --git a/docs/reqstream/version-mark/capture/version-info.yaml b/docs/reqstream/version-mark/capture/version-info.yaml index f4ff9fe..67984df 100644 --- a/docs/reqstream/version-mark/capture/version-info.yaml +++ b/docs/reqstream/version-mark/capture/version-info.yaml @@ -1,39 +1,54 @@ --- sections: - - title: VersionInfo Unit Requirements - requirements: - - id: VersionMark-VersionInfo-Save - title: The VersionInfo class shall save captured version data to a JSON file. - justification: | - Captured version data must be persisted between the capture job and the - publish job. Writing to JSON provides a portable, human-readable format - that other tools can consume if needed. - tests: - - VersionInfo_SaveToFile_CreatesJsonFile - - VersionInfo_SaveAndLoad_RoundTripPreservesData - - VersionInfo_EmptyVersions_SavesAndLoadsCorrectly - - VersionInfo_SpecialCharacters_SavesAndLoadsCorrectly + - title: VersionMark Requirements + sections: + - title: Capture Requirements + sections: + - title: VersionInfo Requirements + requirements: + - id: VersionMark-VersionInfo-Save + title: The VersionInfo class shall save captured version data to a + JSON file. + justification: | + Captured version data must be persisted between the capture job and the + publish job. Writing to JSON provides a portable, human-readable format + that other tools can consume if needed. + tests: + - VersionInfo_SaveToFile_CreatesJsonFile + - VersionInfo_SaveAndLoad_RoundTripPreservesData + - VersionInfo_EmptyVersions_SavesAndLoadsCorrectly + - VersionInfo_SpecialCharacters_SavesAndLoadsCorrectly - - id: VersionMark-VersionInfo-Load - title: The VersionInfo class shall load version data from a JSON file. - justification: | - The publish mode must read the JSON files written by capture jobs. - Loading from JSON allows version data captured in any previous job - to be reconstructed and included in the report. - tests: - - VersionInfo_LoadFromFile_ReadsJsonFile - - VersionInfo_SaveAndLoad_RoundTripPreservesData - - VersionInfo_EmptyVersions_SavesAndLoadsCorrectly - - VersionInfo_SpecialCharacters_SavesAndLoadsCorrectly + - id: VersionMark-VersionInfo-Load + title: The VersionInfo class shall load version data from a JSON file. + justification: | + The publish mode must read the JSON files written by capture jobs. + Loading from JSON allows version data captured in any previous job + to be reconstructed and included in the report. + tests: + - VersionInfo_LoadFromFile_ReadsJsonFile + - VersionInfo_SaveAndLoad_RoundTripPreservesData + - VersionInfo_EmptyVersions_SavesAndLoadsCorrectly + - VersionInfo_SpecialCharacters_SavesAndLoadsCorrectly - - id: VersionMark-VersionInfo-Error - title: The VersionInfo class shall report errors when JSON files cannot be read or parsed. - justification: | - Providing clear errors when a file is missing or malformed prevents silent - data loss and guides users to the source of the problem quickly. - tests: - - VersionInfo_LoadFromFile_NonExistentFile_ThrowsArgumentException - - VersionInfo_LoadFromFile_InvalidJson_ThrowsArgumentException - - VersionInfo_LoadFromFile_EmptyJson_ThrowsArgumentException - - VersionInfo_LoadFromFile_NullJson_ThrowsArgumentException - - VersionInfo_SaveToFile_InvalidPath_ThrowsInvalidOperationException + - id: VersionMark-VersionInfo-Error + title: The VersionInfo class shall report errors when JSON files cannot + be read or parsed. + justification: | + Providing clear errors when a file is missing or malformed prevents silent + data loss and guides users to the source of the problem quickly. + tests: + - VersionInfo_LoadFromFile_NonExistentFile_ThrowsArgumentException + - VersionInfo_LoadFromFile_InvalidJson_ThrowsArgumentException + - VersionInfo_LoadFromFile_EmptyJson_ThrowsArgumentException + - VersionInfo_LoadFromFile_NullJson_ThrowsArgumentException + + - id: VersionMark-VersionInfo-WriteError + title: The VersionInfo class shall signal an error with context when + a JSON file cannot be written. + justification: | + Write failures (e.g., invalid path, permissions) must be surfaced immediately + with a clear, context-bearing exception so that capture pipelines can report + the failure rather than silently producing an incomplete artifact. + tests: + - VersionInfo_SaveToFile_InvalidPath_ThrowsInvalidOperationException diff --git a/docs/reqstream/version-mark/cli.yaml b/docs/reqstream/version-mark/cli.yaml index 3756805..2aca97a 100644 --- a/docs/reqstream/version-mark/cli.yaml +++ b/docs/reqstream/version-mark/cli.yaml @@ -2,10 +2,11 @@ sections: - title: VersionMark Requirements sections: - - title: Command-Line Interface + - title: Command-Line Interface Requirements requirements: - id: VersionMark-CommandLine-Context - title: The tool shall parse and validate command-line arguments consistently across all invocations. + title: The tool shall parse and validate command-line arguments consistently + across all invocations. justification: | Provides a standardized approach to command-line argument parsing and output handling across all DEMA Consulting DotNet Tools. @@ -16,7 +17,8 @@ sections: - Cli_Run_SilentWithVersionFlag_SuppressesOutput - id: VersionMark-CommandLine-Version - title: The tool shall support -v and --version flags to display version information. + title: The tool shall support -v and --version flags to display version + information. justification: | Users need to quickly identify the version of the tool they are using for troubleshooting and compatibility verification. @@ -26,7 +28,8 @@ sections: - Cli_Run_VersionFlag_ExitsCleanly - id: VersionMark-CommandLine-Help - title: The tool shall support -?, -h, and --help flags to display usage information. + title: The tool shall support -?, -h, and --help flags to display usage + information. justification: | Users need access to command-line usage documentation without requiring external resources. @@ -56,7 +59,8 @@ sections: - Cli_Run_ValidateFlag_RunsValidation - id: VersionMark-CommandLine-Results - title: The tool shall support --results flag to write validation results in TRX or JUnit format. + title: The tool shall support --results flag to write validation results + in TRX or JUnit format. justification: | Enables integration with CI/CD systems that expect standard test result formats. tags: @@ -84,7 +88,8 @@ sections: - Cli_Run_InvalidArgs_ThrowsArgumentException - id: VersionMark-CommandLine-InvalidArgs - title: The tool shall reject unknown or malformed command-line arguments with a descriptive error. + title: The tool shall reject unknown or malformed command-line arguments + with a descriptive error. justification: | Clear rejection of invalid arguments prevents silent misconfiguration and guides users toward correct usage without requiring external documentation. @@ -104,7 +109,8 @@ sections: - Cli_Run_InvalidArgs_ThrowsArgumentException - id: VersionMark-CommandLine-Lint - title: The tool shall support --lint to check the configuration file for issues. + title: The tool shall support --lint to check the configuration file for + issues. justification: | Lint support lets users identify and fix issues in .versionmark.yaml files during development, before running capture or publish operations. All issues diff --git a/docs/reqstream/version-mark/cli/context.yaml b/docs/reqstream/version-mark/cli/context.yaml index fea651b..c41eee9 100644 --- a/docs/reqstream/version-mark/cli/context.yaml +++ b/docs/reqstream/version-mark/cli/context.yaml @@ -1,79 +1,101 @@ --- sections: - - title: Context Unit Requirements - requirements: - - id: VersionMark-Context-Create - title: >- - The Context class shall parse command-line arguments into a Context instance - with accessible properties for every supported flag. - justification: | - All argument parsing is centralized in the Context class, ensuring - consistent parsing behavior across all invocations and making the parsed - state available throughout the program. - tests: - - Context_Create_NoArguments_ReturnsDefaultContext - - Context_Create_VersionFlag_SetsVersionTrue - - Context_Create_ShortVersionFlag_SetsVersionTrue - - Context_Create_HelpFlag_SetsHelpTrue - - Context_Create_ShortHelpFlag_H_SetsHelpTrue - - Context_Create_ShortHelpFlag_Question_SetsHelpTrue - - Context_Create_SilentFlag_SetsSilentTrue - - Context_Create_ValidateFlag_SetsValidateTrue - - Context_Create_ResultsFlag_SetsResultsFile - - Context_Create_LogFlag_OpensLogFile - - Context_Create_UnknownArgument_ThrowsArgumentException - - Context_Create_LogFlag_WithoutValue_ThrowsArgumentException - - Context_Create_ResultsFlag_WithoutValue_ThrowsArgumentException - - Context_Create_PublishFlag_SetsPublishTrue - - Context_Create_ReportParameter_SetsReportFile - - Context_Create_ReportDepthParameter_SetsReportDepth - - Context_Create_NoReportDepth_DefaultsToDepthOne - - Context_Create_GlobPatternsAfterSeparator_CapturesPatterns - - Context_Create_PublishWithoutReport_ParsesSuccessfully - - Context_Create_NoGlobPatterns_EmptyArray - - Context_Create_LintFlag_SetsLintTrue - - Context_Create_LintFlag_WithFile_SetsLintFile - - Context_Create_LintFlag_FollowedByFlag_DoesNotConsumeFlagAsFile - - Context_Create_DepthParameter_SetsDepth - - Context_Create_NoDepth_DefaultsToOne - - Context_Create_DepthParameter_SetsDefaultReportDepth - - Context_Create_ExplicitReportDepthOverridesDepth - - Context_Create_DepthZero_ThrowsArgumentException - - Context_Create_DepthNegative_ThrowsArgumentException - - Context_Create_DepthSeven_ThrowsArgumentException - - Context_Create_ReportDepthZero_ThrowsArgumentException - - Context_Create_ReportDepthNegative_ThrowsArgumentException - - Context_Create_ReportDepthSeven_ThrowsArgumentException - - Context_Create_CaptureFlag_SetsCaptureTrue - - Context_Create_JobIdFlag_SetsJobId - - Context_Create_OutputFlag_SetsOutputFile + - title: VersionMark Requirements + sections: + - title: Command-Line Interface Requirements + sections: + - title: Context Requirements + requirements: + - id: VersionMark-Context-Create + title: >- + The Context class shall parse command-line arguments into a Context + instance + with accessible properties for every supported flag. + justification: | + All argument parsing is centralized in the Context class, ensuring + consistent parsing behavior across all invocations and making the parsed + state available throughout the program. + tests: + - Context_Create_NoArguments_ReturnsDefaultContext + - Context_Create_VersionFlag_SetsVersionTrue + - Context_Create_ShortVersionFlag_SetsVersionTrue + - Context_Create_HelpFlag_SetsHelpTrue + - Context_Create_ShortHelpFlag_H_SetsHelpTrue + - Context_Create_ShortHelpFlag_Question_SetsHelpTrue + - Context_Create_SilentFlag_SetsSilentTrue + - Context_Create_ValidateFlag_SetsValidateTrue + - Context_Create_ResultsFlag_SetsResultsFile + - Context_Create_LogFlag_OpensLogFile + - Context_Create_UnknownArgument_ThrowsArgumentException + - Context_Create_LogFlag_WithoutValue_ThrowsArgumentException + - Context_Create_ResultsFlag_WithoutValue_ThrowsArgumentException + - Context_Create_PublishFlag_SetsPublishTrue + - Context_Create_ReportParameter_SetsReportFile + - Context_Create_ReportDepthParameter_SetsReportDepth + - Context_Create_NoReportDepth_DefaultsToDepthOne + - Context_Create_GlobPatternsAfterSeparator_CapturesPatterns + - Context_Create_PublishWithoutReport_ParsesSuccessfully + - Context_Create_NoGlobPatterns_EmptyArray + - Context_Create_LintFlag_SetsLintTrue + - Context_Create_LintFlag_WithFile_SetsLintFile + - Context_Create_LintFlag_FollowedByFlag_DoesNotConsumeFlagAsFile + - Context_Create_DepthParameter_SetsDepth + - Context_Create_NoDepth_DefaultsToOne + - Context_Create_DepthParameter_SetsDefaultReportDepth + - Context_Create_ExplicitReportDepthOverridesDepth + - Context_Create_DepthZero_ThrowsArgumentException + - Context_Create_DepthNegative_ThrowsArgumentException + - Context_Create_DepthSeven_ThrowsArgumentException + - Context_Create_ReportDepthZero_ThrowsArgumentException + - Context_Create_ReportDepthNegative_ThrowsArgumentException + - Context_Create_ReportDepthSeven_ThrowsArgumentException + - Context_Create_CaptureFlag_SetsCaptureTrue + - Context_Create_JobIdFlag_SetsJobId + - Context_Create_OutputFlag_SetsOutputFile + - Context_Create_DepthFlag_WithoutValue_ThrowsArgumentException + - Context_Create_SeparatorOutsideMode_ThrowsArgumentException - - id: VersionMark-Context-WriteLine - title: The Context.WriteLine method shall write output respecting the silent flag. - justification: | - Silent mode suppresses console output so automated scripts can run the tool - without cluttering their log. - tests: - - Context_WriteLine_NotSilent_WritesToConsole - - Context_WriteLine_Silent_DoesNotWriteToConsole + - id: VersionMark-Context-WriteLine + title: The Context.WriteLine method shall write output respecting + the silent flag. + justification: | + Silent mode suppresses console output so automated scripts can run the tool + without cluttering their log. + tests: + - Context_WriteLine_NotSilent_WritesToConsole + - Context_WriteLine_Silent_DoesNotWriteToConsole - - id: VersionMark-Context-WriteError - title: >- - The Context.WriteError method shall write error messages to stderr, - unless silent mode is active, and shall write error messages to the log file - when one is open. - justification: | - Silent mode suppresses all console output, including error messages written to - stderr. The log file always receives error output regardless of silent mode. - tests: - - Context_WriteError_NotSilent_WritesToConsole - - Context_WriteError_Silent_DoesNotWriteToConsole - - Context_WriteError_WritesToLogFile + - id: VersionMark-Context-WriteError + title: >- + The Context.WriteError method shall write error messages to stderr + unless silent mode is active. + justification: | + Silent mode suppresses all output (both stdout and stderr) so that callers + can run the tool without any console output and detect failures solely through + the process exit code. This supports self-validation scenarios where the tool + deliberately triggers errors to verify error handling without polluting the + output stream. + tests: + - Context_WriteError_NotSilent_WritesToConsole + - Context_WriteError_Silent_SuppressesStderr - - id: VersionMark-Context-WriteErrorExitCode - title: The Context.WriteError method shall set the process exit code to a non-zero value. - justification: | - The process must exit with a non-zero code so that calling scripts can detect - failure automatically even when output is silenced. - tests: - - Context_WriteError_SetsErrorExitCode + - id: VersionMark-Context-WriteErrorLog + title: >- + The Context.WriteError method shall write error messages to the + log + file when one is open. + justification: | + The log file always receives error output regardless of silent mode, + ensuring a complete record of all errors is available for debugging and + audit even when console output is suppressed. + tests: + - Context_WriteError_WritesToLogFile + + - id: VersionMark-Context-WriteErrorExitCode + title: The Context.WriteError method shall set the process exit code + to a non-zero value. + justification: | + The process must exit with a non-zero code so that calling scripts can detect + failure automatically even when output is silenced. + tests: + - Context_WriteError_SetsErrorExitCode diff --git a/docs/reqstream/version-mark/cli/program.yaml b/docs/reqstream/version-mark/cli/program.yaml index 66f77ca..6e79e48 100644 --- a/docs/reqstream/version-mark/cli/program.yaml +++ b/docs/reqstream/version-mark/cli/program.yaml @@ -1,58 +1,63 @@ --- sections: - - title: Program Unit Requirements - requirements: - - id: VersionMark-Program-Version - title: The Program class shall expose the tool version. - justification: | - This ensures the displayed version matches the published package version. - tests: - - Program_Version_ReturnsNonEmptyString + - title: VersionMark Requirements + sections: + - title: Command-Line Interface Requirements + sections: + - title: Program Requirements + requirements: + - id: VersionMark-Program-Version + title: The Program class shall expose the tool version. + justification: | + This ensures the displayed version matches the published package version. + tests: + - Program_Version_ReturnsNonEmptyString - - id: VersionMark-Program-Dispatch - title: >- - The Program class shall execute the appropriate operation based on the - provided command-line arguments, in priority order. - justification: | - Consistent flag priority ordering ensures predictable behavior - across all invocations, whether from Main or from self-validation tests. - tests: - - Program_Run_WithVersionFlag_DisplaysVersionOnly - - Program_Run_WithHelpFlag_DisplaysUsageInformation - - Program_Run_WithValidateFlag_RunsValidation - - Program_Run_NoArguments_DisplaysDefaultBehavior + - id: VersionMark-Program-Dispatch + title: >- + The Program class shall execute the appropriate operation based + on the + provided command-line arguments, in priority order. + justification: | + Consistent flag priority ordering ensures predictable behavior + across all invocations, whether from Main or from self-validation tests. + tests: + - Program_Run_WithVersionFlag_DisplaysVersionOnly + - Program_Run_WithHelpFlag_DisplaysUsageInformation + - Program_Run_WithValidateFlag_RunsValidation + - Program_Run_NoArguments_DisplaysDefaultBehavior - - id: VersionMark-Program-RunCapture - title: The Program class shall orchestrate the capture workflow. - justification: | - The capture workflow must validate required arguments and execute tool commands - to produce the JSON output used by the publish workflow. - tests: - - Program_Run_WithCaptureCommand_CapturesToolVersions - - Program_Run_WithCaptureCommandWithoutJobId_ReturnsError - - Program_Run_WithCaptureCommandWithMissingConfig_ReturnsError + - id: VersionMark-Program-RunCapture + title: The Program class shall orchestrate the capture workflow. + justification: | + The capture workflow must validate required arguments and execute tool commands + to produce the JSON output used by the publish workflow. + tests: + - Program_Run_WithCaptureCommand_CapturesToolVersions + - Program_Run_WithCaptureCommandWithoutJobId_ReturnsError + - Program_Run_WithCaptureCommandWithMissingConfig_ReturnsError - - id: VersionMark-Program-RunPublish - title: The Program class shall orchestrate the publish workflow. - justification: | - The publish workflow must validate required arguments, collect version data - from capture files, and produce the markdown report. - tests: - - Program_Run_WithPublishCommandWithoutReport_ReturnsError - - Program_Run_WithPublishCommandNoMatchingFiles_ReturnsError - - Program_Run_WithPublishCommandInvalidJson_ReturnsError - - Program_Run_WithPublishCommand_GeneratesMarkdownReport - - Program_Run_WithPublishCommandCustomDepth_AdjustsHeadingLevels + - id: VersionMark-Program-RunPublish + title: The Program class shall orchestrate the publish workflow. + justification: | + The publish workflow must validate required arguments, collect version data + from capture files, and produce the markdown report. + tests: + - Program_Run_WithPublishCommandWithoutReport_ReturnsError + - Program_Run_WithPublishCommandNoMatchingFiles_ReturnsError + - Program_Run_WithPublishCommandInvalidJson_ReturnsError + - Program_Run_WithPublishCommand_GeneratesMarkdownReport + - Program_Run_WithPublishCommandCustomDepth_AdjustsHeadingLevels - - id: VersionMark-Program-RunLint - title: The Program class shall orchestrate the lint workflow. - justification: | - Lint mode must report all discovered configuration issues and suppress the - application banner so that only issue lines appear in the output, enabling - easy script integration. - tests: - - Program_Run_WithLintFlag_ValidConfig_ReturnsSuccess - - Program_Run_WithLintFlag_ValidConfig_SuppressesBanner - - Program_Run_WithLintFlag_InvalidConfig_ReturnsError - - Program_Run_WithLintFlag_NoFile_UsesDefaultConfigFile - - Program_Run_WithHelpFlag_IncludesLintInformation + - id: VersionMark-Program-RunLint + title: The Program class shall orchestrate the lint workflow. + justification: | + Lint mode must report all discovered configuration issues and suppress the + application banner so that only issue lines appear in the output, enabling + easy script integration. + tests: + - Program_Run_WithLintFlag_ValidConfig_ReturnsSuccess + - Program_Run_WithLintFlag_ValidConfig_SuppressesBanner + - Program_Run_WithLintFlag_InvalidConfig_ReturnsError + - Program_Run_WithLintFlag_NoFile_UsesDefaultConfigFile + - Program_Run_WithHelpFlag_IncludesLintInformation diff --git a/docs/reqstream/version-mark/configuration.yaml b/docs/reqstream/version-mark/configuration.yaml index f7ec43d..962c1bf 100644 --- a/docs/reqstream/version-mark/configuration.yaml +++ b/docs/reqstream/version-mark/configuration.yaml @@ -2,7 +2,7 @@ sections: - title: VersionMark Requirements sections: - - title: Configuration File + - title: Configuration Requirements requirements: - id: VersionMark-Configuration-YamlConfig title: The tool shall read configuration from a .versionmark.yaml file. @@ -11,64 +11,86 @@ sections: version capture commands. tags: - configuration + children: + - VersionMark-VersionMarkConfig-ReadFromFile + - VersionMark-VersionMarkConfig-Load tests: - Configuration_ReadFromFile_MultipleTools_AllToolsAccessible - id: VersionMark-Configuration-ToolDefinition - title: The tool shall support tool definitions with command and regex properties. + title: The tool shall support tool definitions with command and regex + properties. justification: | Enables users to specify how to execute and parse version information for each tool. tags: - configuration + children: + - VersionMark-ToolConfig-EffectiveCommand + - VersionMark-ToolConfig-EffectiveRegex tests: - Configuration_ReadFromFile_MultipleTools_AllToolsAccessible - id: VersionMark-Configuration-OsCommandOverride - title: The tool shall support OS-specific command overrides using -win, -linux, and -macos suffixes. + title: The tool shall support OS-specific command overrides using -win, + -linux, and -macos suffixes. justification: | Different operating systems may require different commands or command variations to capture tool versions. tags: - configuration + children: + - VersionMark-ToolConfig-EffectiveCommand tests: - Configuration_ReadFromFile_WithOsOverrides_SelectsAppropriateCommand - id: VersionMark-Configuration-OsRegexOverride - title: The tool shall support OS-specific regex overrides using -win, -linux, and -macos suffixes. + title: The tool shall support OS-specific regex overrides using -win, + -linux, and -macos suffixes. justification: | Different operating systems may produce different output formats requiring different regular expressions for version extraction. tags: - configuration + children: + - VersionMark-ToolConfig-EffectiveRegex tests: - Configuration_ReadFromFile_OsRegexOverride_SelectsAppropriateRegex - id: VersionMark-Configuration-ValidateTools - title: The tool shall validate that configuration files contain at least one tool definition. + title: The tool shall validate that configuration files contain at least + one tool definition. justification: | Prevents misconfiguration by ensuring the configuration file has actionable content. tags: - configuration + children: + - VersionMark-Load-ToolsSection tests: - Configuration_ReadFromFile_EmptyTools_ThrowsArgumentException - id: VersionMark-Configuration-ReadError - title: The tool shall report errors when configuration files cannot be read. + title: The tool shall report errors when configuration files cannot be + read. justification: | Provides clear feedback to users when configuration files are missing or inaccessible. tags: - configuration + children: + - VersionMark-Load-FileExistence tests: - Configuration_ReadFromFile_MissingFile_ThrowsArgumentException - id: VersionMark-Configuration-ParseError - title: The tool shall report errors when configuration files contain invalid YAML. + title: The tool shall report errors when configuration files contain invalid + YAML. justification: | Provides clear feedback to users when configuration files are malformed. tags: - configuration + children: + - VersionMark-Load-YamlParsing tests: - Configuration_ReadFromFile_InvalidYaml_ThrowsArgumentException diff --git a/docs/reqstream/version-mark/configuration/lint-issue.yaml b/docs/reqstream/version-mark/configuration/lint-issue.yaml new file mode 100644 index 0000000..6eefec0 --- /dev/null +++ b/docs/reqstream/version-mark/configuration/lint-issue.yaml @@ -0,0 +1,30 @@ +--- +sections: + - title: VersionMark Requirements + sections: + - title: Configuration Requirements + sections: + - title: LintIssue Requirements + requirements: + - id: VersionMark-Load-LintIssue-Fields + title: >- + The LintIssue record shall store file path, line, column, severity, + and + description. + justification: | + Capturing all diagnostic fields in a single record allows callers to access + each attribute independently and produce custom diagnostic output as needed. + tests: + - LintIssue_Constructor_AllFields_AreStoredCorrectly + + - id: VersionMark-Load-LintIssue-Format + title: >- + The LintIssue record shall format its fields as "{FilePath}({Line},{Column}): + {severity}: {Description}" with severity in lowercase. + justification: | + A consistent, editor-friendly format (identical to the MSBuild/GCC diagnostic + convention) lets CI systems and editors parse the output automatically and + navigate to the exact location of each issue. + tests: + - LintIssue_ToString_Error_ProducesLowercaseSeverity + - LintIssue_ToString_Warning_ProducesLowercaseSeverity diff --git a/docs/reqstream/version-mark/configuration/load.yaml b/docs/reqstream/version-mark/configuration/load.yaml index 94c76b2..f259367 100644 --- a/docs/reqstream/version-mark/configuration/load.yaml +++ b/docs/reqstream/version-mark/configuration/load.yaml @@ -1,186 +1,217 @@ --- sections: - - title: VersionMark Load Requirements - requirements: - - id: VersionMark-Load-FileExistence - title: The tool shall report an error when the configuration file does not exist. - justification: | - Users may specify an incorrect path; an immediate, clear error message - avoids silent failures and directs the user to fix the file path. - tags: - - configuration - tests: - - VersionMarkConfig_Load_MissingFile_ReturnsNullConfig - - - id: VersionMark-Load-YamlParsing - title: The tool shall report an error when the configuration file contains invalid YAML. - justification: | - Malformed YAML cannot be interpreted. Reporting a parse error with its - location lets users quickly find and fix the syntax issue. - tags: - - configuration - tests: - - VersionMarkConfig_Load_InvalidYaml_ReturnsNullConfig - - - id: VersionMark-Load-ToolsSection - title: The tool shall report an error when the configuration file is missing a non-empty 'tools' section. - justification: | - A configuration without any tool definitions cannot perform any useful - capture or publish operations. - tags: - - configuration - tests: - - VersionMarkConfig_Load_MissingToolsSection_ReturnsNullConfig - - VersionMarkConfig_Load_EmptyToolsSection_ReturnsNullConfig - - - id: VersionMark-Load-ToolCommand - title: The tool shall report an error when a tool is missing a non-empty 'command' field. - justification: | - The 'command' field defines how to invoke the tool to obtain version - information. Without it the tool cannot function at runtime. - tags: - - configuration - tests: - - VersionMarkConfig_Load_MissingCommand_ReturnsNullConfig - - VersionMarkConfig_Load_EmptyCommand_ReturnsNullConfig - - - id: VersionMark-Load-ToolRegex - title: The tool shall report an error when a tool is missing a non-empty 'regex' field. - justification: | - The 'regex' field is used to extract the version string from command output. - Without it the tool cannot parse a version at runtime. - tags: - - configuration - tests: - - VersionMarkConfig_Load_MissingRegex_ReturnsNullConfig - - VersionMarkConfig_Load_EmptyRegex_ReturnsNullConfig - - - id: VersionMark-Load-RegexValid - title: The tool shall report an error when a regex value cannot be compiled. - justification: | - An invalid regex pattern will cause a runtime failure. Detecting it early - during load gives the user a clear error with a precise file location. - tags: - - configuration - tests: - - VersionMarkConfig_Load_InvalidRegex_ReturnsNullConfig - - - id: VersionMark-Load-RegexVersion - title: The tool shall report an error when a regex does not contain a named 'version' capture group. - justification: | - The capture group named 'version' is the mechanism by which the tool - extracts the version string. Its absence causes silent data loss at runtime. - tags: - - configuration - tests: - - VersionMarkConfig_Load_RegexMissingVersionGroup_ReturnsNullConfig - - - id: VersionMark-Load-OsOverrides - title: The tool shall report an error when an OS-specific command or regex override is empty. - justification: | - An empty OS-specific override will be used at runtime on the corresponding - platform, causing a capture failure. Detecting it during load prevents - platform-specific surprises. - tags: - - configuration - tests: - - VersionMarkConfig_Load_OsSpecificEmptyCommand_ReturnsNullConfig - - VersionMarkConfig_Load_OsSpecificEmptyRegex_ReturnsNullConfig - - - id: VersionMark-Load-UnknownKeys - title: The tool shall report unknown keys as non-fatal warnings that do not fail loading. - justification: | - Unknown keys may be typos or forward-compatible extensions. Treating them - as warnings alerts users without blocking valid configurations from - proceeding. - tags: - - configuration - tests: - - VersionMarkConfig_Load_UnknownTopLevelKey_ReturnsConfig - - VersionMarkConfig_Load_UnknownToolKey_ReturnsConfig - - - id: VersionMark-Load-ErrorLocation - title: The tool shall report all load findings with the filename and line/column location. - justification: | - Precise locations (file, line, column) allow users to jump directly to the - problem in their editor, dramatically reducing the time needed to fix issues. - tags: - - configuration - tests: - - VersionMarkConfig_Load_IssuesContainFilePath - - - id: VersionMark-Load-AllIssues - title: The tool shall report all load issues discovered in one invocation without stopping at the first error. - justification: | - Reporting all issues at once lets users fix everything in one edit cycle - rather than repeatedly re-running to discover each subsequent problem. - tags: - - configuration - tests: - - VersionMarkConfig_Load_MultipleErrors_ReportsAll - - - id: VersionMark-Load-Method - title: >- - The VersionMarkConfig.Load method shall validate a .versionmark.yaml configuration file - and return issues with file, line, and column location alongside the loaded configuration. - justification: | - Centralizing all load and validation checks in a single Load method ensures a consistent - validation pass and allows all issues to be reported in one run rather than - stopping at the first error. Returning both the config and issues allows callers - to proceed when only warnings are present. - tests: - - VersionMarkConfig_Load_ValidConfig_ReturnsConfig - - VersionMarkConfig_Load_MissingFile_ReturnsNullConfig - - VersionMarkConfig_Load_InvalidYaml_ReturnsNullConfig - - VersionMarkConfig_Load_MissingToolsSection_ReturnsNullConfig - - VersionMarkConfig_Load_EmptyToolsSection_ReturnsNullConfig - - VersionMarkConfig_Load_MissingCommand_ReturnsNullConfig - - VersionMarkConfig_Load_EmptyCommand_ReturnsNullConfig - - VersionMarkConfig_Load_MissingRegex_ReturnsNullConfig - - VersionMarkConfig_Load_EmptyRegex_ReturnsNullConfig - - VersionMarkConfig_Load_InvalidRegex_ReturnsNullConfig - - VersionMarkConfig_Load_RegexMissingVersionGroup_ReturnsNullConfig - - VersionMarkConfig_Load_UnknownTopLevelKey_ReturnsConfig - - VersionMarkConfig_Load_UnknownToolKey_ReturnsConfig - - VersionMarkConfig_Load_OsSpecificEmptyCommand_ReturnsNullConfig - - VersionMarkConfig_Load_OsSpecificEmptyRegex_ReturnsNullConfig - - VersionMarkConfig_Load_OsSpecificRegexMissingVersionGroup_ReturnsNullConfig - - VersionMarkConfig_Load_OsSpecificInvalidRegex_ReturnsNullConfig - - VersionMarkConfig_Load_MultipleErrors_ReportsAll - - VersionMarkConfig_Load_IssuesContainFilePath - - - id: VersionMark-Load-LintIssue-Fields - title: >- - The LintIssue record shall store file path, line, column, severity, and description. - justification: | - Capturing all diagnostic fields in a single record allows callers to access - each attribute independently and produce custom diagnostic output as needed. - tests: - - LintIssue_Constructor_AllFields_AreStoredCorrectly - - - id: VersionMark-Load-LintIssue-Format - title: >- - The LintIssue record shall format its fields as "{FilePath}({Line},{Column}): {severity}: - {Description}" with severity in lowercase. - justification: | - A consistent, editor-friendly format (identical to the MSBuild/GCC diagnostic - convention) lets CI systems and editors parse the output automatically and - navigate to the exact location of each issue. - tests: - - LintIssue_ToString_Error_ProducesLowercaseSeverity - - LintIssue_ToString_Warning_ProducesLowercaseSeverity - - - id: VersionMark-Load-VersionMarkLoadResult - title: >- - The VersionMarkLoadResult record shall bundle the loaded configuration and all - validation issues, routing errors to the error stream and warnings to standard - output when ReportIssues is called. - justification: | - Bundling the config and issues in a single return value lets callers - distinguish between fatal errors (which prevent loading) and non-fatal - warnings (which allow loading to proceed). Routing errors to the error stream - ensures non-zero exit codes are set for fatal issues. - tests: - - VersionMarkLoadResult_Constructor_AllFields_AreStoredCorrectly - - VersionMarkLoadResult_ReportIssues_Error_WritesToErrorStream - - VersionMarkLoadResult_ReportIssues_Warning_WritesToStdOut + - title: VersionMark Requirements + sections: + - title: Configuration Requirements + sections: + - title: Load Requirements + requirements: + - id: VersionMark-Load-FileExistence + title: The tool shall report an error when the configuration file + does not exist. + justification: | + Users may specify an incorrect path; an immediate, clear error message + avoids silent failures and directs the user to fix the file path. + tags: + - configuration + tests: + - VersionMarkConfig_Load_MissingFile_ReturnsNullConfig + + - id: VersionMark-Load-YamlParsing + title: The tool shall report an error when the configuration file + contains invalid YAML. + justification: | + Malformed YAML cannot be interpreted. Reporting a parse error with its + location lets users quickly find and fix the syntax issue. + tags: + - configuration + tests: + - VersionMarkConfig_Load_InvalidYaml_ReturnsNullConfig + + - id: VersionMark-Load-RootMapping + title: The tool shall report an error when the root node of the configuration + file is not a YAML mapping. + justification: | + A valid configuration file must have a mapping at its root so that top-level + keys such as 'tools' can be located. A scalar, sequence, or other node type + at the root cannot contain tool definitions and must be rejected immediately. + tags: + - configuration + tests: + - VersionMarkConfig_Load_NonMappingRoot_ReturnsNullConfig + + - id: VersionMark-Load-ToolsSection + title: The tool shall report an error when the configuration file + is missing a non-empty 'tools' section. + justification: | + A configuration without any tool definitions cannot perform any useful + capture or publish operations. + tags: + - configuration + tests: + - VersionMarkConfig_Load_MissingToolsSection_ReturnsNullConfig + - VersionMarkConfig_Load_EmptyToolsSection_ReturnsNullConfig + + - id: VersionMark-Load-ToolCommand + title: The tool shall report an error when a tool is missing a non-empty + 'command' field. + justification: | + The 'command' field defines how to invoke the tool to obtain version + information. Without it the tool cannot function at runtime. + tags: + - configuration + tests: + - VersionMarkConfig_Load_MissingCommand_ReturnsNullConfig + - VersionMarkConfig_Load_EmptyCommand_ReturnsNullConfig + + - id: VersionMark-Load-ToolRegex + title: The tool shall report an error when a tool is missing a non-empty + 'regex' field. + justification: | + The 'regex' field is used to extract the version string from command output. + Without it the tool cannot parse a version at runtime. + tags: + - configuration + tests: + - VersionMarkConfig_Load_MissingRegex_ReturnsNullConfig + - VersionMarkConfig_Load_EmptyRegex_ReturnsNullConfig + + - id: VersionMark-Load-RegexValid + title: The tool shall report an error when a regex value cannot be + compiled. + justification: | + An invalid regex pattern will cause a runtime failure. Detecting it early + during load gives the user a clear error with a precise file location. + tags: + - configuration + tests: + - VersionMarkConfig_Load_InvalidRegex_ReturnsNullConfig + + - id: VersionMark-Load-RegexVersion + title: The tool shall report an error when a regex does not contain + a named 'version' capture group. + justification: | + The capture group named 'version' is the mechanism by which the tool + extracts the version string. Its absence causes silent data loss at runtime. + tags: + - configuration + tests: + - VersionMarkConfig_Load_RegexMissingVersionGroup_ReturnsNullConfig + + - id: VersionMark-Load-OsOverrides + title: The tool shall report an error when an OS-specific command + or regex override is empty. + justification: | + An empty OS-specific override will be used at runtime on the corresponding + platform, causing a capture failure. Detecting it during load prevents + platform-specific surprises. + tags: + - configuration + tests: + - VersionMarkConfig_Load_OsSpecificEmptyCommand_ReturnsNullConfig + - VersionMarkConfig_Load_OsSpecificEmptyRegex_ReturnsNullConfig + + - id: VersionMark-Load-UnknownKeys + title: The tool shall report unknown keys as non-fatal warnings that + do not fail loading. + justification: | + Unknown keys may be typos or forward-compatible extensions. Treating them + as warnings alerts users without blocking valid configurations from + proceeding. + tags: + - configuration + tests: + - VersionMarkConfig_Load_UnknownTopLevelKey_ReturnsConfig + - VersionMarkConfig_Load_UnknownToolKey_ReturnsConfig + + - id: VersionMark-Load-AllIssues + title: The tool shall report all load issues discovered in one invocation + without stopping at the first error. + justification: | + Reporting all issues at once lets users fix everything in one edit cycle + rather than repeatedly re-running to discover each subsequent problem. + tags: + - configuration + tests: + - VersionMarkConfig_Load_MultipleErrors_ReportsAll + + - id: VersionMark-Load-IssueLocation + title: >- + The VersionMarkConfig.Load method shall report all load findings + with + the filename and line/column location. + justification: | + Precise locations (file, line, column) allow users to jump directly to the + problem in their editor, dramatically reducing the time needed to fix issues. + tests: + - VersionMarkConfig_Load_IssuesContainFilePath + + - id: VersionMark-Load-Return + title: >- + The VersionMarkConfig.Load method shall return both the loaded + configuration and all validation issues in a single result value. + justification: | + Returning both the config and issues allows callers to proceed when only + warnings are present while rejecting configurations with fatal errors. + tests: + - VersionMarkConfig_Load_ValidConfig_ReturnsConfig + - VersionMarkConfig_Load_MissingFile_ReturnsNullConfig + + - id: VersionMark-Load-OsRegexValid + title: >- + The tool shall report an error when an OS-specific regex override + cannot be compiled. + justification: | + An invalid OS-specific regex pattern will cause a runtime failure on the + corresponding platform. Detecting it during load gives the user a clear + error with a precise file location before deployment. + tags: + - configuration + tests: + - VersionMarkConfig_Load_OsSpecificInvalidRegex_ReturnsNullConfig + + - id: VersionMark-Load-OsRegexVersion + title: >- + The tool shall report an error when an OS-specific regex override + does + not contain a named 'version' capture group. + justification: | + The capture group named 'version' is the mechanism by which the tool + extracts the version string. Its absence in an OS-specific override causes + silent data loss at runtime on the corresponding platform. + tags: + - configuration + tests: + - VersionMarkConfig_Load_OsSpecificRegexMissingVersionGroup_ReturnsNullConfig + + - id: VersionMark-Load-VersionMarkLoadResult-Bundle + title: >- + The VersionMarkLoadResult record shall bundle the loaded configuration + and + all validation issues in a single return value. + justification: | + Bundling the config and issues in a single return value lets callers + distinguish between fatal errors (which prevent loading) and non-fatal + warnings (which allow loading to proceed). + tests: + - VersionMarkLoadResult_Constructor_AllFields_AreStoredCorrectly + + - id: VersionMark-Load-VersionMarkLoadResult-ErrorRouting + title: >- + The VersionMarkLoadResult.ReportIssues method shall route error-severity + issues to the error stream. + justification: | + Routing errors to the error stream ensures non-zero exit codes are set for + fatal issues, allowing calling scripts to detect failure automatically. + tests: + - VersionMarkLoadResult_ReportIssues_Error_WritesToErrorStream + + - id: VersionMark-Load-VersionMarkLoadResult-WarnRouting + title: >- + The VersionMarkLoadResult.ReportIssues method shall route warning-severity + issues to standard output. + justification: | + Routing warnings to standard output rather than the error stream signals + non-fatal findings to callers without triggering a non-zero exit code. + tests: + - VersionMarkLoadResult_ReportIssues_Warning_WritesToStdOut diff --git a/docs/reqstream/version-mark/configuration/tool-config.yaml b/docs/reqstream/version-mark/configuration/tool-config.yaml index 206074d..a868292 100644 --- a/docs/reqstream/version-mark/configuration/tool-config.yaml +++ b/docs/reqstream/version-mark/configuration/tool-config.yaml @@ -1,35 +1,41 @@ --- sections: - - title: ToolConfig Unit Requirements - requirements: - - id: VersionMark-ToolConfig-EffectiveCommand - title: >- - The ToolConfig.GetEffectiveCommand method shall return the OS-specific - command override when present, or the default command otherwise. - justification: | - Different platforms may require different command syntax. GetEffectiveCommand - selects the correct override for the current OS and falls back to the default - when no override is defined. - tests: - - ToolConfig_GetEffectiveCommand_NoOverride_ReturnsDefaultCommand - - ToolConfig_GetEffectiveCommand_WithExplicitOs_ReturnsCorrectCommand - - ToolConfig_GetEffectiveCommand_WindowsOverride_ReturnsWindowsCommand - - ToolConfig_GetEffectiveCommand_LinuxOverride_ReturnsLinuxCommand - - ToolConfig_GetEffectiveCommand_MacOsOverride_ReturnsMacOsCommand - - ToolConfig_GetEffectiveCommand_NoDefaultKey_ThrowsInvalidOperationException + - title: VersionMark Requirements + sections: + - title: Configuration Requirements + sections: + - title: ToolConfig Requirements + requirements: + - id: VersionMark-ToolConfig-EffectiveCommand + title: >- + The ToolConfig.GetEffectiveCommand method shall return the OS-specific + command override when present, or the default command otherwise, + or shall signal an error when no applicable command is configured. + justification: | + Different platforms may require different command syntax. GetEffectiveCommand + selects the correct override for the current OS and falls back to the default + when no override is defined. + tests: + - ToolConfig_GetEffectiveCommand_NoOverride_ReturnsDefaultCommand + - ToolConfig_GetEffectiveCommand_WithExplicitOs_ReturnsCorrectCommand + - ToolConfig_GetEffectiveCommand_WindowsOverride_ReturnsWindowsCommand + - ToolConfig_GetEffectiveCommand_LinuxOverride_ReturnsLinuxCommand + - ToolConfig_GetEffectiveCommand_MacOsOverride_ReturnsMacOsCommand + - ToolConfig_GetEffectiveCommand_NoDefaultKey_ThrowsInvalidOperationException - - id: VersionMark-ToolConfig-EffectiveRegex - title: >- - The ToolConfig.GetEffectiveRegex method shall return the OS-specific regex - override when present, or the default regex otherwise. - justification: | - Different platforms may produce different output formats requiring different - regex patterns. GetEffectiveRegex selects the correct override for the current - OS and falls back to the default when no override is defined. - tests: - - ToolConfig_GetEffectiveRegex_NoOverride_ReturnsDefaultRegex - - ToolConfig_GetEffectiveRegex_WithExplicitOs_ReturnsCorrectRegex - - ToolConfig_GetEffectiveRegex_WindowsOverride_ReturnsWindowsRegex - - ToolConfig_GetEffectiveRegex_LinuxOverride_ReturnsLinuxRegex - - ToolConfig_GetEffectiveRegex_MacOsOverride_ReturnsMacOsRegex - - ToolConfig_GetEffectiveRegex_NoDefaultKey_ThrowsInvalidOperationException + - id: VersionMark-ToolConfig-EffectiveRegex + title: >- + The ToolConfig.GetEffectiveRegex method shall return the OS-specific + regex override when present, or the default regex otherwise, or + shall signal an error when no applicable regular expression is configured. + justification: | + Different platforms may produce different output formats requiring different + regex patterns. GetEffectiveRegex selects the correct override for the current + OS and falls back to the default when no override is defined. + tests: + - ToolConfig_GetEffectiveRegex_NoOverride_ReturnsDefaultRegex + - ToolConfig_GetEffectiveRegex_WithExplicitOs_ReturnsCorrectRegex + - ToolConfig_GetEffectiveRegex_WindowsOverride_ReturnsWindowsRegex + - ToolConfig_GetEffectiveRegex_LinuxOverride_ReturnsLinuxRegex + - ToolConfig_GetEffectiveRegex_MacOsOverride_ReturnsMacOsRegex + - ToolConfig_GetEffectiveRegex_NoDefaultKey_ThrowsInvalidOperationException diff --git a/docs/reqstream/version-mark/configuration/version-mark-config.yaml b/docs/reqstream/version-mark/configuration/version-mark-config.yaml index 98e222d..4dabb59 100644 --- a/docs/reqstream/version-mark/configuration/version-mark-config.yaml +++ b/docs/reqstream/version-mark/configuration/version-mark-config.yaml @@ -1,61 +1,66 @@ --- sections: - - title: VersionMarkConfig Unit Requirements - requirements: - - id: VersionMark-VersionMarkConfig-ReadFromFile - title: >- - The VersionMarkConfig.ReadFromFile method shall load and parse a - .versionmark.yaml file, validating structure and content. - justification: | - ReadFromFile is the single entry point for loading configuration. It validates - file existence, YAML structure, presence of at least one tool definition, - required command and regex field presence and non-emptiness, OS-specific - override validity, regex compilability, and the presence of a named 'version' - capture group, providing clear errors when any check fails. - tests: - - VersionMarkConfig_ReadFromFile_ValidFile_ReturnsConfig - - VersionMarkConfig_ReadFromFile_WithAllOsOverrides_ReturnsConfig - - VersionMarkConfig_ReadFromFile_NonExistentFile_ThrowsArgumentException - - VersionMarkConfig_ReadFromFile_InvalidYaml_ThrowsArgumentException - - VersionMarkConfig_ReadFromFile_NoTools_ThrowsArgumentException + - title: VersionMark Requirements + sections: + - title: Configuration Requirements + sections: + - title: VersionMarkConfig Requirements + requirements: + - id: VersionMark-VersionMarkConfig-ReadFromFile + title: >- + The VersionMarkConfig.ReadFromFile method shall load and parse a + .versionmark.yaml file, validating structure and content. + justification: | + ReadFromFile delegates to Load and converts the first error-level issue into an + ArgumentException, acting as a backward-compatibility wrapper for callers that do + not require the full lint-issue list. It is simpler to call when only the loaded + configuration is needed and error details beyond the first are not important. + tests: + - VersionMarkConfig_ReadFromFile_ValidFile_ReturnsConfig + - VersionMarkConfig_ReadFromFile_WithAllOsOverrides_ReturnsConfig + - VersionMarkConfig_ReadFromFile_NonExistentFile_ThrowsArgumentException + - VersionMarkConfig_ReadFromFile_InvalidYaml_ThrowsArgumentException + - VersionMarkConfig_ReadFromFile_NoTools_ThrowsArgumentException - - id: VersionMark-VersionMarkConfig-Load - title: >- - The VersionMarkConfig.Load method shall perform a full validation - of a .versionmark.yaml file and return both the parsed configuration and all - discovered issues. - justification: | - Load provides the primary API for lint-aware configuration loading. It validates - the file in a single pass, accumulating all warnings and errors rather than - failing on the first problem, and returns null config when any error-severity - issue is found. - tests: - - VersionMarkConfig_Load_ValidConfig_ReturnsConfig - - VersionMarkConfig_Load_MissingFile_ReturnsNullConfig - - VersionMarkConfig_Load_InvalidYaml_ReturnsNullConfig - - VersionMarkConfig_Load_MissingToolsSection_ReturnsNullConfig - - VersionMarkConfig_Load_EmptyToolsSection_ReturnsNullConfig - - VersionMarkConfig_Load_MissingCommand_ReturnsNullConfig - - VersionMarkConfig_Load_EmptyCommand_ReturnsNullConfig - - VersionMarkConfig_Load_MissingRegex_ReturnsNullConfig - - VersionMarkConfig_Load_EmptyRegex_ReturnsNullConfig - - VersionMarkConfig_Load_InvalidRegex_ReturnsNullConfig - - VersionMarkConfig_Load_RegexMissingVersionGroup_ReturnsNullConfig - - VersionMarkConfig_Load_UnknownTopLevelKey_ReturnsConfig - - VersionMarkConfig_Load_UnknownToolKey_ReturnsConfig + - id: VersionMark-VersionMarkConfig-Load + title: >- + The VersionMarkConfig.Load method shall perform a full validation + of a .versionmark.yaml file and return both the parsed configuration + and + all discovered issues. + justification: | + Load provides the primary API for lint-aware configuration loading. It validates + the file in a single pass, accumulating all warnings and errors rather than + failing on the first problem, and returns null config when any error-severity + issue is found. + tests: + - VersionMarkConfig_Load_ValidConfig_ReturnsConfig + - VersionMarkConfig_Load_MissingFile_ReturnsNullConfig + - VersionMarkConfig_Load_InvalidYaml_ReturnsNullConfig + - VersionMarkConfig_Load_MissingToolsSection_ReturnsNullConfig + - VersionMarkConfig_Load_EmptyToolsSection_ReturnsNullConfig + - VersionMarkConfig_Load_MissingCommand_ReturnsNullConfig + - VersionMarkConfig_Load_EmptyCommand_ReturnsNullConfig + - VersionMarkConfig_Load_MissingRegex_ReturnsNullConfig + - VersionMarkConfig_Load_EmptyRegex_ReturnsNullConfig + - VersionMarkConfig_Load_InvalidRegex_ReturnsNullConfig + - VersionMarkConfig_Load_RegexMissingVersionGroup_ReturnsNullConfig + - VersionMarkConfig_Load_UnknownTopLevelKey_ReturnsConfig + - VersionMarkConfig_Load_UnknownToolKey_ReturnsConfig - - id: VersionMark-VersionMarkConfig-FindVersions - title: >- - The VersionMarkConfig.FindVersions method shall execute tool commands and - extract version strings using the configured regex patterns. - justification: | - FindVersions is the core capture mechanism. It runs each configured tool's - command and applies the regex to extract the version string, returning a - VersionInfo record ready for serialization. - tests: - - VersionMarkConfig_FindVersions_DotnetCommand_ReturnsVersionInfo - - VersionMarkConfig_FindVersions_MultipleTools_ReturnsAllVersions - - VersionMarkConfig_FindVersions_NonExistentTool_ThrowsArgumentException - - VersionMarkConfig_FindVersions_InvalidCommand_ThrowsInvalidOperationException - - VersionMarkConfig_FindVersions_RegexNoMatch_ThrowsInvalidOperationException - - VersionMarkConfig_FindVersions_RegexNoVersionGroup_ThrowsInvalidOperationException + - id: VersionMark-VersionMarkConfig-FindVersions + title: >- + The VersionMarkConfig.FindVersions method shall execute tool commands + and + extract version strings using the configured regex patterns. + justification: | + FindVersions is the core capture mechanism. It runs each configured tool's + command and applies the regex to extract the version string, returning a + VersionInfo record ready for serialization. + tests: + - VersionMarkConfig_FindVersions_DotnetCommand_ReturnsVersionInfo + - VersionMarkConfig_FindVersions_MultipleTools_ReturnsAllVersions + - VersionMarkConfig_FindVersions_NonExistentTool_ThrowsArgumentException + - VersionMarkConfig_FindVersions_InvalidCommand_ThrowsInvalidOperationException + - VersionMarkConfig_FindVersions_RegexNoMatch_ThrowsInvalidOperationException + - VersionMarkConfig_FindVersions_RegexNoVersionGroup_ThrowsInvalidOperationException diff --git a/docs/reqstream/version-mark/platform-requirements.yaml b/docs/reqstream/version-mark/platform-requirements.yaml index cb7be1b..5243ce0 100644 --- a/docs/reqstream/version-mark/platform-requirements.yaml +++ b/docs/reqstream/version-mark/platform-requirements.yaml @@ -2,7 +2,7 @@ sections: - title: VersionMark Requirements sections: - - title: Platform Support + - title: Platform Support Requirements requirements: - id: VersionMark-Platform-Windows title: The tool shall build and run on Windows platforms. diff --git a/docs/reqstream/version-mark/publishing.yaml b/docs/reqstream/version-mark/publishing.yaml index 2c1b46f..59302b3 100644 --- a/docs/reqstream/version-mark/publishing.yaml +++ b/docs/reqstream/version-mark/publishing.yaml @@ -2,7 +2,7 @@ sections: - title: VersionMark Requirements sections: - - title: Publish Command + - title: Publish Requirements requirements: - id: VersionMark-Publish-Publish title: The tool shall support --publish flag to enable publish mode. @@ -13,9 +13,11 @@ sections: - publish tests: - Publishing_Format_MultipleCaptureFiles_ProducesConsolidatedReport + - Publishing_Run_WithGlobPattern_ReadsMatchingFiles - id: VersionMark-Publish-Report - title: The tool shall support --report parameter to specify the output markdown file path in publish mode. + title: The tool shall support --report parameter to specify the output + markdown file path in publish mode. justification: | Allows users to control where the generated markdown report is saved, enabling integration with documentation systems and custom workflows. @@ -23,9 +25,11 @@ sections: - publish tests: - Publishing_Format_MultipleCaptureFiles_ProducesConsolidatedReport + - Publishing_Run_WithGlobPattern_ReadsMatchingFiles - id: VersionMark-Publish-ReportDepth - title: The tool shall support --report-depth parameter to control heading depth in generated markdown. + title: The tool shall support --report-depth parameter to control heading + depth in generated markdown. justification: | Enables users to adjust the heading level of the generated markdown to fit within existing document structures and hierarchy requirements. @@ -36,7 +40,8 @@ sections: - Publishing_Run_WithReportDepth_UsesCorrectDepth - id: VersionMark-Publish-RequireReport - title: The tool shall require --report parameter when publish mode is enabled. + title: The tool shall require --report parameter when publish mode is + enabled. justification: | Ensures users specify an output file location for the generated markdown, preventing ambiguity about where the report will be saved. @@ -46,7 +51,8 @@ sections: - Publishing_Run_WithoutReport_ReportsError - id: VersionMark-Publish-GlobPattern - title: The tool shall accept glob patterns after -- separator to specify input JSON files in publish mode. + title: The tool shall accept glob patterns after -- separator to specify + input JSON files in publish mode. justification: | Allows users to flexibly select which captured JSON files to include in the report using standard glob patterns like versionmark-*.json. @@ -56,18 +62,22 @@ sections: - Publishing_Run_WithGlobPattern_ReadsMatchingFiles - id: VersionMark-Publish-Consolidate - title: The tool shall read and parse JSON files matching the specified glob patterns in publish mode. + title: The tool shall read and parse JSON files matching the specified + glob patterns in publish mode. justification: | Implements the core functionality of loading captured version data from multiple JSON files for report generation. tags: - publish + children: + - VersionMark-VersionInfo-Load tests: - Publishing_Format_MultipleCaptureFiles_ProducesConsolidatedReport - Publishing_Format_IdenticalVersionsAcrossJobs_ConsolidatesVersions - id: VersionMark-Publish-ConflictReport - title: The tool shall report errors when no JSON files match the specified glob patterns. + title: The tool shall report errors when no JSON files match the specified + glob patterns. justification: | Provides clear feedback to users when input files cannot be found, preventing silent failures and helping diagnose configuration issues. @@ -86,11 +96,15 @@ sections: across their CI/CD jobs. tags: - publish + children: + - VersionMark-Formatter-Versions + - VersionMark-Formatter-MarkdownList tests: - Publishing_Format_ConflictingVersions_ShowsJobIds - id: VersionMark-Publish-FileError - title: The tool shall report errors when JSON files cannot be read or parsed in publish mode. + title: The tool shall report errors when JSON files cannot be read or + parsed in publish mode. justification: | Provides clear feedback when input files are malformed or inaccessible, helping users identify and fix data quality issues. diff --git a/docs/reqstream/version-mark/publishing/markdown-formatter.yaml b/docs/reqstream/version-mark/publishing/markdown-formatter.yaml index fce7f6e..c8a21dc 100644 --- a/docs/reqstream/version-mark/publishing/markdown-formatter.yaml +++ b/docs/reqstream/version-mark/publishing/markdown-formatter.yaml @@ -2,21 +2,23 @@ sections: - title: VersionMark Requirements sections: - - title: Markdown Formatter + - title: Markdown Formatter Requirements requirements: - id: VersionMark-Formatter-Structure - title: The tool shall generate markdown output listing all tools in sorted order. + title: The tool shall generate markdown output listing all tools in sorted + order. justification: | Provides consistent, predictable output ordering that makes reports easier to read and compare across multiple runs. tags: - formatter tests: - - MarkdownFormatter_FormatVersions_SortsToolsAlphabetically + - MarkdownFormatter_Format_SortsToolsAlphabetically - id: VersionMark-Formatter-JobId title: >- - The tool shall suppress job IDs and emit only the version string when the same + The tool shall suppress job IDs and emit only the version string when + the same version appears across all input files for a tool. justification: | Reduces redundancy in reports when all jobs used the same version of a tool, @@ -24,11 +26,12 @@ sections: tags: - formatter tests: - - MarkdownFormatter_FormatVersions_WithUniformVersions_ShowsVersionOnly + - MarkdownFormatter_Format_WithUniformVersions_ShowsVersionOnly - id: VersionMark-Formatter-Versions title: >- - The tool shall display individual job IDs with their versions when versions differ + The tool shall display individual job IDs with their versions when versions + differ across input files. justification: | Provides detailed visibility into version differences across jobs, @@ -36,11 +39,12 @@ sections: tags: - formatter tests: - - MarkdownFormatter_FormatVersions_WithDifferentVersions_ShowsIndividualJobs + - MarkdownFormatter_Format_WithDifferentVersions_ShowsIndividualJobs - id: VersionMark-Formatter-MarkdownList title: >- - The tool shall use bold formatting and parenthesized job IDs to indicate which + The tool shall use bold formatting and parenthesized job IDs to indicate + which jobs had which versions. justification: | Provides clear visual distinction between different job executions and their @@ -48,14 +52,15 @@ sections: tags: - formatter tests: - - MarkdownFormatter_FormatVersions_WithDifferentVersions_ShowsIndividualJobs + - MarkdownFormatter_Format_WithDifferentVersions_ShowsIndividualJobs - id: VersionMark-Formatter-MarkdownConsolidation - title: The tool shall respect the report-depth parameter when generating markdown heading levels. + title: The tool shall respect the report-depth parameter when generating + markdown heading levels. justification: | Allows the generated markdown to integrate properly into existing document structures with appropriate heading hierarchy. tags: - formatter tests: - - MarkdownFormatter_FormatVersions_WithCustomDepth_UsesCorrectHeadingLevel + - MarkdownFormatter_Format_WithCustomDepth_UsesCorrectHeadingLevel diff --git a/docs/reqstream/version-mark/self-test.yaml b/docs/reqstream/version-mark/self-test.yaml index 220e6be..1afae3f 100644 --- a/docs/reqstream/version-mark/self-test.yaml +++ b/docs/reqstream/version-mark/self-test.yaml @@ -2,7 +2,7 @@ sections: - title: VersionMark Requirements sections: - - title: Validation + - title: SelfTest Requirements requirements: - id: VersionMark-Validate-Capture title: The tool shall verify the capture workflow end-to-end during self-validation. @@ -12,8 +12,11 @@ sections: deployment environment, catching integration problems before real runs. tags: - validation + children: + - VersionMark-Validation-Capture tests: - SelfTest_Run_WithResultsFlag_WritesResultsFile + - SelfTest_Run_Capture_CapturesToolVersions - id: VersionMark-Validate-Publish title: The tool shall verify the publish workflow end-to-end during self-validation. @@ -22,28 +25,53 @@ sections: capture files and produce markdown reports in the deployment environment. tags: - validation + children: + - VersionMark-Validation-Publish + - VersionMark-Validation-HeaderDepth tests: - SelfTest_Run_WithResultsFlag_WritesResultsFile + - SelfTest_Run_Publish_GeneratesMarkdownReport - - id: VersionMark-Validate-Lint + - id: VersionMark-Validate-LintValid title: >- - The tool shall verify that lint mode correctly accepts valid configurations - and rejects invalid configurations during self-validation. + The tool shall verify that lint mode correctly accepts valid + configurations during self-validation. justification: | - Self-validation of lint mode confirms the configuration validation logic - is functioning correctly in the deployment environment. + Self-validation of lint mode's valid-config acceptance confirms the + configuration validation logic approves correct configurations in the + deployment environment. tags: - validation + children: + - VersionMark-Validation-LintValid tests: - - SelfTest_Run_WithResultsFlag_WritesResultsFile + - SelfTest_Run_LintValid_PassesForValidConfig + + - id: VersionMark-Validate-LintInvalid + title: >- + The tool shall verify that lint mode correctly rejects invalid + configurations during self-validation. + justification: | + Self-validation of lint mode's error-rejection confirms the + configuration validation logic rejects bad configurations in the + deployment environment. + tags: + - validation + children: + - VersionMark-Validation-LintInvalid + tests: + - SelfTest_Run_LintInvalid_RejectsInvalidConfig - id: VersionMark-Validate-Results - title: The tool shall write self-validation results to a file in TRX or JUnit XML format. + title: The tool shall write self-validation results to a file in TRX or + JUnit XML format. justification: | Standard test result formats allow CI/CD systems and dashboards to consume and display self-validation outcomes without custom tooling. tags: - validation + children: + - VersionMark-Validation-WriteResults tests: - SelfTest_Run_WithResultsFlag_WritesResultsFile - SelfTest_Run_WithResultsXmlFlag_WritesJUnitResultsFile diff --git a/docs/reqstream/version-mark/self-test/validation.yaml b/docs/reqstream/version-mark/self-test/validation.yaml index 2beff46..ced2252 100644 --- a/docs/reqstream/version-mark/self-test/validation.yaml +++ b/docs/reqstream/version-mark/self-test/validation.yaml @@ -1,55 +1,70 @@ --- sections: - - title: Validation Unit Requirements - requirements: - - id: VersionMark-Validation-Capture - title: The Validation class shall verify the capture mode end-to-end. - justification: | - Self-validation of the capture workflow confirms the tool is correctly - installed and able to execute commands and produce JSON output in the - deployment environment. - tests: - - VersionMark_CapturesVersions + - title: VersionMark Requirements + sections: + - title: SelfTest Requirements + sections: + - title: Validation Requirements + requirements: + - id: VersionMark-Validation-Capture + title: The Validation class shall verify the capture mode end-to-end. + justification: | + Self-validation of the capture workflow confirms the tool is correctly + installed and able to execute commands and produce JSON output in the + deployment environment. + tests: + - VersionMark_CapturesVersions - - id: VersionMark-Validation-Publish - title: The Validation class shall verify the publish mode end-to-end. - justification: | - Self-validation of the publish workflow confirms the tool is correctly - able to read JSON capture files and produce markdown reports in the - deployment environment. - tests: - - VersionMark_GeneratesMarkdownReport + - id: VersionMark-Validation-Publish + title: The Validation class shall verify the publish mode end-to-end. + justification: | + Self-validation of the publish workflow confirms the tool is correctly + able to read JSON capture files and produce markdown reports in the + deployment environment. + tests: + - VersionMark_GeneratesMarkdownReport - - id: VersionMark-Validation-Lint - title: >- - The Validation class shall verify that lint mode correctly accepts valid - and rejects invalid configurations. - justification: | - Self-validation of lint mode confirms that the configuration validation - logic is functioning correctly in the deployment environment. - tests: - - VersionMark_LintPassesForValidConfig - - VersionMark_LintReportsErrorsForInvalidConfig + - id: VersionMark-Validation-LintValid + title: The Validation class shall verify that lint mode accepts valid + configurations. + justification: | + Self-validation of lint mode's valid-config acceptance confirms the + configuration validation logic approves correct configurations in the + deployment environment. + tests: + - VersionMark_LintPassesForValidConfig - - id: VersionMark-Validation-HeaderDepth - title: >- - The Validation class shall use the heading depth specified by the --depth - argument for the self-validation report. - justification: | - The --depth argument allows callers to embed the self-validation report - at the appropriate heading level within a larger markdown document. - tests: - - SelfTest_Run_WithDepthTwo_WritesHashHashHeader + - id: VersionMark-Validation-LintInvalid + title: The Validation class shall verify that lint mode rejects invalid + configurations. + justification: | + Self-validation of lint mode's error-rejection confirms the + configuration validation logic rejects bad configurations in the + deployment environment. + tests: + - VersionMark_LintReportsErrorsForInvalidConfig - - id: VersionMark-Validation-WriteResults - title: >- - The Validation class shall write self-validation results to a TRX or JUnit XML - file when a results file path is specified. - justification: | - Standard test result formats allow CI/CD systems and dashboards to consume - and display self-validation outcomes without custom tooling. Supporting both - TRX (.trx) and JUnit XML (.xml) ensures compatibility with MSTest-based and - JUnit-based CI pipelines. - tests: - - SelfTest_Run_WithResultsFlag_WritesResultsFile - - SelfTest_Run_WithResultsXmlFlag_WritesJUnitResultsFile + - id: VersionMark-Validation-HeaderDepth + title: >- + The Validation class shall use the heading depth specified by the + --depth + argument for the self-validation report. + justification: | + The --depth argument allows callers to embed the self-validation report + at the appropriate heading level within a larger markdown document. + tests: + - SelfTest_Run_WithDepthTwo_WritesHashHashHeader + + - id: VersionMark-Validation-WriteResults + title: >- + The Validation class shall write self-validation results to a TRX + or JUnit + XML file when a results file path is specified. + justification: | + Standard test result formats allow CI/CD systems and dashboards to consume + and display self-validation outcomes without custom tooling. Supporting both + TRX (.trx) and JUnit XML (.xml) ensures compatibility with MSTest-based and + JUnit-based CI pipelines. + tests: + - SelfTest_Run_WithResultsFlag_WritesResultsFile + - SelfTest_Run_WithResultsXmlFlag_WritesJUnitResultsFile diff --git a/docs/reqstream/version-mark/utilities.yaml b/docs/reqstream/version-mark/utilities.yaml index 0a67b5e..e6fa95b 100644 --- a/docs/reqstream/version-mark/utilities.yaml +++ b/docs/reqstream/version-mark/utilities.yaml @@ -2,7 +2,7 @@ sections: - title: VersionMark Requirements sections: - - title: Utilities + - title: Utilities Requirements requirements: - id: VersionMark-Utilities-GlobMatch title: The Utilities subsystem shall provide glob-pattern file matching. @@ -13,6 +13,11 @@ sections: children: - VersionMark-GlobMatcher-FindFiles - VersionMark-GlobMatcher-AbsolutePaths + - VersionMark-GlobMatcher-DeduplicatedSortedOutput + tests: + - GlobMatcher_FindMatchingFiles_RelativePattern_ReturnsMatchingFiles + - GlobMatcher_FindMatchingFiles_AbsolutePattern_ReturnsMatchingFiles + - GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles - id: VersionMark-Utilities-SafePath title: The Utilities subsystem shall provide safe path combination. @@ -22,3 +27,6 @@ sections: file paths from partially-trusted input. children: - VersionMark-PathHelpers-SafeCombine + tests: + - PathHelpers_SafePathCombine_ValidPaths_CombinesCorrectly + - PathHelpers_SafePathCombine_PathTraversalWithDoubleDots_ThrowsArgumentException diff --git a/docs/reqstream/version-mark/utilities/glob-matcher.yaml b/docs/reqstream/version-mark/utilities/glob-matcher.yaml index a274fb4..89e4307 100644 --- a/docs/reqstream/version-mark/utilities/glob-matcher.yaml +++ b/docs/reqstream/version-mark/utilities/glob-matcher.yaml @@ -1,31 +1,47 @@ --- sections: - - title: GlobMatcher Unit Requirements - requirements: - - id: VersionMark-GlobMatcher-FindFiles - title: The GlobMatcher class shall find files matching relative glob patterns relative to the current directory. - justification: | - Publish mode accepts glob patterns supplied on the command line, which are - typically relative to the working directory. GlobMatcher must evaluate these - relative patterns against the current directory so callers do not need to - resolve them manually. - tests: - - GlobMatcher_FindMatchingFiles_RelativePattern_ReturnsMatchingFiles - - GlobMatcher_FindMatchingFiles_EmptyPatterns_ReturnsEmptyList - - GlobMatcher_FindMatchingFiles_PatternMatchingNoFiles_ReturnsEmptyList - - GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles + - title: VersionMark Requirements + sections: + - title: Utilities + sections: + - title: GlobMatcher Requirements + requirements: + - id: VersionMark-GlobMatcher-FindFiles + title: The GlobMatcher class shall find files matching relative glob + patterns relative to the current directory. + justification: | + Publish mode accepts glob patterns supplied on the command line, which are + typically relative to the working directory. GlobMatcher must evaluate these + relative patterns against the current directory so callers do not need to + resolve them manually. + tests: + - GlobMatcher_FindMatchingFiles_RelativePattern_ReturnsMatchingFiles + - GlobMatcher_FindMatchingFiles_EmptyPatterns_ReturnsEmptyList + - GlobMatcher_FindMatchingFiles_PatternMatchingNoFiles_ReturnsEmptyList + - GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles - - id: VersionMark-GlobMatcher-AbsolutePaths - title: >- - The GlobMatcher class shall find files matching absolute glob patterns - regardless of the current working directory. - justification: | - CI/CD pipelines frequently pass fully-qualified artifact paths to VersionMark. - GlobMatcher must evaluate absolute patterns from their own root directory so - that the caller's current working directory does not affect the result. - tests: - - GlobMatcher_FindMatchingFiles_AbsolutePattern_ReturnsMatchingFiles - - GlobMatcher_FindMatchingFiles_SingleFileAbsolutePath_ReturnsSingleFile - - GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles - - GlobMatcher_SplitAbsolutePattern_PatternWithWildcard_SplitsCorrectly - - GlobMatcher_SplitAbsolutePattern_PatternWithoutWildcard_SplitsAtLastSeparator + - id: VersionMark-GlobMatcher-AbsolutePaths + title: >- + The GlobMatcher class shall find files matching absolute glob patterns + regardless of the current working directory. + justification: | + CI/CD pipelines frequently pass fully-qualified artifact paths to VersionMark. + GlobMatcher must evaluate absolute patterns from their own root directory so + that the caller's current working directory does not affect the result. + tests: + - GlobMatcher_FindMatchingFiles_AbsolutePattern_ReturnsMatchingFiles + - GlobMatcher_FindMatchingFiles_SingleFileAbsolutePath_ReturnsSingleFile + - GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles + - GlobMatcher_SplitAbsolutePattern_PatternWithWildcard_SplitsCorrectly + - GlobMatcher_SplitAbsolutePattern_PatternWithoutWildcard_SplitsAtLastSeparator + + - id: VersionMark-GlobMatcher-DeduplicatedSortedOutput + title: >- + The GlobMatcher class shall return a sorted, deduplicated list of + matching file paths. + justification: | + When multiple overlapping glob patterns match the same file, the file + should appear only once in the result. Sorted output ensures deterministic + ordering across calls, making results predictable for downstream consumers. + tests: + - GlobMatcher_FindMatchingFiles_OverlappingPatterns_DeduplicatesResults diff --git a/docs/reqstream/version-mark/utilities/path-helpers.yaml b/docs/reqstream/version-mark/utilities/path-helpers.yaml index 7e50308..f124911 100644 --- a/docs/reqstream/version-mark/utilities/path-helpers.yaml +++ b/docs/reqstream/version-mark/utilities/path-helpers.yaml @@ -1,22 +1,27 @@ --- sections: - - title: PathHelpers Unit Requirements - requirements: - - id: VersionMark-PathHelpers-SafeCombine - title: The PathHelpers class shall combine paths safely, rejecting path-traversal attempts. - justification: | - Constructing file paths from partially-trusted input (such as job IDs and - configuration values) carries a path-traversal risk. SafePathCombine - enforces that the resulting path stays within the intended base directory, - defending against both explicit ".." segments and rooted path injections. - tests: - - PathHelpers_SafePathCombine_ValidPaths_CombinesCorrectly - - PathHelpers_SafePathCombine_PathTraversalWithDoubleDots_ThrowsArgumentException - - PathHelpers_SafePathCombine_DoubleDotsInMiddle_ThrowsArgumentException - - PathHelpers_SafePathCombine_AbsolutePath_ThrowsArgumentException - - PathHelpers_SafePathCombine_CurrentDirectoryReference_CombinesCorrectly - - PathHelpers_SafePathCombine_NestedPaths_CombinesCorrectly - - PathHelpers_SafePathCombine_EmptyRelativePath_ReturnsBasePath - - PathHelpers_SafePathCombine_DotDotAsNamePrefix_CombinesCorrectly - - PathHelpers_SafePathCombine_NullBasePath_ThrowsArgumentNullException - - PathHelpers_SafePathCombine_NullRelativePath_ThrowsArgumentNullException + - title: VersionMark Requirements + sections: + - title: Utilities Requirements + sections: + - title: PathHelpers Requirements + requirements: + - id: VersionMark-PathHelpers-SafeCombine + title: The PathHelpers class shall combine paths safely, rejecting + path-traversal attempts. + justification: | + Constructing file paths from partially-trusted input (such as job IDs and + configuration values) carries a path-traversal risk. SafePathCombine + enforces that the resulting path stays within the intended base directory, + defending against both explicit ".." segments and rooted path injections. + tests: + - PathHelpers_SafePathCombine_ValidPaths_CombinesCorrectly + - PathHelpers_SafePathCombine_PathTraversalWithDoubleDots_ThrowsArgumentException + - PathHelpers_SafePathCombine_DoubleDotsInMiddle_ThrowsArgumentException + - PathHelpers_SafePathCombine_AbsolutePath_ThrowsArgumentException + - PathHelpers_SafePathCombine_CurrentDirectoryReference_CombinesCorrectly + - PathHelpers_SafePathCombine_NestedPaths_CombinesCorrectly + - PathHelpers_SafePathCombine_EmptyRelativePath_ReturnsBasePath + - PathHelpers_SafePathCombine_DotDotAsNamePrefix_CombinesCorrectly + - PathHelpers_SafePathCombine_NullBasePath_ThrowsArgumentNullException + - PathHelpers_SafePathCombine_NullRelativePath_ThrowsArgumentNullException diff --git a/docs/requirements_doc/definition.yaml b/docs/requirements_doc/definition.yaml index 628b789..bc9c807 100644 --- a/docs/requirements_doc/definition.yaml +++ b/docs/requirements_doc/definition.yaml @@ -1,12 +1,10 @@ --- -resource-path: - - docs/requirements_doc - - docs/template +resource-path: [docs/requirements_doc, docs/template] input-files: - docs/requirements_doc/title.txt - docs/requirements_doc/introduction.md - - docs/requirements_doc/generated/requirements.md - - docs/requirements_doc/generated/justifications.md + - docs/requirements_doc/generated/requirements.md # Generated by ReqStream (requirements listing) + - docs/requirements_doc/generated/justifications.md # Generated by ReqStream (requirement justifications) template: template.html table-of-contents: true number-sections: true diff --git a/docs/requirements_doc/introduction.md b/docs/requirements_doc/introduction.md index 53121a6..ee78a9c 100644 --- a/docs/requirements_doc/introduction.md +++ b/docs/requirements_doc/introduction.md @@ -1,30 +1,17 @@ # Introduction -This document contains the requirements for the VersionMark project. +This document lists all requirements for VersionMark. ## Purpose -VersionMark is a reference implementation demonstrating best practices for DEMA Consulting -.NET command-line tools. It provides a standardized approach to command-line argument parsing, -self-validation, and comprehensive documentation generation. +To provide a complete, traceable record of all requirements for VersionMark, +including requirements at the system, subsystem, and unit levels, plus OTS and +Shared Package requirements. ## Scope -This requirements document covers: +This document covers all requirements defined in `docs/reqstream/` for VersionMark. -- Command-line interface and options -- Self-validation framework -- Test result output formats -- Logging capabilities -- Multi-platform support -- Documentation generation -- CI/CD integration +## References -## Audience - -This document is intended for: - -- Software developers working on VersionMark -- Quality assurance teams validating requirements -- Project stakeholders reviewing project scope -- Users understanding the tool's capabilities +N/A diff --git a/docs/requirements_doc/title.txt b/docs/requirements_doc/title.txt index 6345471..02d5ac5 100644 --- a/docs/requirements_doc/title.txt +++ b/docs/requirements_doc/title.txt @@ -1,13 +1,12 @@ --- -title: VersionMark Requirements -subtitle: Requirements Specification for the VersionMark -author: DEMA Consulting -description: Requirements Specification for the VersionMark +title: "VersionMark Requirements Document" +subtitle: "A .NET tool for CI/CD tool version tracking" +author: "DEMA Consulting" +description: "Requirements Document for VersionMark" lang: en-US keywords: - - VersionMark - Requirements - - Specification + - VersionMark + - C# - .NET - - Command-Line Tool ---- +--- \ No newline at end of file diff --git a/docs/requirements_report/definition.yaml b/docs/requirements_report/definition.yaml index 9ee62a4..e8b5571 100644 --- a/docs/requirements_report/definition.yaml +++ b/docs/requirements_report/definition.yaml @@ -1,11 +1,9 @@ --- -resource-path: - - docs/requirements_report - - docs/template +resource-path: [docs/requirements_report, docs/template] input-files: - docs/requirements_report/title.txt - docs/requirements_report/introduction.md - - docs/requirements_report/generated/trace_matrix.md + - docs/requirements_report/generated/trace_matrix.md # Generated by ReqStream (requirements traceability matrix) template: template.html table-of-contents: true number-sections: true diff --git a/docs/requirements_report/introduction.md b/docs/requirements_report/introduction.md index 8c27f68..8a87508 100644 --- a/docs/requirements_report/introduction.md +++ b/docs/requirements_report/introduction.md @@ -1,27 +1,17 @@ # Introduction -This document contains the requirements traceability matrix for the VersionMark project. +This document provides the requirements Trace Matrix for VersionMark, +mapping each requirement to its corresponding test evidence. ## Purpose -The trace matrix provides traceability between requirements and test cases, ensuring that -all requirements are validated through appropriate testing. This demonstrates that the -VersionMark meets its specified requirements. +To demonstrate that every requirement is covered by at least one passing test, +providing compliance evidence for VersionMark. ## Scope -This traceability matrix covers: +This document covers all requirements in `docs/reqstream/` and their test evidence. -- Mapping of requirements to test cases -- Test execution results linked to requirements -- Validation of requirement coverage -- Platform-specific test execution +## References -## Audience - -This document is intended for: - -- Software developers working on VersionMark -- Quality assurance teams validating requirements coverage -- Project stakeholders reviewing test coverage -- Auditors verifying requirements traceability +[VersionMark releases](https://github.com/demaconsulting/VersionMark/releases) diff --git a/docs/requirements_report/title.txt b/docs/requirements_report/title.txt index 3f3146d..5f1b781 100644 --- a/docs/requirements_report/title.txt +++ b/docs/requirements_report/title.txt @@ -1,14 +1,13 @@ --- -title: VersionMark Trace Matrix -subtitle: Requirements Traceability Matrix for the VersionMark -author: DEMA Consulting -description: Requirements Traceability Matrix for the VersionMark +title: "VersionMark Trace Matrix" +subtitle: "A .NET tool for CI/CD tool version tracking" +author: "DEMA Consulting" +description: "Trace Matrix for VersionMark" lang: en-US keywords: - - VersionMark - - Trace Matrix - - Traceability - Requirements - - Testing + - Trace Matrix + - VersionMark + - C# - .NET ---- +--- \ No newline at end of file diff --git a/docs/user_guide/definition.yaml b/docs/user_guide/definition.yaml index 01a2e76..28568be 100644 --- a/docs/user_guide/definition.yaml +++ b/docs/user_guide/definition.yaml @@ -1,7 +1,5 @@ --- -resource-path: - - docs/user_guide - - docs/template +resource-path: [docs/user_guide, docs/template] input-files: - docs/user_guide/title.txt - docs/user_guide/introduction.md diff --git a/docs/user_guide/introduction.md b/docs/user_guide/introduction.md index 962e4ee..e307686 100644 --- a/docs/user_guide/introduction.md +++ b/docs/user_guide/introduction.md @@ -1,25 +1,27 @@ -# Introduction +# Introduction -VersionMark is a tool for capturing and publishing tool version information across CI/CD -environments. It helps track which versions of build tools, compilers, and dependencies are -used in different jobs and environments. +This guide describes how to install, configure, and use VersionMark. ## Purpose -VersionMark provides a standardized way to capture, track, and document tool versions used -across different CI/CD jobs and environments. This ensures transparency, reproducibility, and -helps teams understand which tool versions are used in their build and deployment pipelines. +VersionMark is a .NET tool for capturing and publishing tool version information across +CI/CD environments. It helps teams track which versions of build tools, compilers, and +dependencies are used in different jobs and environments, ensuring transparency, +reproducibility, and traceability between software artifacts and the build environment. + +VersionMark follows the [Continuous Compliance](https://github.com/demaconsulting/ContinuousCompliance) +methodology, which ensures compliance evidence is generated automatically on every CI run. ## Scope -This user guide covers: +This guide covers installation, configuration, capture and publish workflows, +command-line options, self-validation, CI/CD integration, troubleshooting, and best +practices. Prerequisites: .NET 8 or later. + +## References -- Installing and configuring VersionMark -- Capturing tool versions in CI/CD environments -- Publishing version information to documentation -- Integrating with CI/CD systems like GitHub Actions -- Configuration file format and options -- Troubleshooting and best practices +- [Continuous Compliance](https://github.com/demaconsulting/ContinuousCompliance) +- [VersionMark releases](https://github.com/demaconsulting/VersionMark/releases) # Continuous Compliance @@ -29,11 +31,11 @@ compliance evidence is generated automatically on every CI run. ## Key Practices - **Requirements Traceability**: Every requirement is linked to passing tests, and a trace matrix is - auto-generated on each release -- **Linting Enforcement**: markdownlint, cspell, and yamllint are enforced before any build proceeds + auto-generated on each release. +- **Linting Enforcement**: markdownlint, cspell, and yamllint are enforced before any build proceeds. - **Automated Audit Documentation**: Each release ships with generated requirements, justifications, - trace matrix, and quality reports -- **CodeQL and SonarCloud**: Security and quality analysis runs on every build + trace matrix, and quality reports. +- **CodeQL and SonarCloud**: Security and quality analysis runs on every build. # Installation @@ -54,11 +56,11 @@ tools: dotnet: command: dotnet --version regex: '(?\d+\.\d+\.\d+)' - + node: command: node --version regex: 'v(?\d+\.\d+\.\d+)' - + gcc: command: gcc --version regex: 'gcc \(.*\) (?\d+\.\d+\.\d+)' @@ -72,7 +74,8 @@ In each CI/CD job, capture tool versions with a unique job identifier: versionmark --capture --job-id "windows-net8" ``` -This creates a JSON file (e.g., `versionmark-windows-net8.json`) containing the captured versions. +This creates a JSON file (for example, `versionmark-windows-net8.json`) containing the +captured versions. ## Step 3: Publish Versions to Documentation @@ -82,35 +85,35 @@ After all jobs complete, publish the captured versions: versionmark --publish --report versions.md ``` -This generates a markdown file consolidating versions from all jobs. +This generates a Markdown file consolidating versions from all jobs. # Command-Line Reference ## Options -| Option | Description | -| ------------------------- | ---------------------------------------------------------------- | -| **General** | | -| `-v`, `--version` | Display version information | -| `-?`, `-h`, `--help` | Display help message | -| `--silent` | Suppress console output | -| `--log ` | Write output to log file | -| `--depth ` | Heading depth for validation and publish mode (default: 1, 1-6) | -| **Lint Mode** | | -| `--lint []` | Check configuration file (default: `.versionmark.yaml`) | -| **Capture Mode** | | -| `--capture` | Enable capture mode | -| `--job-id ` | **(Required)** Unique identifier for this CI/CD job | -| `--output ` | Output JSON file (default: `versionmark-.json`) | -| `-- ` | List of tool names to capture (default: all tools in config) | -| **Publish Mode** | | -| `--publish` | Enable publish mode | -| `--report ` | **(Required)** Output markdown file path | -| `--report-depth ` | Heading depth for markdown output (default: --depth value, 1-6) | -| `-- ` | Glob patterns for JSON files (default: `versionmark-*.json`) | -| **Self-Validation** | | -| `--validate` | Run self-validation tests | -| `--results ` | Write validation results to file (`.trx` or `.xml`) | +| Option | Description | +| ------------------------ | --------------------------------------------------------------- | +| **General** | | +| `-v`, `--version` | Display version information | +| `-?`, `-h`, `--help` | Display help message | +| `--silent` | Suppress console output | +| `--log ` | Write output to log file | +| `--depth ` | Heading depth for validation and publish mode (default: 1, 1-6) | +| **Lint Mode** | | +| `--lint []` | Check configuration file (default: `.versionmark.yaml`) | +| **Capture Mode** | | +| `--capture` | Enable capture mode | +| `--job-id ` | **(Required)** Unique identifier for this CI/CD job | +| `--output ` | Output JSON file (default: `versionmark-.json`) | +| `-- ` | List of tool names to capture (default: all tools in config) | +| **Publish Mode** | | +| `--publish` | Enable publish mode | +| `--report ` | **(Required)** Output Markdown file path | +| `--report-depth ` | Heading depth for Markdown output (default: `--depth` value) | +| `-- ` | Glob patterns for JSON files (default: `versionmark-*.json`) | +| **Self-Validation** | | +| `--validate` | Run self-validation tests | +| `--results ` | Write validation results to file (`.trx` or `.xml`) | ## Lint Mode @@ -124,18 +127,18 @@ versionmark --lint [] The lint command: -- Suppresses the application banner so the output contains only issue lines -- Reports **all** issues found in the configuration file, not just the first -- Includes the filename and line/column number for each issue +- Suppresses the application banner so the output contains only issue lines. +- Reports **all** issues found in the configuration file, not just the first. +- Includes the filename and line/column number for each issue. - Exits with code `0` if no issues are found (producing no output) or when only warnings are - reported -- Exits with code `1` when errors are detected + reported. +- Exits with code `1` when errors are detected. - Checks for: - - Missing `tools` section - - Tools missing required `command` or `regex` fields - - Invalid regex patterns (cannot be compiled) - - Regex patterns missing the required `(?...)` capture group - - Unknown configuration keys (reported as warnings) + - Missing `tools` section. + - Tools missing required `command` or `regex` fields. + - Invalid regex patterns that cannot be compiled. + - Regex patterns missing the required `(?...)` capture group. + - Unknown configuration keys, reported as warnings. ### Lint Examples @@ -189,7 +192,7 @@ versionmark --capture --job-id "macos" --output versions/macos.json ## Publish Mode -Publish captured versions to markdown documentation: +Publish captured versions to Markdown documentation: ```bash versionmark --publish --report [options] [-- pattern1 pattern2 ...] @@ -226,11 +229,11 @@ after the `--` separator: Common glob pattern syntax: -- `*` - Matches any characters within a directory -- `**` - Matches any characters across multiple directory levels -- `?` - Matches any single character -- `[abc]` - Matches one character from the set -- `{a,b}` - Matches either pattern a or b +- `*` matches any characters within a directory. +- `**` matches any characters across multiple directory levels. +- `?` matches any single character. +- `[abc]` matches one character from the set. +- `{a,b}` matches either pattern `a` or pattern `b`. # Configuration File Format @@ -243,11 +246,11 @@ tools: dotnet: command: dotnet --version regex: '(?\d+\.\d+\.\d+)' - + node: command: node --version regex: 'v(?\d+\.\d+\.\d+)' - + python: command: python --version regex: 'Python (?\d+\.\d+\.\d+)' @@ -257,16 +260,16 @@ tools: Each tool entry in the `tools` dictionary supports the following properties: -| Property | Required | Description | -| ---------------- | -------- | ------------------------------------------------------------------- | -| `command` | Yes* | Shell command to execute to get version information | -| `regex` | Yes* | Regular expression with named 'version' group: `(?...)` | -| `command-win` | No | Command override for Windows | -| `command-linux` | No | Command override for Linux | -| `command-macos` | No | Command override for macOS | -| `regex-win` | No | Regex override for Windows | -| `regex-linux` | No | Regex override for Linux | -| `regex-macos` | No | Regex override for macOS | +| Property | Required | Description | +| --------------- | -------- | ---------------------------------------------------------------- | +| `command` | Yes* | Shell command to execute to get version information | +| `regex` | Yes* | Regular expression with named `version` group: `(?...)` | +| `command-win` | No | Command override for Windows | +| `command-linux` | No | Command override for Linux | +| `command-macos` | No | Command override for macOS | +| `regex-win` | No | Regex override for Windows | +| `regex-linux` | No | Regex override for Linux | +| `regex-macos` | No | Regex override for macOS | \* Required unless OS-specific variants (`command-win`/`command-linux`/`command-macos` or `regex-win`/`regex-linux`/`regex-macos`) are provided instead. @@ -287,7 +290,7 @@ tools: regex-win: 'gcc\.exe \(.*\) (?\d+\.\d+\.\d+)' regex-linux: 'gcc-13 \(.*\) (?\d+\.\d+\.\d+)' regex-macos: 'gcc-14 \(.*\) (?\d+\.\d+\.\d+)' - + powershell: command: pwsh --version command-win: powershell -Command "$PSVersionTable.PSVersion.ToString()" @@ -301,13 +304,13 @@ values. ## Commands with Spaces in Paths -When a tool's executable is installed at a path that contains spaces, the path must be -enclosed in double quotes so the shell treats it as a single token. +When a tool's executable is installed at a path that contains spaces, the path must be enclosed +in double quotes so the shell treats it as a single token. ### Windows: Environment Variable Paths Many Windows tools are installed at paths exposed via environment variables. Use the variable -directly inside double quotes: +inside double quotes: ```yaml tools: @@ -316,15 +319,15 @@ tools: command-win: '"%LLVM_PATH%\bin\clang-format" --version' regex: 'clang-format version (?.*)' - # Visual Studio's cl.exe via VSINSTALLDIR + # Visual Studio cl.exe via VSINSTALLDIR msvc: command-win: '"%VSINSTALLDIR%VC\Tools\MSVC\14.39.33519\bin\Hostx64\x64\cl.exe"' regex: 'Version (?[\d.]+)' ``` -> **Note:** The entire value is single-quoted in YAML so that the inner double quotes are -> preserved literally. VersionMark executes the command via `cmd.exe` as `/c "{command}"`, -> preserving inner quoted paths and arguments while still allowing normal `%VAR%` expansion. +> **Note:** The entire value is single-quoted in YAML so the inner double quotes are preserved +> literally. VersionMark executes the command via `cmd.exe` as `/c "{command}"`, preserving +> inner quoted paths and arguments while still allowing normal `%VAR%` expansion. ### Windows: Absolute Paths with Spaces @@ -377,13 +380,15 @@ tools: ## Regular Expression Tips -The regex must contain a named 'version' capture group using .NET syntax `(?...)` that -captures the version number. Examples: +The regex must contain a named `version` capture group using .NET syntax `(?...)` +that captures the version number. Examples: -- **Simple version**: `(?\d+\.\d+\.\d+)` - Captures `1.2.3` -- **Prefixed version**: `Version (?\d+\.\d+\.\d+)` - Captures `1.2.3` from `Version 1.2.3` -- **Multiline output**: `(?m)version (?\d+\.\d+\.\d+)` - Uses multiline mode -- **Build metadata**: `(?\d+\.\d+\.\d+[-+][a-zA-Z0-9.]+)` - Captures `1.2.3-beta.1` +- **Simple version**: `(?\d+\.\d+\.\d+)` captures `1.2.3`. +- **Prefixed version**: `Version (?\d+\.\d+\.\d+)` captures `1.2.3` from + `Version 1.2.3`. +- **Multiline output**: `(?m)version (?\d+\.\d+\.\d+)` uses multiline mode. +- **Build metadata**: `(?\d+\.\d+\.\d+[-+][a-zA-Z0-9.]+)` captures + `1.2.3-beta.1`. # Output Formats @@ -405,12 +410,12 @@ When you run the capture command, VersionMark creates a JSON file with the follo ### JSON Structure -- **JobId**: The unique identifier provided via `--job-id` -- **Versions**: Object mapping tool names to their captured versions +- **JobId**: The unique identifier provided via `--job-id`. +- **Versions**: Object mapping tool names to their captured versions. ## Publish Output (Markdown) -The publish command generates a markdown file with a bulleted list of tool versions: +The publish command generates a Markdown file with a bulleted list of tool versions. ### Example 1: All Jobs Use Same Version @@ -436,16 +441,15 @@ identifiers. - **node**: 21.0.0 (windows-net9, linux-net9) ``` -When a tool has different versions across jobs, each version is listed as a separate bullet -with the jobs that use it shown in parentheses. Job IDs within each group are listed in -alphabetical order. +When a tool has different versions across jobs, each version is listed as a separate bullet with +jobs that use it shown in parentheses. Job IDs within each group are listed in alphabetical order. ### Output Format Details -- **Heading**: Controlled by `--report-depth` parameter (default: `#` for depth 1) -- **Tool Order**: Tools are listed in alphabetical order (case-insensitive) -- **Version Order**: When multiple versions exist, they are sorted alphabetically -- **Job IDs**: Within each version group, job IDs are sorted alphabetically +- **Heading**: Controlled by `--report-depth` parameter (default: `#` for depth 1). +- **Tool Order**: Tools are listed in alphabetical order (case-insensitive). +- **Version Order**: When multiple versions exist, they are sorted alphabetically. +- **Job IDs**: Within each version group, job IDs are sorted alphabetically. # Self-Validation @@ -479,7 +483,7 @@ versionmark --silent --validate --results results.trx ``` Use `--depth` to embed the self-validation report at a specific heading level within a larger -markdown document: +Markdown document: ```bash versionmark --validate --depth 2 @@ -492,13 +496,13 @@ Example output: ```text # DEMA Consulting VersionMark -| Information | Value | -| :------------------ | :------------------------------------------------- | -| Tool Version | 1.2.3 | -| Machine Name | build-agent-01 | -| OS Version | Ubuntu 22.04.3 LTS | -| DotNet Runtime | .NET 10.0.0 | -| Time Stamp | 2025-01-01 12:00:00 UTC | +| Information | Value | +| :------------- | :-------------------------- | +| Tool Version | 1.2.3 | +| Machine Name | build-agent-01 | +| OS Version | Ubuntu 22.04.3 LTS | +| DotNet Runtime | .NET 10.0.0 | +| Time Stamp | 2025-01-01 12:00:00 UTC | ✓ VersionMark_CapturesVersions - Passed ✓ VersionMark_GeneratesMarkdownReport - Passed @@ -514,18 +518,18 @@ If any tests fail, the exit code will be non-zero. ## Validation Tests -| Test | What It Proves | -| :--- | :------------- | -| `VersionMark_CapturesVersions` | The tool can read a `.versionmark.yaml` config and capture versions to a JSON file | -| `VersionMark_GeneratesMarkdownReport` | The tool can read captured JSON files and generate a markdown report | -| `VersionMark_LintPassesForValidConfig` | The lint command exits with code 0 for a valid configuration file | -| `VersionMark_LintReportsErrorsForInvalidConfig` | The lint command exits with code 1 for an invalid config | +| Test | What It Proves | +| :------------------------------------------ | :--------------------------------------------------------- | +| `VersionMark_CapturesVersions` | The tool can read a `.versionmark.yaml` config and capture versions to a JSON file. | +| `VersionMark_GeneratesMarkdownReport` | The tool can read captured JSON files and generate a Markdown report. | +| `VersionMark_LintPassesForValidConfig` | The lint command exits with code 0 for a valid configuration file. | +| `VersionMark_LintReportsErrorsForInvalidConfig` | The lint command exits with code 1 for an invalid config. | # CI/CD Integration ## GitHub Actions Example -Here's a complete example of using VersionMark in a GitHub Actions workflow: +Here is a complete example of using VersionMark in a GitHub Actions workflow: ```yaml name: Build @@ -537,64 +541,64 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v4 - + - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.x' - + - name: Install VersionMark run: dotnet tool install -g DemaConsulting.VersionMark - + - name: Capture tool versions run: versionmark --capture --job-id "windows-net8" - + - name: Upload version capture uses: actions/upload-artifact@v4 with: name: versions-windows-net8 path: versionmark-windows-net8.json - + build-linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - + - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.x' - + - name: Install VersionMark run: dotnet tool install -g DemaConsulting.VersionMark - + - name: Capture tool versions run: versionmark --capture --job-id "linux-net8" - + - name: Upload version capture uses: actions/upload-artifact@v4 with: name: versions-linux-net8 path: versionmark-linux-net8.json - + publish-versions: needs: [build-windows, build-linux] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - + - name: Download all version captures uses: actions/download-artifact@v4 with: pattern: versions-* merge-multiple: true - + - name: Install VersionMark run: dotnet tool install -g DemaConsulting.VersionMark - + - name: Publish versions run: versionmark --publish --report docs/tool-versions.md - + - name: Commit version documentation run: | git config user.name "github-actions[bot]" @@ -606,11 +610,11 @@ jobs: ## Key Integration Points -1. **Install VersionMark**: Install the tool in each job that needs to capture versions -2. **Capture per Job**: Run `versionmark capture` with a unique `--job-id` in each job -3. **Upload Artifacts**: Save the captured JSON files as artifacts -4. **Download Artifacts**: In the publish job, download all captured JSON files -5. **Publish**: Run `versionmark publish` to generate consolidated documentation +1. **Install VersionMark**: Install the tool in each job that needs to capture versions. +2. **Capture per Job**: Run `versionmark --capture` with a unique `--job-id` in each job. +3. **Upload Artifacts**: Save the captured JSON files as artifacts. +4. **Download Artifacts**: In the publish job, download all captured JSON files. +5. **Publish**: Run `versionmark --publish` to generate consolidated documentation. # Common Workflows @@ -624,7 +628,7 @@ tools: dotnet: command: dotnet --version regex: '(?\d+\.\d+\.\d+)' - + msbuild: command: msbuild -version regex: '(?\d+\.\d+\.\d+\.\d+)' @@ -640,7 +644,7 @@ tools: gcc: command: gcc --version regex: 'gcc.*?(?\d+\.\d+\.\d+)' - + clang: command: clang --version regex: 'clang version (?\d+\.\d+\.\d+)' @@ -656,11 +660,11 @@ tools: docker: command: docker --version regex: 'Docker version (?\d+\.\d+\.\d+)' - + kubectl: command: kubectl version --client --short regex: 'v(?\d+\.\d+\.\d+)' - + terraform: command: terraform version regex: 'Terraform v(?\d+\.\d+\.\d+)' @@ -682,7 +686,7 @@ value so the inner double quotes are preserved: ```yaml # WRONG — spaces in path cause the shell to split the executable name -command-win: '%LLVM_PATH%\bin\clang-format --version' +command-win: '"%LLVM_PATH%\bin\clang-format --version' # CORRECT — double-quote the executable path command-win: '"%LLVM_PATH%\bin\clang-format" --version' @@ -694,64 +698,63 @@ See [Commands with Spaces in Paths](#commands-with-spaces-in-paths) for more exa If a tool command fails because the tool is not installed: -- VersionMark aborts capture immediately and reports an error to stderr -- No JSON output file is created for that job -- Install the missing tool (or remove it from configuration), then re-run capture +- VersionMark aborts capture immediately and reports an error to stderr. +- No JSON output file is created for that job. +- Install the missing tool or remove it from the configuration, then rerun capture. ## Version Not Matched -If the regex doesn't match the command output: +If the regex does not match the command output: -- VersionMark aborts capture immediately and reports an error to stderr -- No JSON output file is created for that job -- Check the actual output of the command manually -- Adjust the regex to match the format -- Use online regex testers to validate your pattern -- Remember to escape special regex characters +- VersionMark aborts capture immediately and reports an error to stderr. +- No JSON output file is created for that job. +- Check the actual output of the command manually. +- Adjust the regex to match the format. +- Use online regex testers to validate your pattern. +- Remember to escape special regex characters. ## OS-Specific Issues If a tool behaves differently on different platforms: -- Use the OS-specific overrides to provide platform-specific commands -- Test on each platform to ensure the commands work -- Consider using platform-specific tools in separate configurations +- Use OS-specific overrides to provide platform-specific commands. +- Test on each platform to ensure the commands work. +- Consider using platform-specific tools in separate configurations. ## No JSON Files Found If the publish command reports "No JSON files found": -- Check that the glob patterns match your JSON file names -- Verify JSON files are in the current directory (or use full/relative paths in patterns) -- Use `-- versionmark-*.json` explicitly if files don't match the default pattern -- Check that capture jobs successfully created JSON files before publishing +- Check that the glob patterns match your JSON file names. +- Verify that JSON files are in the current directory, or use full or relative paths in patterns. +- Use `-- versionmark-*.json` explicitly if files do not match the default pattern. +- Check that capture jobs successfully created JSON files before publishing. ## Invalid JSON Files If a JSON file cannot be parsed during publish: -- Ensure the file was created by the capture command (not manually edited) -- Check that the file is valid JSON format -- Verify the file contains required fields: `JobId` and `Versions` -- Re-run the capture command if the file is corrupted +- Ensure the file was created by the capture command rather than manually edited. +- Check that the file is valid JSON format. +- Verify that the file contains the required `JobId` and `Versions` fields. +- Rerun the capture command if the file is corrupted. # Best Practices -1. **Use Descriptive Job IDs**: Make job-ids descriptive (e.g., `windows-net8-release` -instead of `job1`) -2. **Version Control Config**: Commit `.versionmark.yaml` to version control -3. **Automate Publishing**: Integrate publishing into your CI/CD pipeline -4. **Regular Updates**: Update captured versions regularly to track changes -5. **Document Changes**: Review version changes in pull requests -6. **Test Locally**: Test capture commands locally before adding to CI/CD -7. **Consistent Naming**: Use consistent naming for JSON files across jobs (default pattern -works well) -8. **Heading Depth**: Choose `--report-depth` based on where the report will be included -(e.g., depth 3 for subsections) +1. **Use Descriptive Job IDs**: Make job IDs descriptive, for example `windows-net8-release` + instead of `job1`. +2. **Version Control Config**: Commit `.versionmark.yaml` to version control. +3. **Automate Publishing**: Integrate publishing into your CI/CD pipeline. +4. **Regular Updates**: Update captured versions regularly to track changes. +5. **Document Changes**: Review version changes in pull requests. +6. **Test Locally**: Test capture commands locally before adding them to CI/CD. +7. **Consistent Naming**: Use consistent naming for JSON files across jobs; the default pattern + works well. +8. **Heading Depth**: Choose `--report-depth` based on where the report will be included, for + example depth 3 for subsections. 9. **Artifact Management**: In CI/CD, upload JSON files as artifacts and download them before -publishing -10. **Review Generated Reports**: Check the generated markdown to ensure version information -is accurate + publishing. +10. **Review Generated Reports**: Check the generated Markdown to ensure version information is + accurate. - [continuous-compliance]: https://github.com/demaconsulting/ContinuousCompliance diff --git a/docs/user_guide/title.txt b/docs/user_guide/title.txt index 53d9187..ef32ccd 100644 --- a/docs/user_guide/title.txt +++ b/docs/user_guide/title.txt @@ -1,13 +1,13 @@ --- -title: VersionMark User Guide -subtitle: User Guide -author: DEMA Consulting -description: User guide for the VersionMark .NET tool for capturing and publishing tool version information +title: "VersionMark User Guide" +subtitle: "A .NET tool for CI/CD tool version tracking" +author: "DEMA Consulting" +description: "User Guide for VersionMark" lang: en-US keywords: + - User Guide - VersionMark - .NET - Command-Line Tool - - Documentation - Version Capture ---- +--- \ No newline at end of file diff --git a/docs/verification/introduction.md b/docs/verification/introduction.md index f0eb164..e8e9924 100644 --- a/docs/verification/introduction.md +++ b/docs/verification/introduction.md @@ -16,26 +16,31 @@ The purpose of this document is to: ## Scope -This document covers verification of five in-house subsystems within the VersionMark system: +This document covers verification of six in-house subsystems within the VersionMark system: - The **Cli Subsystem**: argument parsing and program dispatch via `Program` and `Context` - The **Configuration Subsystem**: YAML configuration loading and validation via `VersionMarkConfig`, `ToolConfig`, and `LintIssue` - The **Capture Subsystem**: tool version capture and JSON serialization via `VersionInfo` - The **Publishing Subsystem**: markdown report generation via `MarkdownFormatter` -- The **SelfTest Subsystem**: built-in self-validation via `Validation` and `PathHelpers` +- The **SelfTest Subsystem**: built-in self-validation via `Validation` +- The **Utilities Subsystem**: glob-pattern file matching and safe path combination via + `GlobMatcher` and `PathHelpers` It also covers verification evidence for the following Off-The-Shelf (OTS) components: - **BuildMark** - build notes generation tool +- **DemaConsulting.TestResults** - TRX/JUnit result serialization library - **FileAssert** - file content assertion tool -- **xUnit** - unit testing framework +- **Microsoft.Extensions.FileSystemGlobbing** - glob-pattern file matching library - **Pandoc** - document conversion tool - **ReqStream** - requirements traceability tool - **ReviewMark** - code review enforcement tool - **SarifMark** - SARIF report tool - **SonarMark** - SonarCloud report tool - **WeasyPrint** - HTML-to-PDF conversion tool +- **xUnit** - unit testing framework +- **YamlDotNet** - YAML parsing library This document does not cover installation, end-user usage patterns, or CI/CD pipeline configuration. Those topics are addressed in the VersionMark User Guide and the @@ -59,8 +64,10 @@ VersionMark (System) │ └── VersionInfo (Unit) ├── Publishing (Subsystem) │ └── MarkdownFormatter (Unit) -└── SelfTest (Subsystem) - ├── Validation (Unit) +├── SelfTest (Subsystem) +│ └── Validation (Unit) +└── Utilities (Subsystem) + ├── GlobMatcher (Unit) └── PathHelpers (Unit) ``` @@ -68,23 +75,31 @@ VersionMark (System) In-house items have parallel artifacts in the following locations: -- Requirements: `docs/reqstream/version-mark.yaml`, - `docs/reqstream/version-mark/{subsystem}/{item}.yaml` -- Design: `docs/design/version-mark.md`, - `docs/design/version-mark/{subsystem}/{item}.md` -- Verification: `docs/verification/version-mark.md`, - `docs/verification/version-mark/{subsystem}/{item}.md` -- Source: `src/DemaConsulting.VersionMark/{Subsystem}/{Unit}.cs` -- Tests: `test/DemaConsulting.VersionMark.Tests/{Subsystem}/{Unit}Tests.cs` +- Requirements: `docs/reqstream/version-mark.yaml` for the system, with subsystem and unit + requirements in paths such as `docs/reqstream/version-mark/cli.yaml` and + `docs/reqstream/version-mark/cli/context.yaml` +- Design: `docs/design/version-mark.md` for the system, with subsystem and unit design + documents in paths such as `docs/design/version-mark/cli.md` and + `docs/design/version-mark/cli/context.md` +- Verification: `docs/verification/version-mark.md` for the system, with subsystem and unit + verification documents in paths such as `docs/verification/version-mark/cli.md` and + `docs/verification/version-mark/cli/context.md` +- Source: implementation files under `src/DemaConsulting.VersionMark/`, such as + `src/DemaConsulting.VersionMark/Program.cs`, + `src/DemaConsulting.VersionMark/Configuration/VersionMarkConfig.cs`, and + `src/DemaConsulting.VersionMark/Utilities/PathHelpers.cs` +- Tests: verification evidence under `test/DemaConsulting.VersionMark.Tests/`, such as + `test/DemaConsulting.VersionMark.Tests/ProgramTests.cs`, + `test/DemaConsulting.VersionMark.Tests/Configuration/VersionMarkConfigTests.cs`, and + `test/DemaConsulting.VersionMark.Tests/Utilities/PathHelpersTests.cs` OTS items (no design documentation) have artifacts in these locations: -- Requirements: `docs/reqstream/ots/{ots-name}.yaml` -- Verification: `docs/verification/ots/{ots-name}.md` +- Requirements: `docs/reqstream/ots/`, for example `docs/reqstream/ots/buildmark.yaml` +- Verification: `docs/verification/ots/`, for example `docs/verification/ots/buildmark.md` Review-sets are defined in `.reviewmark.yaml`. ## References -- [REF-1] VersionMark Software Design Document, DEMA Consulting -- [REF-2] VersionMark Requirements Document, DEMA Consulting +- [VersionMark releases](https://github.com/demaconsulting/VersionMark/releases) diff --git a/docs/verification/ots.md b/docs/verification/ots.md index 92b91ad..40d5c06 100644 --- a/docs/verification/ots.md +++ b/docs/verification/ots.md @@ -1,25 +1,83 @@ -# Off-The-Shelf Component Verification +# OTS Verification -## Overview - -This section documents the verification evidence for each Off-The-Shelf (OTS) component -used by VersionMark. OTS components are third-party tools and libraries that are not -developed in-house. Their verification relies on the vendor's own quality assurance and, -where available, self-validation mechanisms provided by the tool itself. +VersionMark uses several Off-The-Shelf (OTS) components. This section documents the +verification evidence for each OTS component. OTS components are third-party tools and +libraries not developed in-house. Their verification relies on the vendor's own quality +assurance and, where available, self-validation mechanisms provided by the tool itself. For OTS tools that include a `--validate` flag, VersionMark's CI pipeline runs self-validation and captures the results as TRX artifacts. For tools without a built-in self-validation mode, verification is based on functional evidence produced during the build and document generation pipeline. -The following OTS components are covered in this section: - -- **BuildMark** - build notes generation tool -- **FileAssert** - file content assertion tool -- **xUnit** - unit testing framework -- **Pandoc** - document conversion tool -- **ReqStream** - requirements traceability tool -- **ReviewMark** - code review enforcement tool -- **SarifMark** - SARIF report tool -- **SonarMark** - SonarCloud report tool -- **WeasyPrint** - HTML-to-PDF conversion tool +## Verification Strategy + +OTS components are divided into two verification categories based on whether the tool +provides a built-in self-validation mode. + +Tools that expose a `--validate` flag are verified by invoking that flag directly in the +CI pipeline. The following tools fall into this category: BuildMark, FileAssert, ReqStream, +ReviewMark, SarifMark, and SonarMark. Each tool's self-validation exercises its own +internal logic and reports results in TRX format. xUnit is treated similarly: the +`dotnet test` run that executes the VersionMark test suite simultaneously validates that +xUnit itself is operating correctly. + +Tools without a built-in self-validation mode are verified through functional evidence +produced during the CI document generation pipeline. Pandoc and WeasyPrint fall into this +category. The CI pipeline generates seven document types — requirements report, design +document, verification document, user guide, build notes, code quality report, and code +review report — and FileAssert asserts the content of each generated document. Successful +FileAssert assertions confirm that Pandoc and WeasyPrint performed their respective +conversion steps correctly. + +DemaConsulting.TestResults is verified through the SelfTest subsystem integration tests +that exercise TRX and JUnit serialization. Microsoft.Extensions.FileSystemGlobbing is +verified through the GlobMatcher unit tests that exercise glob-pattern file matching. +YamlDotNet is verified through the Configuration subsystem unit and integration tests that +exercise YAML parsing. + +## Qualification Evidence + +The following evidence artifacts are collected during each CI run: + +For tools with built-in self-validation (BuildMark, FileAssert, ReqStream, ReviewMark, +SarifMark, SonarMark), the CI pipeline captures TRX result files stored as pipeline +artifacts under the path `artifacts/{tool}-self-validation.trx`, where `{tool}` is the +lowercase tool name (for example, `artifacts/buildmark-self-validation.trx`). + +For xUnit, evidence is provided by the TRX files produced by `dotnet test` across the +supported platform matrix (Windows, Linux, macOS × .NET 8, .NET 9, .NET 10), stored at +`artifacts/validation-{os}-{dotnet}.trx`. + +For Pandoc and WeasyPrint, evidence is provided by the FileAssert TRX results that assert +the content of each of the seven generated document types. These TRX files are captured +alongside the other self-validation artifacts and confirm that document conversion +completed successfully and produced output matching the expected structure and content. + +For DemaConsulting.TestResults, evidence is provided by the SelfTest integration tests +that produce and assert TRX and JUnit XML files. For Microsoft.Extensions.FileSystemGlobbing, +evidence is provided by the GlobMatcher unit tests. For YamlDotNet, evidence is provided +by the Configuration subsystem unit and integration tests. + +## Regression Approach + +When an OTS component is upgraded to a new version, the full CI pipeline is re-run against +that version before the upgrade is accepted. + +For tools with built-in self-validation (BuildMark, FileAssert, ReqStream, ReviewMark, +SarifMark, SonarMark, xUnit), the corresponding self-validation TRX must complete with +zero failures. Any failure in the TRX output blocks the upgrade until the root cause is +resolved. + +For Pandoc and WeasyPrint, all FileAssert assertions on the seven generated document types +must pass. A failure in any FileAssert assertion indicates that a conversion step produced +unexpected output and must be investigated before the upgrade proceeds. + +For DemaConsulting.TestResults, Microsoft.Extensions.FileSystemGlobbing, and YamlDotNet, +the relevant subsystem and unit tests must pass with zero failures across all supported +platform matrix combinations. + +In addition to executing the pipeline, the release notes for the new version are reviewed +to identify any changes in behavior that may affect the features used by VersionMark or +its document generation workflow. Behavioral changes that affect used features are assessed +and, where necessary, addressed before the upgrade is approved. diff --git a/docs/verification/ots/buildmark.md b/docs/verification/ots/buildmark.md index 915d739..f93fac2 100644 --- a/docs/verification/ots/buildmark.md +++ b/docs/verification/ots/buildmark.md @@ -1,23 +1,41 @@ -## BuildMark Verification +## BuildMark -### Overview +### Verification Approach -BuildMark is an OTS tool developed by DEMA Consulting that generates build notes -documenting which versions of tools were used during a build. VersionMark uses BuildMark -in its CI/CD pipeline to capture and publish build notes as part of the compliance -evidence package. +BuildMark is an OTS tool developed by DEMA Consulting that captures GitHub Actions +workflow run metadata (run number, commit SHA, workflow name, and related details) and +renders it as a markdown build-notes document. VersionMark uses BuildMark in its CI/CD +pipeline to capture and publish build notes as part of the compliance evidence package. -### Verification Approach +BuildMark is verified through two mechanisms. First, the CI pipeline runs +`dotnet buildmark --validate --results artifacts/buildmark-self-validation.trx`, which +executes BuildMark's internal test suite and writes results to a TRX file. Second, the CI +pipeline runs BuildMark to generate `docs/build_notes/generated/build_notes.md` from +GitHub Actions workflow metadata. A passing CI run producing this file confirms BuildMark +is operating correctly in the CI environment. + +### Test Environment + +N/A — BuildMark is an OTS tool verified through the GitHub Actions CI pipeline. No +additional test environment configuration is required beyond a successful CI workflow run +with the BuildMark tool installed. + +### Acceptance Criteria -BuildMark is verified through its built-in self-validation mechanism. The CI pipeline -runs `dotnet buildmark --validate --results artifacts/buildmark-self-validation.trx`, -which executes BuildMark's internal test suite and writes results to a TRX file. The -presence of a passing TRX file serves as evidence that BuildMark is functioning correctly -in the CI environment. +The self-validation TRX (`artifacts/buildmark-self-validation.trx`) must be produced and +contain zero failed tests. The generated build notes document +(`docs/build_notes/generated/build_notes.md`) must be produced and incorporated into the +build notes document collection. -### Requirements Coverage +### Test Scenarios -The following list maps BuildMark requirements to verification evidence: +**BuildMarkSelfValidation**: The CI pipeline runs +`dotnet buildmark --validate --results artifacts/buildmark-self-validation.trx`, which +executes BuildMark's internal test suite. The TRX file must be produced and contain no +failed tests. This scenario is verified by `artifacts/buildmark-self-validation.trx`. -- **`VersionMark-OTS-BuildMark`**: `artifacts/buildmark-self-validation.trx` - (BuildMark self-validation passing in CI) +**BuildMark_MarkdownReportGeneration**: The CI pipeline runs BuildMark to generate +`docs/build_notes/generated/build_notes.md` from GitHub Actions workflow metadata. The +markdown build notes document must be produced and incorporated into the build notes +document collection. This scenario is verified by +`docs/build_notes/generated/build_notes.md`. diff --git a/docs/verification/ots/dema-consulting-test-results.md b/docs/verification/ots/dema-consulting-test-results.md new file mode 100644 index 0000000..1142cd1 --- /dev/null +++ b/docs/verification/ots/dema-consulting-test-results.md @@ -0,0 +1,30 @@ +## DemaConsulting.TestResults + +### Verification Approach + +`DemaConsulting.TestResults` is an OTS library developed by DEMA Consulting that provides +`TestResults`, `TrxSerializer`, and `JUnitSerializer` types. The SelfTest subsystem uses +this library to collect in-process test results and serialize them to TRX (`.trx`) or +JUnit XML (`.xml`) format for downstream traceability tooling. + +`DemaConsulting.TestResults` is verified through the SelfTest subsystem integration tests +and the built-in `--validate` mode. Tests that exercise the `--results ` flag cause +`Validation.WriteResultsFile` to call `TrxSerializer.Serialize`, producing a real TRX +output file. Tests that exercise the `--results-xml ` path call +`JUnitSerializer.Serialize`, producing a real JUnit XML file. Both files are asserted for +existence and correct content by the integration tests. Correct serialization of a +`TestResults` collection into a well-formed TRX or JUnit XML file confirms the library is +functioning correctly. + +### Test Scenarios + +**TestResults_TrxSerialization**: The SelfTest integration test invokes +`--validate --results .trx`, which serializes the internal test results to a TRX +file. The resulting TRX file must be produced and its content must assert correct +structure. This scenario is verified by `SelfTest_Run_WithResultsFlag_WritesResultsFile`. + +**TestResults_JUnitSerialization**: The SelfTest integration test invokes +`--validate --results-xml .xml`, which serializes the internal test results to a +JUnit XML file. The resulting JUnit XML file must be produced and asserted for correct +structure. This scenario is verified by +`SelfTest_Run_WithResultsXmlFlag_WritesJUnitResultsFile`. diff --git a/docs/verification/ots/fileassert.md b/docs/verification/ots/fileassert.md index 2550828..9bd6d9a 100644 --- a/docs/verification/ots/fileassert.md +++ b/docs/verification/ots/fileassert.md @@ -1,28 +1,54 @@ -## FileAssert Verification +## FileAssert -### Overview +### Verification Approach FileAssert is an OTS tool developed by DEMA Consulting that asserts the content of generated files against expected patterns. VersionMark uses FileAssert in its CI/CD pipeline to verify that generated documents (HTML, PDF, and other output types) are produced correctly and contain the expected content. -### Verification Approach - -FileAssert is verified through two mechanisms: - -1. **Self-validation**: The CI pipeline runs - `dotnet fileassert --validate --results artifacts/fileassert-self-validation.trx`, - which executes FileAssert's internal test suite and writes results to a TRX file. - -2. **Functional evidence**: FileAssert is exercised in CI by asserting the content - of each of the seven generated document types (requirements report, design document, - verification document, and others). A passing CI run with FileAssert assertions - provides functional evidence that FileAssert is operating correctly. - -### Requirements Coverage - -The following list maps FileAssert requirements to verification evidence: - -- **`VersionMark-OTS-FileAssert`**: `artifacts/fileassert-self-validation.trx` - (FileAssert self-validation passing in CI) and FileAssert assertions on generated documents +FileAssert is verified through two mechanisms. First, the CI pipeline runs +`dotnet fileassert --validate --results artifacts/fileassert-self-validation.trx`, which +executes FileAssert's internal test suite and writes results to a TRX file. Second, +FileAssert is exercised in CI by asserting the content of each of the seven generated +document types (build notes, code quality report, code review plan, code review report, +design document, verification document, and user guide). A passing CI run with all +FileAssert assertions provides functional evidence that FileAssert is operating correctly. + +### Test Environment + +N/A — FileAssert is an OTS tool verified through the GitHub Actions CI pipeline. No +additional test environment configuration is required beyond a successful CI workflow run +with the FileAssert tool installed and the document artifacts produced by Pandoc and +WeasyPrint. + +### Acceptance Criteria + +The self-validation TRX (`artifacts/fileassert-self-validation.trx`) must be produced and +contain zero failed tests. All seven document-assertion TRX files +(`artifacts/fileassert-build-notes.trx`, `artifacts/fileassert-code-quality.trx`, +`artifacts/fileassert-code-review.trx`, `artifacts/fileassert-design.trx`, +`artifacts/fileassert-verification.trx`, `artifacts/fileassert-user-guide.trx`, and +`artifacts/fileassert-requirements.trx`) must be produced and contain zero failed tests. + +### Test Scenarios + +**FileAssertSelfValidation**: The CI pipeline runs +`dotnet fileassert --validate --results artifacts/fileassert-self-validation.trx`, which +executes FileAssert's internal test suite. The TRX file must be produced and contain no +failed tests. This scenario is verified by `artifacts/fileassert-self-validation.trx`. +This scenario corresponds to test IDs `FileAssert_VersionDisplay` and +`FileAssert_HelpDisplay` in the requirements traceability matrix. + +**FileAssertFunctionalEvidence**: The CI pipeline runs FileAssert assertions on each of +the seven generated document types (HTML and PDF). All assertions must pass, confirming +that FileAssert is evaluating file content correctly in the CI environment. This scenario +is verified by `artifacts/fileassert-build-notes.trx` (tests `Pandoc_BuildNotesHtml` and +`WeasyPrint_BuildNotesPdf`), `artifacts/fileassert-code-quality.trx` (tests +`Pandoc_CodeQualityHtml` and `WeasyPrint_CodeQualityPdf`), +`artifacts/fileassert-code-review.trx` (tests `Pandoc_ReviewPlanHtml`, +`WeasyPrint_ReviewPlanPdf`, `Pandoc_ReviewReportHtml`, and `WeasyPrint_ReviewReportPdf`), +`artifacts/fileassert-design.trx` (tests `Pandoc_DesignHtml` and `WeasyPrint_DesignPdf`), +`artifacts/fileassert-verification.trx` (tests `Pandoc_VerificationHtml` and +`WeasyPrint_VerificationPdf`), and `artifacts/fileassert-user-guide.trx` (tests +`Pandoc_UserGuideHtml` and `WeasyPrint_UserGuidePdf`). diff --git a/docs/verification/ots/microsoft-extensions-file-system-globbing.md b/docs/verification/ots/microsoft-extensions-file-system-globbing.md new file mode 100644 index 0000000..3746238 --- /dev/null +++ b/docs/verification/ots/microsoft-extensions-file-system-globbing.md @@ -0,0 +1,30 @@ +## Microsoft.Extensions.FileSystemGlobbing + +### Verification Approach + +`Microsoft.Extensions.FileSystemGlobbing` is an OTS library from Microsoft that provides +the `Matcher` class for glob-pattern file matching. The Utilities subsystem uses it inside +`GlobMatcher.FindMatchingFiles` to evaluate both relative and absolute glob patterns +against the file system and return the matching file paths. + +`Microsoft.Extensions.FileSystemGlobbing` is verified through the GlobMatcher unit tests. +Tests supply known directory structures and glob patterns to `GlobMatcher.FindMatchingFiles` +and assert on the returned file list. Scenarios cover relative patterns, absolute patterns, +overlapping patterns (deduplication), and patterns that match no files. Successful +execution of these tests across all supported OS and .NET version matrix combinations +confirms that the `Matcher` class is evaluating patterns correctly in each environment. + +### Test Scenarios + +**FileSystemGlobbing_MatchesRelativePattern**: `GlobMatcher.FindMatchingFiles` with a +relative glob pattern returns the expected matching files from the current directory. This +scenario is verified by `GlobMatcher_FindMatchingFiles_RelativePattern_ReturnsMatchingFiles`. + +**FileSystemGlobbing_MatchesAbsolutePattern**: `GlobMatcher.FindMatchingFiles` with an +absolute glob pattern returns the expected matching files from the rooted directory. This +scenario is verified by `GlobMatcher_FindMatchingFiles_AbsolutePattern_ReturnsMatchingFiles`. + +**FileSystemGlobbing_DeduplicatesOverlappingPatterns**: Two overlapping glob patterns +supplied to `GlobMatcher.FindMatchingFiles` return each matching file only once in the +sorted result. This scenario is verified by +`GlobMatcher_FindMatchingFiles_OverlappingPatterns_DeduplicatesResults`. diff --git a/docs/verification/ots/pandoc.md b/docs/verification/ots/pandoc.md index 450eab7..8257349 100644 --- a/docs/verification/ots/pandoc.md +++ b/docs/verification/ots/pandoc.md @@ -1,22 +1,58 @@ -## Pandoc Verification +## Pandoc -### Overview +### Verification Approach Pandoc is an OTS document conversion tool used in the VersionMark CI/CD pipeline to compile the multiple markdown input files for each document collection into a single HTML output file. It processes `definition.yaml` files to determine input ordering, template, and table-of-contents settings. -### Verification Approach - Pandoc is verified through functional evidence. The CI pipeline generates seven document -types (requirements report, design document, verification document, user guide, build -notes, code quality report, and code review report) using Pandoc. FileAssert then asserts +types (build notes, code quality report, code review plan, code review report, design +document, verification document, and user guide) using Pandoc. FileAssert then asserts the content of each generated document. A passing CI run with all FileAssert assertions provides evidence that Pandoc is converting documents correctly. -### Requirements Coverage +### Test Scenarios + +**PandocGeneratesDesignDocument**: The CI pipeline runs Pandoc on +`docs/design/definition.yaml` to generate the design HTML document. FileAssert verifies +the resulting HTML contains the expected content. This scenario is verified by the +FileAssert TRX results for the design document. + +**PandocGeneratesVerificationDocument**: The CI pipeline runs Pandoc on +`docs/verification/definition.yaml` to generate the verification HTML document. FileAssert +verifies the resulting HTML contains the expected content. This scenario is verified by +the FileAssert TRX results for the verification document. + +**PandocGeneratesUserGuide**: The CI pipeline runs Pandoc on `docs/user_guide/definition.yaml` +to generate the user guide HTML document. FileAssert verifies the resulting HTML contains +the expected content. This scenario is verified by the FileAssert TRX results for the user +guide. + +**PandocGeneratesBuildNotes**: The CI pipeline runs Pandoc on +`docs/build_notes/definition.yaml` to generate the build notes HTML document. FileAssert +verifies the resulting HTML. This scenario is verified by the FileAssert TRX results for +the build notes. + +**PandocGeneratesCodeQualityReport**: The CI pipeline runs Pandoc on +`docs/code_quality/definition.yaml` to generate the code quality HTML document. FileAssert +verifies the resulting HTML. This scenario is verified by the FileAssert TRX results for +the code quality report. + +**PandocGeneratesCodeReviewPlan**: The CI pipeline runs Pandoc on +`docs/code_review_plan/definition.yaml` to generate the code review plan HTML document. +FileAssert verifies the resulting HTML. This scenario is verified by the FileAssert TRX +results for the code review plan. -The following list maps Pandoc requirements to verification evidence: +**PandocGeneratesCodeReviewReport**: The CI pipeline runs Pandoc on +`docs/code_review_report/definition.yaml` to generate the code review HTML document. +FileAssert verifies the resulting HTML. This scenario is verified by the FileAssert TRX +results for the code review report. -- **`VersionMark-OTS-Pandoc`**: FileAssert TRX evidence covering seven generated document types in CI +**PandocGeneratesRequirementsReport** *(supplementary, non-OTS-evidence)*: The CI pipeline +runs Pandoc on `docs/requirements_doc/definition.yaml` to generate the requirements HTML +document. This scenario runs after ReqStream and the resulting HTML document is validated +by FileAssert. It is recorded here for completeness but does not contribute to OTS +qualification evidence because the requirements tests are explicitly excluded from OTS +evidence scope. diff --git a/docs/verification/ots/reqstream.md b/docs/verification/ots/reqstream.md index 5f84bfa..bfb1f2c 100644 --- a/docs/verification/ots/reqstream.md +++ b/docs/verification/ots/reqstream.md @@ -1,23 +1,29 @@ -## ReqStream Verification +## ReqStream -### Overview +### Verification Approach ReqStream is an OTS requirements traceability tool developed by DEMA Consulting. It processes requirements YAML files and generates traceability reports showing which requirements are covered by which tests. VersionMark uses ReqStream to generate the requirements traceability report as part of the compliance evidence package. -### Verification Approach - -ReqStream is verified through its built-in self-validation mechanism. The CI pipeline -runs `dotnet reqstream --validate --results artifacts/reqstream-self-validation.trx`, -which executes ReqStream's internal test suite and writes results to a TRX file. A -passing TRX file serves as evidence that ReqStream is functioning correctly in the CI -environment. +ReqStream is verified through two mechanisms. First, the CI pipeline runs +`dotnet reqstream --validate --results artifacts/reqstream-self-validation.trx`, which +executes ReqStream's internal test suite and writes results to a TRX file. Second, the CI +pipeline runs ReqStream with the `--enforce` flag against the VersionMark requirements +YAML files and the TRX test results produced by `dotnet test`. A non-zero exit code from +ReqStream causes the pipeline to fail, meaning a passing CI run proves every requirement +in the project is linked to at least one passing test — confirming ReqStream is enforcing +requirements coverage correctly. -### Requirements Coverage +### Test Scenarios -The following list maps ReqStream requirements to verification evidence: +**ReqStreamSelfValidation**: The CI pipeline runs +`dotnet reqstream --validate --results artifacts/reqstream-self-validation.trx`, which +executes ReqStream's internal test suite. The TRX file must be produced and contain no +failed tests. This scenario is verified by `artifacts/reqstream-self-validation.trx`. -- **`VersionMark-OTS-ReqStream`**: `artifacts/reqstream-self-validation.trx` - (ReqStream self-validation passing in CI) +**ReqStream_EnforcementMode**: The CI pipeline runs `dotnet reqstream --enforce` with all +VersionMark requirements YAML files and test TRX results. The tool must exit zero, +confirming every requirement has passing test evidence. This scenario is verified by the +passing `--enforce` run in CI. diff --git a/docs/verification/ots/reviewmark.md b/docs/verification/ots/reviewmark.md index 4046209..c87f9be 100644 --- a/docs/verification/ots/reviewmark.md +++ b/docs/verification/ots/reviewmark.md @@ -1,23 +1,46 @@ -## ReviewMark Verification +## ReviewMark -### Overview +### Verification Approach ReviewMark is an OTS code review enforcement tool developed by DEMA Consulting. It tracks the review status of files using a `.reviewmark.yaml` configuration and generates code review reports. VersionMark uses ReviewMark to enforce formal review coverage across all source, documentation, and requirements files. -### Verification Approach +ReviewMark is verified through two mechanisms. First, the CI pipeline runs +`dotnet reviewmark --validate --results artifacts/reviewmark-self-validation.trx`, which +executes ReviewMark's internal test suite and writes results to a TRX file. Second, the CI +pipeline runs ReviewMark against the VersionMark `.reviewmark.yaml` configuration to +generate a review plan markdown document and a review report markdown document. A passing +CI run producing both documents confirms ReviewMark is reading the review configuration +and generating correct output. + +### Test Environment + +N/A — ReviewMark is an OTS tool verified through the GitHub Actions CI pipeline. No +additional test environment configuration is required beyond a successful CI workflow run +with the ReviewMark tool installed. + +### Acceptance Criteria + +The self-validation TRX (`artifacts/reviewmark-self-validation.trx`) must be produced and +contain zero failed tests. The generated review plan (`docs/code_review_plan/generated/plan.md`) +and review report (`docs/code_review_report/generated/report.md`) must be produced and +incorporated into the code review document collection. -ReviewMark is verified through its built-in self-validation mechanism. The CI pipeline -runs `dotnet reviewmark --validate --results artifacts/reviewmark-self-validation.trx`, -which executes ReviewMark's internal test suite and writes results to a TRX file. A -passing TRX file serves as evidence that ReviewMark is functioning correctly in the CI -environment. +### Test Scenarios -### Requirements Coverage +**ReviewMarkSelfValidation**: The CI pipeline runs +`dotnet reviewmark --validate --results artifacts/reviewmark-self-validation.trx`, which +executes ReviewMark's internal test suite. The TRX file must be produced and contain no +failed tests. This scenario is verified by `artifacts/reviewmark-self-validation.trx`. -The following list maps ReviewMark requirements to verification evidence: +**ReviewMark_ReviewPlanGeneration**: The CI pipeline runs ReviewMark to generate +`docs/code_review_plan/generated/plan.md` from the `.reviewmark.yaml` configuration. The +review plan document must be produced listing all files with their review status. This +scenario is verified by `docs/code_review_plan/generated/plan.md`. -- **`VersionMark-OTS-ReviewMark`**: `artifacts/reviewmark-self-validation.trx` - (ReviewMark self-validation passing in CI) +**ReviewMark_ReviewReportGeneration**: The CI pipeline runs ReviewMark to generate +`docs/code_review_report/generated/report.md`. The review report document must be produced +summarizing review coverage across the repository. This scenario is verified by +`docs/code_review_report/generated/report.md`. diff --git a/docs/verification/ots/sarifmark.md b/docs/verification/ots/sarifmark.md index 7f5cddf..d58ae66 100644 --- a/docs/verification/ots/sarifmark.md +++ b/docs/verification/ots/sarifmark.md @@ -1,23 +1,44 @@ -## SarifMark Verification +## SarifMark -### Overview +### Verification Approach SarifMark is an OTS SARIF report tool developed by DEMA Consulting. It converts SARIF (Static Analysis Results Interchange Format) files produced by code analysis tools into human-readable markdown reports. VersionMark uses SarifMark in its CI/CD pipeline to generate code quality reports from static analysis output. -### Verification Approach +SarifMark is verified through two mechanisms. First, the CI pipeline runs +`dotnet sarifmark --validate --results artifacts/sarifmark-self-validation.trx`, which +executes SarifMark's internal test suite and writes results to a TRX file. Second, the CI +pipeline passes the SARIF file produced by the CodeQL code scanning step to SarifMark, +which generates `docs/code_quality/generated/quality.md`. A passing CI run producing this +report confirms SarifMark is reading SARIF correctly and generating markdown output. + +### Test Environment + +N/A — SarifMark is an OTS tool verified through the GitHub Actions CI pipeline. No +additional test environment configuration is required beyond a successful CI workflow run +with the SarifMark tool installed. + +### Acceptance Criteria + +The self-validation TRX (`artifacts/sarifmark-self-validation.trx`) must be produced and +contain zero failed tests. The generated quality report +(`docs/code_quality/generated/quality.md`) must be produced and incorporated into the +code quality document collection. -SarifMark is verified through its built-in self-validation mechanism. The CI pipeline -runs `dotnet sarifmark --validate --results artifacts/sarifmark-self-validation.trx`, -which executes SarifMark's internal test suite and writes results to a TRX file. A -passing TRX file serves as evidence that SarifMark is functioning correctly in the CI -environment. +### Test Scenarios -### Requirements Coverage +**SarifMarkSelfValidation**: The CI pipeline runs +`dotnet sarifmark --validate --results artifacts/sarifmark-self-validation.trx`, which +executes SarifMark's internal test suite. The TRX file must be produced and contain no +failed tests. This scenario is verified by `artifacts/sarifmark-self-validation.trx`. -The following list maps SarifMark requirements to verification evidence: +**SarifMark_SarifReading**: The CI pipeline runs SarifMark against the CodeQL SARIF output +file. The tool must read the SARIF without error and extract all findings. This scenario +is verified by the successful generation of `docs/code_quality/generated/quality.md`. -- **`VersionMark-OTS-SarifMark`**: `artifacts/sarifmark-self-validation.trx` - (SarifMark self-validation passing in CI) +**SarifMark_MarkdownReportGeneration**: The CI pipeline runs SarifMark to generate +`docs/code_quality/generated/quality.md` from the CodeQL SARIF file. The markdown quality +report must be produced and incorporated into the code quality document collection. This +scenario is verified by `docs/code_quality/generated/quality.md`. diff --git a/docs/verification/ots/sonarmark.md b/docs/verification/ots/sonarmark.md index 237dd23..1a72915 100644 --- a/docs/verification/ots/sonarmark.md +++ b/docs/verification/ots/sonarmark.md @@ -1,23 +1,57 @@ -## SonarMark Verification +## SonarMark -### Overview +### Verification Approach SonarMark is an OTS SonarCloud report tool developed by DEMA Consulting. It retrieves code quality metrics from SonarCloud and generates summary reports. VersionMark uses SonarMark in its CI/CD pipeline to include SonarCloud quality gate results in the compliance evidence package. -### Verification Approach +SonarMark is verified through two mechanisms. First, the CI pipeline runs +`dotnet sonarmark --validate --results artifacts/sonarmark-self-validation.trx`, which +executes SonarMark's internal test suite and writes results to a TRX file. Second, the CI +pipeline runs SonarMark to retrieve quality gate status, open issues, and security +hotspot data from the SonarCloud project for VersionMark, then generates +`docs/code_quality/generated/sonarmark.md`. A passing CI run producing this report +confirms SonarMark is communicating with SonarCloud and generating correct markdown +output. + +### Test Environment + +N/A — SonarMark is an OTS tool verified through the GitHub Actions CI pipeline. No +additional test environment configuration is required beyond a successful CI workflow run +with the SonarMark tool installed. + +### Acceptance Criteria + +The self-validation TRX (`artifacts/sonarmark-self-validation.trx`) must be produced and +contain zero failed tests. The generated quality report +(`docs/code_quality/generated/sonarmark.md`) must be produced and incorporated into the +code quality document collection. + +### Test Scenarios + +**SonarMarkSelfValidation**: The CI pipeline runs +`dotnet sonarmark --validate --results artifacts/sonarmark-self-validation.trx`, which +executes SonarMark's internal test suite. The TRX file must be produced and contain no +failed tests. This scenario is verified by `artifacts/sonarmark-self-validation.trx`. -SonarMark is verified through its built-in self-validation mechanism. The CI pipeline -runs `dotnet sonarmark --validate --results artifacts/sonarmark-self-validation.trx`, -which executes SonarMark's internal test suite and writes results to a TRX file. A -passing TRX file serves as evidence that SonarMark is functioning correctly in the CI -environment. +**SonarMark_QualityGateRetrieval**: The CI pipeline runs SonarMark to retrieve the +SonarCloud quality gate status for the VersionMark project. The tool must return the gate +result without error. This scenario is verified by the successful generation of +`docs/code_quality/generated/sonarmark.md`. -### Requirements Coverage +**SonarMark_IssuesRetrieval**: The CI pipeline runs SonarMark to retrieve open issues from +SonarCloud. The tool must return issue count and severity data for inclusion in the quality +report. This scenario is verified by the open issues section of +`docs/code_quality/generated/sonarmark.md`. -The following list maps SonarMark requirements to verification evidence: +**SonarMark_HotSpotsRetrieval**: The CI pipeline runs SonarMark to retrieve security +hotspots from SonarCloud. The tool must return hotspot data for inclusion in the quality +report. This scenario is verified by the security hotspots section of +`docs/code_quality/generated/sonarmark.md`. -- **`VersionMark-OTS-SonarMark`**: `artifacts/sonarmark-self-validation.trx` - (SonarMark self-validation passing in CI) +**SonarMark_MarkdownReportGeneration**: The CI pipeline runs SonarMark to generate +`docs/code_quality/generated/sonarmark.md` from the SonarCloud data. The markdown quality +report must be produced and incorporated into the code quality document collection. This +scenario is verified by `docs/code_quality/generated/sonarmark.md`. diff --git a/docs/verification/ots/weasyprint.md b/docs/verification/ots/weasyprint.md index fa6da61..65f90e1 100644 --- a/docs/verification/ots/weasyprint.md +++ b/docs/verification/ots/weasyprint.md @@ -1,21 +1,47 @@ -## WeasyPrint Verification +## WeasyPrint -### Overview +### Verification Approach WeasyPrint is an OTS HTML-to-PDF conversion tool. VersionMark uses WeasyPrint in its CI/CD pipeline to convert the HTML documents generated by Pandoc into PDF format for distribution and archiving as compliance evidence. -### Verification Approach - WeasyPrint is verified through functional evidence. The CI pipeline generates seven document types using Pandoc followed by WeasyPrint to produce PDF output. FileAssert then asserts the content and structure of each generated document. A passing CI run with all FileAssert assertions provides evidence that WeasyPrint is converting documents correctly. -### Requirements Coverage +### Test Scenarios + +**WeasyPrintGeneratesRequirementsReportPdf** (supplementary, non-OTS-evidence): The CI pipeline runs WeasyPrint to convert +the Pandoc-generated HTML for the requirements report into PDF. FileAssert must verify the +resulting PDF is non-empty and valid. This scenario is verified by the FileAssert TRX +results for the requirements report PDF. + +**WeasyPrintGeneratesDesignDocumentPdf**: The CI pipeline runs WeasyPrint to convert the +design document HTML to PDF. FileAssert must verify the resulting PDF. This scenario is +verified by the FileAssert TRX results for the design document PDF. + +**WeasyPrintGeneratesVerificationDocumentPdf**: The CI pipeline runs WeasyPrint to convert +the verification document HTML to PDF. FileAssert must verify the resulting PDF. This +scenario is verified by the FileAssert TRX results for the verification document PDF. + +**WeasyPrintGeneratesUserGuidePdf**: The CI pipeline runs WeasyPrint to convert the user +guide HTML to PDF. FileAssert must verify the resulting PDF. This scenario is verified by +the FileAssert TRX results for the user guide PDF. + +**WeasyPrintGeneratesBuildNotesPdf**: The CI pipeline runs WeasyPrint to convert the build +notes HTML to PDF. FileAssert must verify the resulting PDF. This scenario is verified by +the FileAssert TRX results for the build notes PDF. + +**WeasyPrintGeneratesCodeQualityReportPdf**: The CI pipeline runs WeasyPrint to convert +the code quality report HTML to PDF. FileAssert must verify the resulting PDF. This +scenario is verified by the FileAssert TRX results for the code quality report PDF. -The following list maps WeasyPrint requirements to verification evidence: +**WeasyPrintGeneratesCodeReviewPlanPdf**: The CI pipeline runs WeasyPrint to convert the +code review plan HTML to PDF. FileAssert must verify the resulting PDF. This scenario is +verified by the FileAssert TRX results for the code review plan PDF. -- **`VersionMark-OTS-WeasyPrint`**: FileAssert TRX evidence covering seven generated PDF - document types in CI +**WeasyPrintGeneratesCodeReviewReportPdf**: The CI pipeline runs WeasyPrint to convert the +code review report HTML to PDF. FileAssert must verify the resulting PDF. This scenario is +verified by the FileAssert TRX results for the code review report PDF. diff --git a/docs/verification/ots/xunit.md b/docs/verification/ots/xunit.md index 140ed9b..1d37804 100644 --- a/docs/verification/ots/xunit.md +++ b/docs/verification/ots/xunit.md @@ -1,13 +1,11 @@ -## xUnit Verification +## xUnit -### Overview +### Verification Approach xUnit is the OTS unit testing framework used by the VersionMark test project `DemaConsulting.VersionMark.Tests`. It provides the test runner, assertion library, and TRX results output used throughout this verification design document. -### Verification Approach - xUnit is verified through execution of the VersionMark test suite. The CI pipeline runs: ```text @@ -19,12 +17,19 @@ dotnet test --no-build --configuration Release This command is executed across multiple operating system and .NET version combinations in the CI matrix (Windows, Linux, macOS against .NET 8, .NET 9, and .NET 10). A passing -test run on each combination provides evidence that xUnit is functioning correctly in -each environment. The resulting TRX files are collected as CI artifacts. +test run on each combination provides evidence that xUnit is functioning correctly in each +environment. The resulting TRX files are collected as CI artifacts. + +### Test Scenarios -### Requirements Coverage +**xUnitRunsTestSuiteOnWindows**: The CI pipeline runs `dotnet test` on Windows across +.NET 8, .NET 9, and .NET 10. TRX result files must be produced with no failed tests. This +scenario is verified by `artifacts/validation-windows-*.trx`. -The following list maps xUnit requirements to verification evidence: +**xUnitRunsTestSuiteOnLinux**: The CI pipeline runs `dotnet test` on Linux across +.NET 8, .NET 9, and .NET 10. TRX result files must be produced with no failed tests. This +scenario is verified by `artifacts/validation-linux-*.trx`. -- **`VersionMark-OTS-xUnit`**: TRX result files from `dotnet test` across all CI matrix - OS/dotnet combinations +**xUnitRunsTestSuiteOnMacOs**: The CI pipeline runs `dotnet test` on macOS across +.NET 8, .NET 9, and .NET 10. TRX result files must be produced with no failed tests. This +scenario is verified by `artifacts/validation-macos-*.trx`. diff --git a/docs/verification/ots/yaml-dot-net.md b/docs/verification/ots/yaml-dot-net.md new file mode 100644 index 0000000..3a38ccb --- /dev/null +++ b/docs/verification/ots/yaml-dot-net.md @@ -0,0 +1,29 @@ +## YamlDotNet + +### Verification Approach + +`YamlDotNet` is an OTS YAML parsing library. The Configuration subsystem uses it to +parse `.versionmark.yaml` files inside `VersionMarkConfig.Load`. All YAML node traversal, +type checking, and source-location extraction performed during configuration loading and +linting depend on this library. + +`YamlDotNet` is verified through the Configuration subsystem unit and integration tests. +Every test that invokes `VersionMarkConfig.Load` or `VersionMarkConfig.ReadFromFile` with +a YAML input string exercises the YamlDotNet parsing path. Tests cover valid YAML, +syntactically invalid YAML (confirmed to produce a parse-error `LintIssue`), and edge +cases such as missing required sections. Successful execution of these tests across all +supported OS and .NET version matrix combinations confirms that YamlDotNet is functioning +correctly in each environment. + +### Test Scenarios + +**YamlDotNet_ParsesValidConfig**: `VersionMarkConfig.Load` with a well-formed YAML file +returns a non-null `Config` and an empty issues list, confirming that YamlDotNet correctly +parses valid YAML input. This scenario is verified by +`VersionMarkConfig_Load_ValidConfig_ReturnsConfig`. + +**YamlDotNet_ReportsParseError**: `VersionMarkConfig.Load` with invalid YAML returns a +null `Config` and an error-level `LintIssue` containing source-location information +derived from the `YamlException` thrown by YamlDotNet, confirming that YamlDotNet +surfaces parse errors correctly. This scenario is verified by +`VersionMarkConfig_Load_InvalidYaml_ReturnsNullConfig`. diff --git a/docs/verification/title.txt b/docs/verification/title.txt index bf3cce9..b7e1643 100644 --- a/docs/verification/title.txt +++ b/docs/verification/title.txt @@ -1,8 +1,8 @@ --- -title: VersionMark Verification Design Document -subtitle: Tool Version Capture and Publication Tool -author: DEMA Consulting -description: Verification design document for VersionMark +title: "VersionMark Verification Design Document" +subtitle: "Tool version tracking for CI/CD environments" +author: "DEMA Consulting" +description: "Verification Design Document for VersionMark" lang: en-US keywords: - VersionMark @@ -10,4 +10,4 @@ keywords: - Command-Line Tool - Verification - Verification Design Document ---- +--- \ No newline at end of file diff --git a/docs/verification/version-mark.md b/docs/verification/version-mark.md index 9b8e703..3744101 100644 --- a/docs/verification/version-mark.md +++ b/docs/verification/version-mark.md @@ -1,25 +1,13 @@ -# VersionMark System Verification - -## Overview - -This section documents the verification design for the VersionMark system. VersionMark is -a .NET global tool that captures tool version information from CI/CD job environments and -publishes consolidated version reports as markdown. - -The verification strategy is organized around six subsystems: - -- **Cli** - command-line argument parsing and program dispatch -- **Configuration** - YAML configuration loading and validation -- **Capture** - tool version capture and JSON serialization -- **Publishing** - markdown report generation -- **SelfTest** - built-in self-validation -- **Utilities** - glob-pattern file matching and safe path combination +# VersionMark ## Verification Approach -Each subsystem is verified through a combination of integration tests (at the subsystem -level) and unit tests (at the unit level). All tests are implemented using xUnit and -are located under `test/DemaConsulting.VersionMark.Tests/`. +VersionMark is a .NET global tool that captures tool version information from CI/CD job +environments and publishes consolidated version reports as markdown. The verification +strategy is organized around six subsystems: Cli, Configuration, Capture, Publishing, +SelfTest, and Utilities. Each subsystem is verified through a combination of integration +tests (at the subsystem level) and unit tests (at the unit level). All tests are +implemented using xUnit and are located under `test/DemaConsulting.VersionMark.Tests/`. Tests are executed using `dotnet test` across multiple operating systems (Windows, Linux, macOS) and multiple .NET versions (8, 9, 10). Each test run produces a TRX results file @@ -28,14 +16,51 @@ which serves as compliance evidence. The built-in `--validate` mode exercises capture, publish, and lint workflows end-to-end and produces a results file that can be used as post-deployment verification evidence. -## System-Level Test Environments +## Test Environment + +System-level verification is performed in the GitHub Actions CI/CD environment. Each +matrix job runs on a specific platform and .NET version combination, producing named TRX +result files. The file naming convention (`artifacts/validation-{os}-{dotnet}.trx`) and +test names provide the platform linkage used by ReqStream filters. + +## Acceptance Criteria + +- All automated tests pass with zero failures across all supported operating system and + .NET version matrix combinations (Windows, Linux, macOS × .NET 8, .NET 9, .NET 10). +- The built-in self-validation (`--validate`) exits with code 0 on each supported platform. +- No unresolved anomalies of severity "error" or above remain open at the time of release. + +## Test Scenarios + +**VersionMark-Validate-Full**: Invoke `--validate --silent` to run all internal self-test +scenarios. This exercises the `VersionMark_CapturesVersions`, +`VersionMark_GeneratesMarkdownReport`, `VersionMark_LintPassesForValidConfig`, and +`VersionMark_LintReportsErrorsForInvalidConfig` scenarios internally and asserts that the +process exits with code 0. A non-zero exit code or any internal scenario failure +constitutes a test failure. This scenario is tested by +`IntegrationTest_ValidateFlag_RunsValidation`. -System-level verification is performed in the GitHub Actions CI/CD environment. Each matrix -job runs on a specific platform and .NET version combination, producing named TRX result -files. The file naming convention (`artifacts/validation-{os}-{dotnet}.trx`) and test names -provide the platform linkage used by ReqStream filters. +**VersionMark-CapturePublishCycle**: Verify capture mode and publish mode independently. +The capture scenario invokes the capture workflow and asserts that a JSON output file is +produced with the expected tool name and version entries. The publish scenario writes known +JSON capture files and invokes the publish workflow, asserting that the markdown report +contains the expected content. This scenario confirms that the Capture and Publishing +subsystems each operate correctly in isolation; end-to-end integration is verified through +the self-validation suite (see VersionMark-Validate-Full). This scenario is tested by +`IntegrationTest_CaptureCommand_CapturesToolVersions`. -## Requirements Coverage Summary +**VersionMark-CLI-ErrorHandling**: Submit an unknown argument, omit a required flag, and +supply a missing configuration file path in separate invocations. Assert that each +invocation produces a non-zero exit code and emits a descriptive error message. This +scenario confirms that the Cli subsystem correctly rejects invalid inputs at the +assembled-system level. This scenario is tested by +`IntegrationTest_UnknownArgument_ReturnsError`, +`IntegrationTest_CaptureCommandWithoutJobId_ReturnsError`, +`IntegrationTest_CaptureCommandWithMissingConfig_ReturnsError`, and +`IntegrationTest_LintFlag_MissingConfig_ReturnsError`. -The subsystem chapters that follow provide detailed test-scenario-to-requirement mappings. -Each requirement at every level is covered by at least one named test scenario. +**VersionMark-MultiPlatform**: Each of the above scenarios (VersionMark-Validate-Full, +VersionMark-CapturePublishCycle, and VersionMark-CLI-ErrorHandling) is repeated on +Windows, Linux, and macOS across .NET 8, .NET 9, and .NET 10. Successful execution on all +matrix combinations confirms that there are no platform-specific regressions in the +assembled system. This scenario is verified by the full CI matrix run. diff --git a/docs/verification/version-mark/capture.md b/docs/verification/version-mark/capture.md index 6b59dfa..532ea26 100644 --- a/docs/verification/version-mark/capture.md +++ b/docs/verification/version-mark/capture.md @@ -1,57 +1,77 @@ -## Capture Subsystem Verification +## Capture -### Overview +### Verification Approach The Capture subsystem is responsible for executing tool version commands, extracting version strings, and serializing the results to JSON. It consists of one unit: -`VersionInfo` (the JSON version data record). +`VersionInfo` (the JSON version data record). Subsystem-level integration tests are in +`Capture/CaptureTests.cs` and cover the full capture workflow including configuration +loading, command execution, output file writing, and loading the saved data. Tests use a +temporary directory containing a `.versionmark.yaml` configuration file. No external API +mocks are required. -Subsystem-level integration tests are in `Capture/CaptureTests.cs` and cover the full -capture workflow including configuration loading, command execution, output file writing, -and loading the saved data. Unit-level verification for `VersionInfo` is in the chapter -that follows. +### Test Environment -### Verification Approach +N/A - standard test environment. Tests create temporary directories and configuration files +during setup and clean them up afterwards. -Integration tests use a temporary directory containing a `.versionmark.yaml` configuration -file. Tests invoke capture operations and assert on the written JSON files and displayed -output. No external API mocks are required. +### Acceptance Criteria + +- All subsystem integration tests pass with zero failures across all supported OS and .NET + version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the Capture subsystem is covered by at least one named test + scenario. ### Test Scenarios -The following integration test scenarios verify Capture subsystem requirements: - -- **`Capture_Context_CaptureFlag_SetsCaptureMode`**: `--capture` sets capture mode in context. -- **`Capture_Context_WithJobId_SetsJobId`**: `--job-id` sets the job ID in context. -- **`Capture_Run_NoOutputFlagSpecified_UsesDefaultFilename`**: Default output filename is derived from job ID. -- **`Capture_Context_WithToolFilter_SetsToolNames`**: Tool filter patterns after `--` are captured. -- **`Capture_Run_NoToolFilter_CapturesAllConfiguredTools`**: No tool filter captures all configured tools. -- **`Capture_Config_ReadFromFile_LoadsToolDefinitions`**: Config file loads all tool definitions. -- **`Capture_FindVersions_ExecutesCommandAndExtractsVersion`**: Command is executed and version extracted via regex. -- **`Capture_Run_DisplaysCapturedVersionsAfterCapture`**: Captured versions are displayed after capture. -- **`Capture_Run_MissingConfig_ReportsError`**: Missing config file reports an error. -- **`Capture_SaveAndLoad_PreservesAllVersionData`**: Save and load cycle preserves all version data. -- **`Capture_MultipleCaptures_EachFileHasDistinctJobId`**: Multiple capture files each have a distinct job ID. - -### Dependencies - -No external mocks are required. Tests use temporary directories and configuration files -created during test setup. - -### Requirements Coverage - -The following list maps Capture subsystem requirements to test scenarios: - -- **`VersionMark-Capture-Capture`**: `Capture_Context_CaptureFlag_SetsCaptureMode` -- **`VersionMark-Capture-JobId`**: `Capture_Context_WithJobId_SetsJobId` -- **`VersionMark-Capture-Output`**: `Capture_SaveAndLoad_PreservesAllVersionData` -- **`VersionMark-Capture-DefaultOutput`**: `Capture_Run_NoOutputFlagSpecified_UsesDefaultFilename` -- **`VersionMark-Capture-ToolFilter`**: `Capture_Context_WithToolFilter_SetsToolNames` -- **`VersionMark-Capture-MultipleTools`**: `Capture_Run_NoToolFilter_CapturesAllConfiguredTools` -- **`VersionMark-Capture-Config`**: `Capture_Config_ReadFromFile_LoadsToolDefinitions` -- **`VersionMark-Capture-Command`**: `Capture_FindVersions_ExecutesCommandAndExtractsVersion` -- **`VersionMark-Capture-JsonOutput`**: `Capture_SaveAndLoad_PreservesAllVersionData`, - `Capture_MultipleCaptures_EachFileHasDistinctJobId` -- **`VersionMark-Capture-Display`**: `Capture_Run_DisplaysCapturedVersionsAfterCapture` -- **`VersionMark-Capture-ConfigError`**: `Capture_Run_MissingConfig_ReportsError` -- **`VersionMark-Capture-CommandFailure`**: `Capture_FindVersions_ExecutesCommandAndExtractsVersion` +**Capture_Context_CaptureFlag_SetsCaptureMode**: `--capture` sets capture mode in the +context. This scenario is tested by `Capture_Context_CaptureFlag_SetsCaptureMode`. + +**Capture_Context_WithJobId_SetsJobId**: `--job-id` sets the job ID in the context. This +scenario is tested by `Capture_Context_WithJobId_SetsJobId`. + +**Capture_Run_NoOutputFlagSpecified_UsesDefaultFilename**: When no `--output` flag is +specified, the default output filename is derived from the job ID. This scenario is tested +by `Capture_Run_NoOutputFlagSpecified_UsesDefaultFilename`. + +**Capture_Context_WithToolFilter_SetsToolNames**: Tool filter patterns after `--` are +captured in the context. This scenario is tested by +`Capture_Context_WithToolFilter_SetsToolNames`. + +**Capture_Run_NoToolFilter_CapturesAllConfiguredTools**: When no tool filter is specified, +all configured tools are captured. This scenario is tested by +`Capture_Run_NoToolFilter_CapturesAllConfiguredTools`. + +**Capture_Config_ReadFromFile_LoadsToolDefinitions**: The config file loads all tool +definitions correctly. This scenario is tested by +`Capture_Config_ReadFromFile_LoadsToolDefinitions`. + +**Capture_FindVersions_ExecutesCommandAndExtractsVersion**: The tool command is executed and +the version string is extracted via the configured regex. This scenario is tested by +`Capture_FindVersions_ExecutesCommandAndExtractsVersion`. + +**Capture_Run_DisplaysCapturedVersionsAfterCapture**: Captured versions are displayed in +the output after capture completes. This scenario is tested by +`Capture_Run_DisplaysCapturedVersionsAfterCapture`. + +**Capture_Run_MissingConfig_ReportsError**: A missing config file reports an error. This +scenario is tested by `Capture_Run_MissingConfig_ReportsError`. + +**Capture_SaveAndLoad_PreservesAllVersionData**: A save and load cycle preserves all +version data. This scenario is tested by `Capture_SaveAndLoad_PreservesAllVersionData`. + +**Capture_MultipleCaptures_EachFileHasDistinctJobId**: Multiple capture files each have a +distinct job ID. This scenario is tested by +`Capture_MultipleCaptures_EachFileHasDistinctJobId`. + +**Capture_Run_MissingJobId_ReportsErrorAndNonZeroExitCode**: When `--capture` is used +without `--job-id`, the capture pipeline reports an error and returns a non-zero exit code. +This scenario is tested by `Capture_Run_MissingJobId_ReportsErrorAndNonZeroExitCode`. + +**Capture_Run_InvalidCommand_ReportsErrorAndNonZeroExitCode**: When a configured command is +invalid or not found, the capture pipeline reports an error and returns a non-zero exit code. +This scenario is tested by `Capture_Run_InvalidCommand_ReportsErrorAndNonZeroExitCode`. + +**Capture_Run_RegexNoMatch_ReportsErrorAndNonZeroExitCode**: When command output does not +match the configured regex, the capture pipeline reports an error and returns a non-zero +exit code. This scenario is tested by `Capture_Run_RegexNoMatch_ReportsErrorAndNonZeroExitCode`. diff --git a/docs/verification/version-mark/capture/version-info.md b/docs/verification/version-mark/capture/version-info.md index bc4072d..b4efeea 100644 --- a/docs/verification/version-mark/capture/version-info.md +++ b/docs/verification/version-mark/capture/version-info.md @@ -1,44 +1,60 @@ -### VersionInfo Unit Verification +### VersionInfo -#### Overview +#### Verification Approach The `VersionInfo` unit is a JSON-serializable record that holds captured version data for a single CI/CD job. It provides `SaveToFile` to write the record to a JSON file and -`LoadFromFile` to read it back. Tests are in `Capture/VersionInfoTests.cs`. +`LoadFromFile` to read it back. Tests are in `Capture/VersionInfoTests.cs` and read and +write temporary JSON files. No external mocks are required. + +#### Test Environment + +N/A - standard test environment. Tests create temporary JSON files during setup and clean +them up afterwards. + +#### Acceptance Criteria + +- All unit tests for `VersionInfo` pass with zero failures across all supported OS and + .NET version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the `VersionInfo` unit is covered by at least one named test + scenario. #### Test Scenarios -The following test scenarios verify `VersionInfo`: - -- **`VersionInfo_SaveToFile_CreatesJsonFile`**: `SaveToFile` creates a JSON file at the specified path. -- **`VersionInfo_SaveAndLoad_RoundTripPreservesData`**: Save followed by load preserves all version entries. -- **`VersionInfo_EmptyVersions_SavesAndLoadsCorrectly`**: An empty versions dictionary saves and loads correctly. -- **`VersionInfo_SpecialCharacters_SavesAndLoadsCorrectly`**: Version strings with special characters are preserved. -- **`VersionInfo_LoadFromFile_ReadsJsonFile`**: `LoadFromFile` reads a pre-existing JSON file correctly. -- **`VersionInfo_LoadFromFile_NonExistentFile_ThrowsArgumentException`**: Non-existent file throws ArgumentException. -- **`VersionInfo_LoadFromFile_InvalidJson_ThrowsArgumentException`**: Invalid JSON content throws ArgumentException. -- **`VersionInfo_LoadFromFile_EmptyJson_ThrowsArgumentException`**: Empty JSON file throws ArgumentException. -- **`VersionInfo_LoadFromFile_NullJson_ThrowsArgumentException`**: JSON containing only null throws ArgumentException. -- **`VersionInfo_SaveToFile_InvalidPath_ThrowsInvalidOperationException`**: Invalid file path throws InvalidOperationException. - -#### Dependencies - -No external mocks are required. Tests read and write temporary JSON files. - -#### Requirements Coverage - -The following list maps `VersionInfo` unit requirements to test scenarios: - -- **`VersionMark-VersionInfo-Save`**: `VersionInfo_SaveToFile_CreatesJsonFile`, - `VersionInfo_SaveAndLoad_RoundTripPreservesData`, - `VersionInfo_EmptyVersions_SavesAndLoadsCorrectly`, - `VersionInfo_SpecialCharacters_SavesAndLoadsCorrectly` -- **`VersionMark-VersionInfo-Load`**: `VersionInfo_LoadFromFile_ReadsJsonFile`, - `VersionInfo_SaveAndLoad_RoundTripPreservesData`, - `VersionInfo_EmptyVersions_SavesAndLoadsCorrectly`, - `VersionInfo_SpecialCharacters_SavesAndLoadsCorrectly` -- **`VersionMark-VersionInfo-Error`**: `VersionInfo_LoadFromFile_NonExistentFile_ThrowsArgumentException`, - `VersionInfo_LoadFromFile_InvalidJson_ThrowsArgumentException`, - `VersionInfo_LoadFromFile_EmptyJson_ThrowsArgumentException`, - `VersionInfo_LoadFromFile_NullJson_ThrowsArgumentException`, - `VersionInfo_SaveToFile_InvalidPath_ThrowsInvalidOperationException` +**VersionInfo_SaveToFile_CreatesJsonFile**: `SaveToFile` creates a JSON file at the +specified path. This scenario is tested by `VersionInfo_SaveToFile_CreatesJsonFile`. + +**VersionInfo_SaveAndLoad_RoundTripPreservesData**: A save followed by a load preserves all +version entries. This scenario is tested by +`VersionInfo_SaveAndLoad_RoundTripPreservesData`. + +**VersionInfo_EmptyVersions_SavesAndLoadsCorrectly**: An empty versions dictionary saves +and loads correctly. This scenario is tested by +`VersionInfo_EmptyVersions_SavesAndLoadsCorrectly`. + +**VersionInfo_SpecialCharacters_SavesAndLoadsCorrectly**: Version strings with special +characters are preserved through a save and load cycle. This scenario is tested by +`VersionInfo_SpecialCharacters_SavesAndLoadsCorrectly`. + +**VersionInfo_LoadFromFile_ReadsJsonFile**: `LoadFromFile` reads a pre-existing JSON file +correctly. This scenario is tested by `VersionInfo_LoadFromFile_ReadsJsonFile`. + +**VersionInfo_LoadFromFile_NonExistentFile_ThrowsArgumentException**: A non-existent file +throws `ArgumentException`. This scenario is tested by +`VersionInfo_LoadFromFile_NonExistentFile_ThrowsArgumentException`. + +**VersionInfo_LoadFromFile_InvalidJson_ThrowsArgumentException**: Invalid JSON content +throws `ArgumentException`. This scenario is tested by +`VersionInfo_LoadFromFile_InvalidJson_ThrowsArgumentException`. + +**VersionInfo_LoadFromFile_EmptyJson_ThrowsArgumentException**: An empty JSON file throws +`ArgumentException`. This scenario is tested by +`VersionInfo_LoadFromFile_EmptyJson_ThrowsArgumentException`. + +**VersionInfo_LoadFromFile_NullJson_ThrowsArgumentException**: A JSON file containing only +null throws `ArgumentException`. This scenario is tested by +`VersionInfo_LoadFromFile_NullJson_ThrowsArgumentException`. + +**VersionInfo_SaveToFile_InvalidPath_ThrowsInvalidOperationException**: An invalid file +path throws `InvalidOperationException`. This scenario is tested by +`VersionInfo_SaveToFile_InvalidPath_ThrowsInvalidOperationException`. diff --git a/docs/verification/version-mark/cli.md b/docs/verification/version-mark/cli.md index d96972b..1097b30 100644 --- a/docs/verification/version-mark/cli.md +++ b/docs/verification/version-mark/cli.md @@ -1,52 +1,59 @@ -## Cli Subsystem Verification +## Cli -### Overview +### Verification Approach The Cli subsystem is responsible for parsing command-line arguments, routing program flow to the appropriate subsystem, and managing all output (console, error, and log file). It consists of two units: `Program` (the entry point) and `Context` (the argument and output -container). +container). Subsystem-level integration tests are in `CliTests.cs` and cover the full CLI +pipeline with `Context` and `Program` working together. Tests construct a `Context` with +specific arguments and call `Program.Run`, then assert on console output and exit code. +`StringWriter` captures console output without external mocks or file system interaction. +The one test that exercises file I/O uses a temporary file created during test setup. -Subsystem-level integration tests are in `CliTests.cs` and cover the full CLI pipeline -with `Context` and `Program` working together. Unit-level verification for `Program` and -`Context` is in the chapters that follow. +### Test Environment -### Verification Approach +N/A - standard test environment. All tests run using `dotnet test` with no additional +environment setup required beyond the standard .NET test runner. Console output is +captured using `StringWriter`. The one test that exercises file I/O +(`Cli_Run_LogFlag_WritesOutputToLogFile`) uses a temporary file created during test setup. + +### Acceptance Criteria -Integration tests construct a `Context` with specific arguments and call `Program.Run`, -then assert on console output and exit code. `StringWriter` captures console output -without external mocks or file system interaction. +- All subsystem integration tests pass with zero failures across all supported OS and .NET + version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the Cli subsystem is covered by at least one named test scenario. ### Test Scenarios -The following integration test scenarios verify Cli subsystem requirements: - -- **`Cli_Run_VersionFlag_ExitsCleanly`**: Context with `--version`; Program.Run exits with code 0. -- **`Cli_Run_SilentWithVersionFlag_SuppressesOutput`**: Context with `--silent --version`; output is suppressed. -- **`Cli_Run_HelpFlag_DisplaysUsageInformation`**: `--help` shows usage; exit code 0. -- **`Cli_Run_ValidateFlag_RunsValidation`**: `--validate --silent` exits 0. -- **`Cli_Run_InvalidArgs_ThrowsArgumentException`**: `--unknown-flag-xyz` throws ArgumentException. -- **`Cli_Run_LintFlag_ValidConfig_Succeeds`**: `--lint ` on a valid config exits 0. -- **`Cli_Run_ResultsFlag_WritesResultsFile`**: `--validate --results ` creates a TRX file. -- **`Cli_Run_LogFlag_WritesOutputToLogFile`**: `--version --log ` writes to log. - -### Dependencies - -No external mocks are required. Tests use `StringWriter` to capture console output. - -### Requirements Coverage - -The following list maps Cli subsystem requirements to test scenarios: - -- **`VersionMark-CommandLine-Context`**: `Cli_Run_VersionFlag_ExitsCleanly`, - `Cli_Run_SilentWithVersionFlag_SuppressesOutput` -- **`VersionMark-CommandLine-Version`**: `Cli_Run_VersionFlag_ExitsCleanly` -- **`VersionMark-CommandLine-Help`**: `Cli_Run_HelpFlag_DisplaysUsageInformation` -- **`VersionMark-CommandLine-Silent`**: `Cli_Run_SilentWithVersionFlag_SuppressesOutput` -- **`VersionMark-CommandLine-Validate`**: `Cli_Run_ValidateFlag_RunsValidation` -- **`VersionMark-CommandLine-Results`**: `Cli_Run_ResultsFlag_WritesResultsFile` -- **`VersionMark-CommandLine-Log`**: `Cli_Run_LogFlag_WritesOutputToLogFile` -- **`VersionMark-CommandLine-ErrorOutput`**: `Cli_Run_InvalidArgs_ThrowsArgumentException` -- **`VersionMark-CommandLine-InvalidArgs`**: `Cli_Run_InvalidArgs_ThrowsArgumentException` -- **`VersionMark-CommandLine-ExitCode`**: `Cli_Run_InvalidArgs_ThrowsArgumentException` -- **`VersionMark-CommandLine-Lint`**: `Cli_Run_LintFlag_ValidConfig_Succeeds` +**Cli_Run_VersionFlag_ExitsCleanly**: A `Context` constructed with `--version` calls +`Program.Run`, which exits with code 0. This scenario is tested by +`Cli_Run_VersionFlag_ExitsCleanly`. + +**Cli_Run_SilentWithVersionFlag_SuppressesOutput**: A `Context` constructed with +`--silent --version` verifies that all console output is suppressed during the run. This +scenario is tested by `Cli_Run_SilentWithVersionFlag_SuppressesOutput`. + +**Cli_Run_HelpFlag_DisplaysUsageInformation**: A `Context` constructed with `--help` +verifies that usage information is displayed and the exit code is 0. This scenario is +tested by `Cli_Run_HelpFlag_DisplaysUsageInformation`. + +**Cli_Run_ValidateFlag_RunsValidation**: A `Context` constructed with `--validate +--silent` verifies that validation runs successfully and exits with code 0. This scenario +is tested by `Cli_Run_ValidateFlag_RunsValidation`. + +**Cli_Run_InvalidArgs_ThrowsArgumentException**: A `Context` constructed with an +unrecognized flag such as `--unknown-flag-xyz` verifies that an ArgumentException is +thrown. This scenario is tested by `Cli_Run_InvalidArgs_ThrowsArgumentException`. + +**Cli_Run_LintFlag_ValidConfig_Succeeds**: A `Context` constructed with `--lint ` +pointing to a valid configuration file verifies that the run exits with code 0. This +scenario is tested by `Cli_Run_LintFlag_ValidConfig_Succeeds`. + +**Cli_Run_ResultsFlag_WritesResultsFile**: A `Context` constructed with `--validate +--results ` verifies that a TRX results file is created at the specified path. This +scenario is tested by `Cli_Run_ResultsFlag_WritesResultsFile`. + +**Cli_Run_LogFlag_WritesOutputToLogFile**: A `Context` constructed with `--version --log +` verifies that program output is written to the specified log file. This scenario +is tested by `Cli_Run_LogFlag_WritesOutputToLogFile`. diff --git a/docs/verification/version-mark/cli/context.md b/docs/verification/version-mark/cli/context.md index 1a32cd4..0c318b1 100644 --- a/docs/verification/version-mark/cli/context.md +++ b/docs/verification/version-mark/cli/context.md @@ -1,101 +1,181 @@ -### Context Unit Verification +### Context -#### Overview +#### Verification Approach The `Context` unit parses command-line arguments and manages all output routing (console, -error stream, and log file). Unit tests are in `Cli/ContextTests.cs`. Each test -constructs a `Context` via `Context.Create()` with specific argument arrays and asserts on -the resulting property values. `StringWriter` captures console output. +error stream, and log file). Unit tests are in `Cli/ContextTests.cs`. Each test constructs +a `Context` via `Context.Create()` with specific argument arrays and asserts on the +resulting property values. `StringWriter` captures console output. The one test that +exercises file I/O (`Context_Create_LogFlag_OpensLogFile`) uses a temporary log file +created during test setup. + +#### Test Environment + +N/A - standard test environment. All tests run using `dotnet test` with no additional +environment setup required. + +#### Acceptance Criteria + +- All unit tests for `Context` pass with zero failures across all supported OS and .NET + version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the `Context` unit is covered by at least one named test scenario. #### Test Scenarios -The following test scenarios verify `Context` unit requirements: - -- **`Context_Create_NoArguments_ReturnsDefaultContext`**: No arguments creates a default context with all flags false. -- **`Context_Create_VersionFlag_SetsVersionTrue`**: `--version` sets `Version = true`. -- **`Context_Create_ShortVersionFlag_SetsVersionTrue`**: `-v` sets `Version = true`. -- **`Context_Create_HelpFlag_SetsHelpTrue`**: `--help` sets `Help = true`. -- **`Context_Create_ShortHelpFlag_H_SetsHelpTrue`**: `-h` sets `Help = true`. -- **`Context_Create_ShortHelpFlag_Question_SetsHelpTrue`**: `-?` sets `Help = true`. -- **`Context_Create_SilentFlag_SetsSilentTrue`**: `--silent` sets `Silent = true`. -- **`Context_Create_ValidateFlag_SetsValidateTrue`**: `--validate` sets `Validate = true`. -- **`Context_Create_ResultsFlag_SetsResultsFile`**: `--results file.trx` sets `ResultsFile`. -- **`Context_Create_ResultFlag_SetsResultsFile`**: `--result file.trx` (alias) sets `ResultsFile`. -- **`Context_Create_LogFlag_OpensLogFile`**: `--log file.log` opens a log file writer. -- **`Context_Create_UnknownArgument_ThrowsArgumentException`**: Unknown argument throws ArgumentException. -- **`Context_Create_LogFlag_WithoutValue_ThrowsArgumentException`**: `--log` without a value throws ArgumentException. -- **`Context_Create_ResultsFlag_WithoutValue_ThrowsArgumentException`**: `--results` without a value throws ArgumentException. -- **`Context_Create_PublishFlag_SetsPublishTrue`**: `--publish` sets `Publish = true`. -- **`Context_Create_ReportParameter_SetsReportFile`**: `--report file.md` sets `ReportFile`. -- **`Context_Create_ReportDepthParameter_SetsReportDepth`**: `--report-depth 2` sets `ReportDepth` to 2. -- **`Context_Create_NoReportDepth_DefaultsToDepthOne`**: Default `ReportDepth` is 1 when not specified. -- **`Context_Create_ReportDepthZero_ThrowsArgumentException`**: `--report-depth 0` throws ArgumentException. -- **`Context_Create_ReportDepthNegative_ThrowsArgumentException`**: Negative `--report-depth` throws ArgumentException. -- **`Context_Create_ReportDepthSeven_ThrowsArgumentException`**: `--report-depth 7` throws ArgumentException. -- **`Context_Create_DepthParameter_SetsDepth`**: `--depth 2` sets the depth. -- **`Context_Create_NoDepth_DefaultsToOne`**: Default depth is 1 when not specified. -- **`Context_Create_DepthParameter_SetsDefaultReportDepth`**: `--depth` sets the default report depth. -- **`Context_Create_ExplicitReportDepthOverridesDepth`**: Explicit `--report-depth` overrides the value set by `--depth`. -- **`Context_Create_DepthZero_ThrowsArgumentException`**: `--depth 0` throws ArgumentException. -- **`Context_Create_DepthNegative_ThrowsArgumentException`**: Negative `--depth` throws ArgumentException. -- **`Context_Create_DepthSeven_ThrowsArgumentException`**: `--depth 7` throws ArgumentException. -- **`Context_Create_GlobPatternsAfterSeparator_CapturesPatterns`**: - Patterns after `--` separator are captured in GlobPatterns. -- **`Context_Create_PublishWithoutReport_ParsesSuccessfully`**: `--publish` without `--report` parses without error. -- **`Context_Create_NoGlobPatterns_EmptyArray`**: No `--` separator produces an empty GlobPatterns array. -- **`Context_Create_LintFlag_SetsLintTrue`**: `--lint` sets `Lint = true`. -- **`Context_Create_LintFlag_WithFile_SetsLintFile`**: `--lint file.yaml` sets the lint config file path. -- **`Context_Create_LintFlag_FollowedByFlag_DoesNotConsumeFlagAsFile`**: - `--lint --version` does not consume `--version` as the lint file. -- **`Context_Create_CaptureFlag_SetsCaptureTrue`**: `--capture` sets `Capture = true`. -- **`Context_Create_JobIdFlag_SetsJobId`**: `--job-id build-1` sets `JobId`. -- **`Context_Create_OutputFlag_SetsOutputFile`**: `--output file.json` sets `OutputFile`. -- **`Context_WriteLine_NotSilent_WritesToConsole`**: `WriteLine` writes to console when not in silent mode. -- **`Context_WriteLine_Silent_DoesNotWriteToConsole`**: `WriteLine` suppresses output in silent mode. -- **`Context_WriteError_NotSilent_WritesToConsole`**: `WriteError` writes to console when not in silent mode. -- **`Context_WriteError_Silent_DoesNotWriteToConsole`**: `WriteError` suppresses output in silent mode. -- **`Context_WriteError_WritesToLogFile`**: `WriteError` writes the message to the log file. -- **`Context_WriteError_SetsErrorExitCode`**: `WriteError` sets the exit code to 1. - -#### Dependencies - -Tests use `StringWriter` to capture console output. No file system access is required -except for `Context_Create_LogFlag_OpensLogFile`, which uses a temporary log file. - -#### Requirements Coverage - -The following list maps `Context` unit requirements to test scenarios: - -- **`VersionMark-Context-Create`**: `Context_Create_NoArguments_ReturnsDefaultContext`, - `Context_Create_VersionFlag_SetsVersionTrue`, `Context_Create_ShortVersionFlag_SetsVersionTrue`, - `Context_Create_HelpFlag_SetsHelpTrue`, `Context_Create_ShortHelpFlag_H_SetsHelpTrue`, - `Context_Create_ShortHelpFlag_Question_SetsHelpTrue`, `Context_Create_SilentFlag_SetsSilentTrue`, - `Context_Create_ValidateFlag_SetsValidateTrue`, `Context_Create_ResultsFlag_SetsResultsFile`, - `Context_Create_ResultFlag_SetsResultsFile`, `Context_Create_LogFlag_OpensLogFile`, - `Context_Create_UnknownArgument_ThrowsArgumentException`, - `Context_Create_LogFlag_WithoutValue_ThrowsArgumentException`, - `Context_Create_ResultsFlag_WithoutValue_ThrowsArgumentException`, - `Context_Create_PublishFlag_SetsPublishTrue`, `Context_Create_ReportParameter_SetsReportFile`, - `Context_Create_ReportDepthParameter_SetsReportDepth`, - `Context_Create_NoReportDepth_DefaultsToDepthOne`, - `Context_Create_GlobPatternsAfterSeparator_CapturesPatterns`, - `Context_Create_PublishWithoutReport_ParsesSuccessfully`, - `Context_Create_NoGlobPatterns_EmptyArray`, `Context_Create_LintFlag_SetsLintTrue`, - `Context_Create_LintFlag_WithFile_SetsLintFile`, - `Context_Create_LintFlag_FollowedByFlag_DoesNotConsumeFlagAsFile`, - `Context_Create_DepthParameter_SetsDepth`, `Context_Create_NoDepth_DefaultsToOne`, - `Context_Create_DepthParameter_SetsDefaultReportDepth`, - `Context_Create_ExplicitReportDepthOverridesDepth`, - `Context_Create_DepthZero_ThrowsArgumentException`, - `Context_Create_DepthNegative_ThrowsArgumentException`, - `Context_Create_DepthSeven_ThrowsArgumentException`, - `Context_Create_ReportDepthZero_ThrowsArgumentException`, - `Context_Create_ReportDepthNegative_ThrowsArgumentException`, - `Context_Create_ReportDepthSeven_ThrowsArgumentException`, - `Context_Create_CaptureFlag_SetsCaptureTrue`, `Context_Create_JobIdFlag_SetsJobId`, - `Context_Create_OutputFlag_SetsOutputFile` -- **`VersionMark-Context-WriteLine`**: `Context_WriteLine_NotSilent_WritesToConsole`, - `Context_WriteLine_Silent_DoesNotWriteToConsole` -- **`VersionMark-Context-WriteError`**: `Context_WriteError_NotSilent_WritesToConsole`, - `Context_WriteError_Silent_DoesNotWriteToConsole`, `Context_WriteError_WritesToLogFile` -- **`VersionMark-Context-WriteErrorExitCode`**: `Context_WriteError_SetsErrorExitCode` +**Context_Create_NoArguments_ReturnsDefaultContext**: No arguments creates a default context +with all flags false. This scenario is tested by +`Context_Create_NoArguments_ReturnsDefaultContext`. + +**Context_Create_VersionFlag_SetsVersionTrue**: `--version` sets `Version = true`. This +scenario is tested by `Context_Create_VersionFlag_SetsVersionTrue`. + +**Context_Create_ShortVersionFlag_SetsVersionTrue**: `-v` sets `Version = true`. This +scenario is tested by `Context_Create_ShortVersionFlag_SetsVersionTrue`. + +**Context_Create_HelpFlag_SetsHelpTrue**: `--help` sets `Help = true`. This scenario is +tested by `Context_Create_HelpFlag_SetsHelpTrue`. + +**Context_Create_ShortHelpFlag_H_SetsHelpTrue**: `-h` sets `Help = true`. This scenario +is tested by `Context_Create_ShortHelpFlag_H_SetsHelpTrue`. + +**Context_Create_ShortHelpFlag_Question_SetsHelpTrue**: `-?` sets `Help = true`. This +scenario is tested by `Context_Create_ShortHelpFlag_Question_SetsHelpTrue`. + +**Context_Create_SilentFlag_SetsSilentTrue**: `--silent` sets `Silent = true`. This +scenario is tested by `Context_Create_SilentFlag_SetsSilentTrue`. + +**Context_Create_ValidateFlag_SetsValidateTrue**: `--validate` sets `Validate = true`. +This scenario is tested by `Context_Create_ValidateFlag_SetsValidateTrue`. + +**Context_Create_ResultsFlag_SetsResultsFile**: `--results file.trx` sets +`ResultsFile`. This scenario is tested by `Context_Create_ResultsFlag_SetsResultsFile`. + +**Context_Create_ResultFlag_SetsResultsFile**: `--result file.trx` (alias) sets +`ResultsFile`. This scenario is tested by `Context_Create_ResultFlag_SetsResultsFile`. + +**Context_Create_LogFlag_OpensLogFile**: `--log file.log` opens a log file writer. This +scenario is tested by `Context_Create_LogFlag_OpensLogFile`. + +**Context_Create_UnknownArgument_ThrowsArgumentException**: An unknown argument throws +`ArgumentException`. This scenario is tested by +`Context_Create_UnknownArgument_ThrowsArgumentException`. + +**Context_Create_LogFlag_WithoutValue_ThrowsArgumentException**: `--log` without a value +throws `ArgumentException`. This scenario is tested by +`Context_Create_LogFlag_WithoutValue_ThrowsArgumentException`. + +**Context_Create_ResultsFlag_WithoutValue_ThrowsArgumentException**: `--results` without +a value throws `ArgumentException`. This scenario is tested by +`Context_Create_ResultsFlag_WithoutValue_ThrowsArgumentException`. + +**Context_Create_PublishFlag_SetsPublishTrue**: `--publish` sets `Publish = true`. This +scenario is tested by `Context_Create_PublishFlag_SetsPublishTrue`. + +**Context_Create_ReportParameter_SetsReportFile**: `--report file.md` sets +`ReportFile`. This scenario is tested by `Context_Create_ReportParameter_SetsReportFile`. + +**Context_Create_ReportDepthParameter_SetsReportDepth**: `--report-depth 2` sets +`ReportDepth` to 2. This scenario is tested by +`Context_Create_ReportDepthParameter_SetsReportDepth`. + +**Context_Create_NoReportDepth_DefaultsToDepthOne**: Default `ReportDepth` is 1 when not +specified. This scenario is tested by `Context_Create_NoReportDepth_DefaultsToDepthOne`. + +**Context_Create_ReportDepthZero_ThrowsArgumentException**: `--report-depth 0` throws +`ArgumentException`. This scenario is tested by +`Context_Create_ReportDepthZero_ThrowsArgumentException`. + +**Context_Create_ReportDepthNegative_ThrowsArgumentException**: A negative +`--report-depth` throws `ArgumentException`. This scenario is tested by +`Context_Create_ReportDepthNegative_ThrowsArgumentException`. + +**Context_Create_ReportDepthSeven_ThrowsArgumentException**: `--report-depth 7` throws +`ArgumentException`. This scenario is tested by +`Context_Create_ReportDepthSeven_ThrowsArgumentException`. + +**Context_Create_DepthParameter_SetsDepth**: `--depth 2` sets the depth. This scenario +is tested by `Context_Create_DepthParameter_SetsDepth`. + +**Context_Create_NoDepth_DefaultsToOne**: Default depth is 1 when not specified. This +scenario is tested by `Context_Create_NoDepth_DefaultsToOne`. + +**Context_Create_DepthParameter_SetsDefaultReportDepth**: `--depth` sets the default +report depth. This scenario is tested by +`Context_Create_DepthParameter_SetsDefaultReportDepth`. + +**Context_Create_ExplicitReportDepthOverridesDepth**: Explicit `--report-depth` overrides +the value set by `--depth`. This scenario is tested by +`Context_Create_ExplicitReportDepthOverridesDepth`. + +**Context_Create_DepthZero_ThrowsArgumentException**: `--depth 0` throws +`ArgumentException`. This scenario is tested by +`Context_Create_DepthZero_ThrowsArgumentException`. + +**Context_Create_DepthNegative_ThrowsArgumentException**: A negative `--depth` throws +`ArgumentException`. This scenario is tested by +`Context_Create_DepthNegative_ThrowsArgumentException`. + +**Context_Create_DepthSeven_ThrowsArgumentException**: `--depth 7` throws +`ArgumentException`. This scenario is tested by +`Context_Create_DepthSeven_ThrowsArgumentException`. + +**Context_Create_GlobPatternsAfterSeparator_CapturesPatterns**: Patterns after the `--` +separator are captured in `GlobPatterns`. This scenario is tested by +`Context_Create_GlobPatternsAfterSeparator_CapturesPatterns`. + +**Context_Create_PublishWithoutReport_ParsesSuccessfully**: `--publish` without +`--report` parses without error. This scenario is tested by +`Context_Create_PublishWithoutReport_ParsesSuccessfully`. + +**Context_Create_NoGlobPatterns_EmptyArray**: No `--` separator produces an empty +`GlobPatterns` array. This scenario is tested by +`Context_Create_NoGlobPatterns_EmptyArray`. + +**Context_Create_LintFlag_SetsLintTrue**: `--lint` sets `Lint = true`. This scenario is +tested by `Context_Create_LintFlag_SetsLintTrue`. + +**Context_Create_LintFlag_WithFile_SetsLintFile**: `--lint file.yaml` sets the lint +config file path. This scenario is tested by +`Context_Create_LintFlag_WithFile_SetsLintFile`. + +**Context_Create_LintFlag_FollowedByFlag_DoesNotConsumeFlagAsFile**: `--lint --version` +does not consume `--version` as the lint file. This scenario is tested by +`Context_Create_LintFlag_FollowedByFlag_DoesNotConsumeFlagAsFile`. + +**Context_Create_CaptureFlag_SetsCaptureTrue**: `--capture` sets `Capture = true`. This +scenario is tested by `Context_Create_CaptureFlag_SetsCaptureTrue`. + +**Context_Create_JobIdFlag_SetsJobId**: `--job-id build-1` sets `JobId`. This scenario +is tested by `Context_Create_JobIdFlag_SetsJobId`. + +**Context_Create_OutputFlag_SetsOutputFile**: `--output file.json` sets `OutputFile`. +This scenario is tested by `Context_Create_OutputFlag_SetsOutputFile`. + +**Context_WriteLine_NotSilent_WritesToConsole**: `WriteLine` writes to console when not +in silent mode. This scenario is tested by `Context_WriteLine_NotSilent_WritesToConsole`. + +**Context_WriteLine_Silent_DoesNotWriteToConsole**: `WriteLine` suppresses output in +silent mode. This scenario is tested by +`Context_WriteLine_Silent_DoesNotWriteToConsole`. + +**Context_WriteError_NotSilent_WritesToConsole**: `WriteError` writes to console when not +in silent mode. This scenario is tested by +`Context_WriteError_NotSilent_WritesToConsole`. + +**Context_WriteError_Silent_SuppressesStderr**: `WriteError` suppresses output in +silent mode. This scenario is tested by +`Context_WriteError_Silent_SuppressesStderr`. + +**Context_WriteError_WritesToLogFile**: `WriteError` writes the message to the log file. +This scenario is tested by `Context_WriteError_WritesToLogFile`. + +**Context_WriteError_SetsErrorExitCode**: `WriteError` sets the exit code to 1. This +scenario is tested by `Context_WriteError_SetsErrorExitCode`. + +**Context_Create_DepthFlag_WithoutValue_ThrowsArgumentException**: `--depth` without a +value throws `ArgumentException`. This scenario is tested by +`Context_Create_DepthFlag_WithoutValue_ThrowsArgumentException`. + +**Context_Create_SeparatorOutsideMode_ThrowsArgumentException**: The `--` separator used +outside capture or publish mode throws `ArgumentException`. This scenario is tested by +`Context_Create_SeparatorOutsideMode_ThrowsArgumentException`. diff --git a/docs/verification/version-mark/cli/program.md b/docs/verification/version-mark/cli/program.md index 842fc05..f37bfef 100644 --- a/docs/verification/version-mark/cli/program.md +++ b/docs/verification/version-mark/cli/program.md @@ -1,66 +1,107 @@ -### Program Unit Verification +### Program -#### Overview +#### Verification Approach The `Program` unit is the entry point for the VersionMark tool. It dispatches to the -appropriate subsystem based on parsed context flags. Unit tests are in `ProgramTests.cs`. -Each test constructs a `Context` with specific arguments and calls `Program.Run`, then -asserts on console output and exit code. `StringWriter` captures console output. +appropriate subsystem based on parsed context flags and exposes a static `Version` property +for the tool version string. Unit tests are in `ProgramTests.cs` and cover every dispatch +path including each supported flag, invalid argument handling, capture, publish, and lint +workflows. Tests construct a `Context` with specific arguments and call `Program.Run`, then +assert on console output and exit code. All console output is captured using `StringWriter` +so no real I/O is required for most scenarios. Capture and publish tests use temporary +directories and configuration files created during test setup. + +#### Test Environment + +N/A - standard test environment. Tests that require file I/O use temporary directories +created during test setup. The `FindVersions` scenarios require the `dotnet` command to be +available on `PATH`. + +#### Acceptance Criteria + +- All unit tests for `Program` pass with zero failures across all supported OS and .NET + version matrix combinations (Windows, Linux, macOS × .NET 8, .NET 9, .NET 10). +- Every requirement for the `Program` unit is covered by at least one named test scenario. #### Test Scenarios -The following test scenarios verify `Program` unit requirements: - -- **`Program_Version_ReturnsNonEmptyString`**: `Program.Version` returns a non-empty version string. -- **`Program_Run_WithVersionFlag_DisplaysVersionOnly`**: `--version` writes version only (no copyright). -- **`Program_Run_WithHelpFlag_DisplaysUsageInformation`**: `--help` writes usage information. -- **`Program_Run_WithValidateFlag_RunsValidation`**: `--validate` writes output containing "Total Tests:". -- **`Program_Run_NoArguments_DisplaysDefaultBehavior`**: No arguments writes banner with version and copyright. -- **`Program_Run_WithCaptureCommand_CapturesToolVersions`**: `--capture --job-id --output -- dotnet` creates output file. -- **`Program_Run_WithCaptureCommandNoToolFilter_CapturesAllConfiguredTools`**: - Capture without tool filter captures all configured tools. -- **`Program_Run_WithCaptureCommandWithoutJobId_ReturnsError`**: - Missing `--job-id` returns a non-zero exit code. -- **`Program_Run_WithCaptureCommandWithMissingConfig_ReturnsError`**: - Missing `.versionmark.yaml` returns a non-zero exit code. -- **`Program_Run_WithPublishCommandWithoutReport_ReturnsError`**: - `--publish` without `--report` returns a non-zero exit code. -- **`Program_Run_WithPublishCommandNoMatchingFiles_ReturnsError`**: Glob matching no files returns a non-zero exit code. -- **`Program_Run_WithPublishCommandInvalidJson_ReturnsError`**: Invalid JSON file returns a non-zero exit code. -- **`Program_Run_WithPublishCommand_GeneratesMarkdownReport`**: Full publish generates a markdown report file. -- **`Program_Run_WithPublishCommandCustomDepth_AdjustsHeadingLevels`**: Custom depth adjusts heading - levels in the report. -- **`Program_Run_WithLintFlag_ValidConfig_ReturnsSuccess`**: Valid config lint returns exit code 0. -- **`Program_Run_WithLintFlag_ValidConfig_SuppressesBanner`**: Lint mode suppresses the banner. -- **`Program_Run_WithLintFlag_InvalidConfig_ReturnsError`**: Invalid config lint reports error - and returns non-zero exit code. -- **`Program_Run_WithLintFlag_NoFile_UsesDefaultConfigFile`**: No file argument uses `.versionmark.yaml` as default. -- **`Program_Run_WithHelpFlag_IncludesLintInformation`**: Help output includes lint command information. - -#### Dependencies - -Tests use `StringWriter` to capture console output. Capture and publish tests use -temporary directories and configuration files created during test setup. - -#### Requirements Coverage - -The following list maps `Program` unit requirements to test scenarios: - -- **`VersionMark-Program-Version`**: `Program_Version_ReturnsNonEmptyString` -- **`VersionMark-Program-Dispatch`**: `Program_Run_WithVersionFlag_DisplaysVersionOnly`, - `Program_Run_WithHelpFlag_DisplaysUsageInformation`, - `Program_Run_WithValidateFlag_RunsValidation`, `Program_Run_NoArguments_DisplaysDefaultBehavior` -- **`VersionMark-Program-RunCapture`**: `Program_Run_WithCaptureCommand_CapturesToolVersions`, - `Program_Run_WithCaptureCommandNoToolFilter_CapturesAllConfiguredTools`, - `Program_Run_WithCaptureCommandWithoutJobId_ReturnsError`, - `Program_Run_WithCaptureCommandWithMissingConfig_ReturnsError` -- **`VersionMark-Program-RunPublish`**: `Program_Run_WithPublishCommandWithoutReport_ReturnsError`, - `Program_Run_WithPublishCommandNoMatchingFiles_ReturnsError`, - `Program_Run_WithPublishCommandInvalidJson_ReturnsError`, - `Program_Run_WithPublishCommand_GeneratesMarkdownReport`, - `Program_Run_WithPublishCommandCustomDepth_AdjustsHeadingLevels` -- **`VersionMark-Program-RunLint`**: `Program_Run_WithLintFlag_ValidConfig_ReturnsSuccess`, - `Program_Run_WithLintFlag_ValidConfig_SuppressesBanner`, - `Program_Run_WithLintFlag_InvalidConfig_ReturnsError`, - `Program_Run_WithLintFlag_NoFile_UsesDefaultConfigFile`, - `Program_Run_WithHelpFlag_IncludesLintInformation` +**Program_Version_ReturnsNonEmptyString**: `Program.Version` returns a non-empty version +string. This scenario is tested by `Program_Version_ReturnsNonEmptyString`. + +**Program_Run_WithVersionFlag_DisplaysVersionOnly**: A `Context` with `--version` calls +`Program.Run`, which writes the version string only (no copyright) and exits with code 0. +This scenario is tested by `Program_Run_WithVersionFlag_DisplaysVersionOnly`. + +**Program_Run_WithHelpFlag_DisplaysUsageInformation**: A `Context` with `--help` calls +`Program.Run`, which writes usage information and exits with code 0. This scenario is +tested by `Program_Run_WithHelpFlag_DisplaysUsageInformation`. + +**Program_Run_WithValidateFlag_RunsValidation**: A `Context` with `--validate` calls +`Program.Run`, which writes output containing "Total Tests:" and exits with code 0. This +scenario is tested by `Program_Run_WithValidateFlag_RunsValidation`. + +**Program_Run_NoArguments_DisplaysDefaultBehavior**: A `Context` with no arguments calls +`Program.Run`, which writes the banner with version and copyright and exits with code 0. +This scenario is tested by `Program_Run_NoArguments_DisplaysDefaultBehavior`. + +**Program_Run_WithCaptureCommand_CapturesToolVersions**: A `Context` with `--capture +--job-id --output -- dotnet` calls `Program.Run`, which creates the output JSON file +containing the captured version. This scenario is tested by +`Program_Run_WithCaptureCommand_CapturesToolVersions`. + +**Program_Run_WithCaptureCommandNoToolFilter_CapturesAllConfiguredTools**: A `Context` with +`--capture` and no tool filter patterns calls `Program.Run`, which captures all tools +defined in the configuration. This scenario is tested by +`Program_Run_WithCaptureCommandNoToolFilter_CapturesAllConfiguredTools`. + +**Program_Run_WithCaptureCommandWithoutJobId_ReturnsError**: A `Context` with `--capture` +but no `--job-id` calls `Program.Run`, which returns a non-zero exit code. This scenario +is tested by `Program_Run_WithCaptureCommandWithoutJobId_ReturnsError`. + +**Program_Run_WithCaptureCommandWithMissingConfig_ReturnsError**: A `Context` with +`--capture` pointing to a missing `.versionmark.yaml` calls `Program.Run`, which returns +a non-zero exit code. This scenario is tested by +`Program_Run_WithCaptureCommandWithMissingConfig_ReturnsError`. + +**Program_Run_WithPublishCommandWithoutReport_ReturnsError**: A `Context` with `--publish` +but no `--report` calls `Program.Run`, which returns a non-zero exit code. This scenario +is tested by `Program_Run_WithPublishCommandWithoutReport_ReturnsError`. + +**Program_Run_WithPublishCommandNoMatchingFiles_ReturnsError**: A `Context` with `--publish` +and a glob pattern matching no files calls `Program.Run`, which returns a non-zero exit +code. This scenario is tested by `Program_Run_WithPublishCommandNoMatchingFiles_ReturnsError`. + +**Program_Run_WithPublishCommandInvalidJson_ReturnsError**: A `Context` with `--publish` +pointing to an invalid JSON file calls `Program.Run`, which returns a non-zero exit code. +This scenario is tested by `Program_Run_WithPublishCommandInvalidJson_ReturnsError`. + +**Program_Run_WithPublishCommand_GeneratesMarkdownReport**: A full publish invocation calls +`Program.Run`, which generates a markdown report file at the specified path. This scenario +is tested by `Program_Run_WithPublishCommand_GeneratesMarkdownReport`. + +**Program_Run_WithPublishCommandCustomDepth_AdjustsHeadingLevels**: A publish invocation +with a custom `--depth` value calls `Program.Run`, which adjusts heading levels in the +report accordingly. This scenario is tested by +`Program_Run_WithPublishCommandCustomDepth_AdjustsHeadingLevels`. + +**Program_Run_WithLintFlag_ValidConfig_ReturnsSuccess**: A `Context` with `--lint ` +pointing to a valid configuration calls `Program.Run`, which exits with code 0. This +scenario is tested by `Program_Run_WithLintFlag_ValidConfig_ReturnsSuccess`. + +**Program_Run_WithLintFlag_ValidConfig_SuppressesBanner**: A `Context` with `--lint ` +on a valid configuration calls `Program.Run`, which suppresses the banner output. This +scenario is tested by `Program_Run_WithLintFlag_ValidConfig_SuppressesBanner`. + +**Program_Run_WithLintFlag_InvalidConfig_ReturnsError**: A `Context` with `--lint ` +pointing to an invalid configuration calls `Program.Run`, which reports an error and +returns a non-zero exit code. This scenario is tested by +`Program_Run_WithLintFlag_InvalidConfig_ReturnsError`. + +**Program_Run_WithLintFlag_NoFile_UsesDefaultConfigFile**: A `Context` with `--lint` and +no file argument calls `Program.Run`, which uses `.versionmark.yaml` as the default +configuration file path. This scenario is tested by +`Program_Run_WithLintFlag_NoFile_UsesDefaultConfigFile`. + +**Program_Run_WithHelpFlag_IncludesLintInformation**: A `Context` with `--help` calls +`Program.Run`, which includes lint command information in the usage output. This scenario +is tested by `Program_Run_WithHelpFlag_IncludesLintInformation`. diff --git a/docs/verification/version-mark/configuration.md b/docs/verification/version-mark/configuration.md index 8b2c8e0..67811e6 100644 --- a/docs/verification/version-mark/configuration.md +++ b/docs/verification/version-mark/configuration.md @@ -1,47 +1,51 @@ -## Configuration Subsystem Verification +## Configuration -### Overview +### Verification Approach The Configuration subsystem is responsible for loading and validating the `.versionmark.yaml` configuration file. It consists of three units: `VersionMarkConfig` (the top-level configuration container), `ToolConfig` (per-tool configuration record), and -`LintIssue` (lint issue record and load result). +`LintIssue` (lint issue record and load result). Subsystem-level integration tests are in +`Configuration/ConfigurationTests.cs` and cover the full configuration loading workflow via +`VersionMarkConfig.ReadFromFile`. Tests write temporary YAML files to disk and assert on +the returned configuration object. No external mocks or stubs are required. -Subsystem-level integration tests are in `Configuration/ConfigurationTests.cs` and cover -the full configuration loading workflow via `VersionMarkConfig.ReadFromFile`. Unit-level -verification for each unit is in the chapters that follow. +### Test Environment -### Verification Approach +N/A - standard test environment. Tests write temporary YAML files to disk during setup and +clean them up afterwards. No additional environment configuration is required. -Integration tests write temporary YAML files to disk and call `VersionMarkConfig.ReadFromFile`, -then assert on the returned configuration object. No external mocks or stubs are required. +### Acceptance Criteria -### Test Scenarios +- All subsystem integration tests pass with zero failures across all supported OS and .NET + version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the Configuration subsystem is covered by at least one named test + scenario. -The following integration test scenarios verify Configuration subsystem requirements: +### Test Scenarios -- **`Configuration_ReadFromFile_MultipleTools_AllToolsAccessible`**: Multiple tools all accessible after load. -- **`Configuration_ReadFromFile_WithOsOverrides_SelectsAppropriateCommand`**: OS overrides select correct command. -- **`Configuration_ReadFromFile_OsRegexOverride_SelectsAppropriateRegex`**: OS regex overrides select correct regex. -- **`Configuration_ReadFromFile_EmptyTools_ThrowsArgumentException`**: Empty tools section throws ArgumentException. -- **`Configuration_ReadFromFile_MissingFile_ThrowsArgumentException`**: Missing config file throws ArgumentException. -- **`Configuration_ReadFromFile_InvalidYaml_ThrowsArgumentException`**: Invalid YAML throws ArgumentException. +**Configuration_ReadFromFile_MultipleTools_AllToolsAccessible**: A YAML file defining +multiple tools is loaded and all tools are accessible in the returned config object. This +scenario is tested by `Configuration_ReadFromFile_MultipleTools_AllToolsAccessible`. -### Dependencies +**Configuration_ReadFromFile_WithOsOverrides_SelectsAppropriateCommand**: A YAML file with +OS-specific command overrides is loaded and the correct command is selected for the current +OS. This scenario is tested by +`Configuration_ReadFromFile_WithOsOverrides_SelectsAppropriateCommand`. -No external mocks are required. Tests read from temporary YAML files written during the -test setup. +**Configuration_ReadFromFile_OsRegexOverride_SelectsAppropriateRegex**: A YAML file with +OS-specific regex overrides is loaded and the correct regex is selected for the current OS. +This scenario is tested by +`Configuration_ReadFromFile_OsRegexOverride_SelectsAppropriateRegex`. -### Requirements Coverage +**Configuration_ReadFromFile_EmptyTools_ThrowsArgumentException**: A YAML file with an +empty tools section throws `ArgumentException` during loading. This scenario is tested by +`Configuration_ReadFromFile_EmptyTools_ThrowsArgumentException`. -The following list maps Configuration subsystem requirements to test scenarios: +**Configuration_ReadFromFile_MissingFile_ThrowsArgumentException**: A path pointing to a +non-existent file throws `ArgumentException`. This scenario is tested by +`Configuration_ReadFromFile_MissingFile_ThrowsArgumentException`. -- **`VersionMark-Configuration-YamlConfig`**: `Configuration_ReadFromFile_MultipleTools_AllToolsAccessible` -- **`VersionMark-Configuration-ToolDefinition`**: `Configuration_ReadFromFile_MultipleTools_AllToolsAccessible` -- **`VersionMark-Configuration-OsCommandOverride`**: - `Configuration_ReadFromFile_WithOsOverrides_SelectsAppropriateCommand` -- **`VersionMark-Configuration-OsRegexOverride`**: - `Configuration_ReadFromFile_OsRegexOverride_SelectsAppropriateRegex` -- **`VersionMark-Configuration-ValidateTools`**: `Configuration_ReadFromFile_EmptyTools_ThrowsArgumentException` -- **`VersionMark-Configuration-ReadError`**: `Configuration_ReadFromFile_MissingFile_ThrowsArgumentException` -- **`VersionMark-Configuration-ParseError`**: `Configuration_ReadFromFile_InvalidYaml_ThrowsArgumentException` +**Configuration_ReadFromFile_InvalidYaml_ThrowsArgumentException**: A file containing +invalid YAML throws `ArgumentException` during loading. This scenario is tested by +`Configuration_ReadFromFile_InvalidYaml_ThrowsArgumentException`. diff --git a/docs/verification/version-mark/configuration/lint-issue.md b/docs/verification/version-mark/configuration/lint-issue.md index bc9a244..8d8ad88 100644 --- a/docs/verification/version-mark/configuration/lint-issue.md +++ b/docs/verification/version-mark/configuration/lint-issue.md @@ -1,35 +1,47 @@ -### LintIssue Unit Verification +### LintIssue -#### Overview +#### Verification Approach The `LintIssue` unit represents a single lint issue produced during configuration validation. The related `VersionMarkLoadResult` record holds the loaded configuration (or null on failure) together with all discovered issues. Tests are in -`Configuration/LintIssueTests.cs`. +`Configuration/LintIssueTests.cs` and use `StringWriter` to capture console and error +stream output. No file system access is required. -#### Test Scenarios +#### Test Environment + +N/A - standard test environment. All tests run using `dotnet test` with no additional +environment setup required. + +#### Acceptance Criteria -The following test scenarios verify `LintIssue` and `VersionMarkLoadResult`: +- All unit tests for `LintIssue` pass with zero failures across all supported OS and .NET + version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the `LintIssue` unit is covered by at least one named test + scenario. + +#### Test Scenarios -- **`LintIssue_Constructor_AllFields_AreStoredCorrectly`**: Constructor stores severity, file path, line, column, and message. -- **`LintIssue_ToString_Error_ProducesLowercaseSeverity`**: `ToString` for an error issue includes lowercase "error". -- **`LintIssue_ToString_Warning_ProducesLowercaseSeverity`**: `ToString` for a warning issue includes lowercase "warning". -- **`VersionMarkLoadResult_Constructor_AllFields_AreStoredCorrectly`**: Constructor stores config and issue list correctly. -- **`VersionMarkLoadResult_ReportIssues_Error_WritesToErrorStream`**: Error-severity issues are written to the error stream. -- **`VersionMarkLoadResult_ReportIssues_Warning_WritesToStdOut`**: Warning-severity issues are written to standard output. +**LintIssue_Constructor_AllFields_AreStoredCorrectly**: The constructor correctly stores +severity, file path, line, column, and message fields. This scenario is tested by +`LintIssue_Constructor_AllFields_AreStoredCorrectly`. -#### Dependencies +**LintIssue_ToString_Error_ProducesLowercaseSeverity**: `ToString` for an error issue +includes the lowercase string "error". This scenario is tested by +`LintIssue_ToString_Error_ProducesLowercaseSeverity`. -Tests use `StringWriter` to capture console and error stream output. +**LintIssue_ToString_Warning_ProducesLowercaseSeverity**: `ToString` for a warning issue +includes the lowercase string "warning". This scenario is tested by +`LintIssue_ToString_Warning_ProducesLowercaseSeverity`. -#### Requirements Coverage +**VersionMarkLoadResult_Constructor_AllFields_AreStoredCorrectly**: The constructor +correctly stores the config and issue list. This scenario is tested by +`VersionMarkLoadResult_Constructor_AllFields_AreStoredCorrectly`. -The following list maps `LintIssue` and `VersionMarkLoadResult` unit requirements to -test scenarios: +**VersionMarkLoadResult_ReportIssues_Error_WritesToErrorStream**: Error-severity issues are +written to the error stream. This scenario is tested by +`VersionMarkLoadResult_ReportIssues_Error_WritesToErrorStream`. -- **`VersionMark-Load-LintIssue-Fields`**: `LintIssue_Constructor_AllFields_AreStoredCorrectly` -- **`VersionMark-Load-LintIssue-Format`**: `LintIssue_ToString_Error_ProducesLowercaseSeverity`, - `LintIssue_ToString_Warning_ProducesLowercaseSeverity` -- **`VersionMark-Load-VersionMarkLoadResult`**: `VersionMarkLoadResult_Constructor_AllFields_AreStoredCorrectly`, - `VersionMarkLoadResult_ReportIssues_Error_WritesToErrorStream`, - `VersionMarkLoadResult_ReportIssues_Warning_WritesToStdOut` +**VersionMarkLoadResult_ReportIssues_Warning_WritesToStdOut**: Warning-severity issues are +written to standard output. This scenario is tested by +`VersionMarkLoadResult_ReportIssues_Warning_WritesToStdOut`. diff --git a/docs/verification/version-mark/configuration/tool-config.md b/docs/verification/version-mark/configuration/tool-config.md index a60ead7..a86ef39 100644 --- a/docs/verification/version-mark/configuration/tool-config.md +++ b/docs/verification/version-mark/configuration/tool-config.md @@ -1,54 +1,72 @@ -### ToolConfig Unit Verification +### ToolConfig -#### Overview +#### Verification Approach The `ToolConfig` unit holds the per-tool configuration record, including default command, default regex, and OS-specific overrides. It provides `GetEffectiveCommand` and `GetEffectiveRegex` methods that select the appropriate value for the current (or a -specified) operating system. Tests are in `Configuration/VersionMarkConfigTests.cs`. +specified) operating system. Tests are in `Configuration/VersionMarkConfigTests.cs` and +call methods directly on `ToolConfig` instances. No external mocks or file system access +is required. -#### Test Scenarios — GetEffectiveCommand +#### Test Environment -The following test scenarios verify `ToolConfig.GetEffectiveCommand`: +N/A - standard test environment. All tests run using `dotnet test` with no additional +environment setup required. -- **`ToolConfig_GetEffectiveCommand_NoOverride_ReturnsDefaultCommand`**: No OS override returns the default command. -- **`ToolConfig_GetEffectiveCommand_WithExplicitOs_ReturnsCorrectCommand`**: Explicit OS argument returns the matching override. -- **`ToolConfig_GetEffectiveCommand_WindowsOverride_ReturnsWindowsCommand`**: Windows override selected on Windows. -- **`ToolConfig_GetEffectiveCommand_LinuxOverride_ReturnsLinuxCommand`**: Linux override selected on Linux. -- **`ToolConfig_GetEffectiveCommand_MacOsOverride_ReturnsMacOsCommand`**: macOS override selected on macOS. -- **`ToolConfig_GetEffectiveCommand_NoDefaultKey_ThrowsInvalidOperationException`**: - No default key and no matching override throws InvalidOperationException. +#### Acceptance Criteria -#### Test Scenarios — GetEffectiveRegex +- All unit tests for `ToolConfig` pass with zero failures across all supported OS and .NET + version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the `ToolConfig` unit is covered by at least one named test + scenario. -The following test scenarios verify `ToolConfig.GetEffectiveRegex`: +#### Test Scenarios -- **`ToolConfig_GetEffectiveRegex_NoOverride_ReturnsDefaultRegex`**: No OS override returns the default regex. -- **`ToolConfig_GetEffectiveRegex_WithExplicitOs_ReturnsCorrectRegex`**: Explicit OS argument returns the matching override. -- **`ToolConfig_GetEffectiveRegex_WindowsOverride_ReturnsWindowsRegex`**: Windows override selected on Windows. -- **`ToolConfig_GetEffectiveRegex_LinuxOverride_ReturnsLinuxRegex`**: Linux override selected on Linux. -- **`ToolConfig_GetEffectiveRegex_MacOsOverride_ReturnsMacOsRegex`**: macOS override selected on macOS. -- **`ToolConfig_GetEffectiveRegex_NoDefaultKey_ThrowsInvalidOperationException`**: - No default key and no matching override throws InvalidOperationException. +**ToolConfig_GetEffectiveCommand_NoOverride_ReturnsDefaultCommand**: No OS override returns +the default command. This scenario is tested by +`ToolConfig_GetEffectiveCommand_NoOverride_ReturnsDefaultCommand`. -#### Dependencies +**ToolConfig_GetEffectiveCommand_WithExplicitOs_ReturnsCorrectCommand**: An explicit OS +argument returns the matching override command. This scenario is tested by +`ToolConfig_GetEffectiveCommand_WithExplicitOs_ReturnsCorrectCommand`. -No external mocks or file system access is required. Tests call methods directly on -`ToolConfig` instances. +**ToolConfig_GetEffectiveCommand_WindowsOverride_ReturnsWindowsCommand**: The Windows +override is selected when running on Windows. This scenario is tested by +`ToolConfig_GetEffectiveCommand_WindowsOverride_ReturnsWindowsCommand`. -#### Requirements Coverage +**ToolConfig_GetEffectiveCommand_LinuxOverride_ReturnsLinuxCommand**: The Linux override +is selected when running on Linux. This scenario is tested by +`ToolConfig_GetEffectiveCommand_LinuxOverride_ReturnsLinuxCommand`. -The following list maps `ToolConfig` unit requirements to test scenarios: +**ToolConfig_GetEffectiveCommand_MacOsOverride_ReturnsMacOsCommand**: The macOS override +is selected when running on macOS. This scenario is tested by +`ToolConfig_GetEffectiveCommand_MacOsOverride_ReturnsMacOsCommand`. -- **`VersionMark-ToolConfig-EffectiveCommand`**: `ToolConfig_GetEffectiveCommand_NoOverride_ReturnsDefaultCommand`, - `ToolConfig_GetEffectiveCommand_WithExplicitOs_ReturnsCorrectCommand`, - `ToolConfig_GetEffectiveCommand_WindowsOverride_ReturnsWindowsCommand`, - `ToolConfig_GetEffectiveCommand_LinuxOverride_ReturnsLinuxCommand`, - `ToolConfig_GetEffectiveCommand_MacOsOverride_ReturnsMacOsCommand`, - `ToolConfig_GetEffectiveCommand_NoDefaultKey_ThrowsInvalidOperationException` -- **`VersionMark-ToolConfig-EffectiveRegex`**: `ToolConfig_GetEffectiveRegex_NoOverride_ReturnsDefaultRegex`, - `ToolConfig_GetEffectiveRegex_WithExplicitOs_ReturnsCorrectRegex`, - `ToolConfig_GetEffectiveRegex_WindowsOverride_ReturnsWindowsRegex`, - `ToolConfig_GetEffectiveRegex_LinuxOverride_ReturnsLinuxRegex`, - `ToolConfig_GetEffectiveRegex_MacOsOverride_ReturnsMacOsRegex`, - `ToolConfig_GetEffectiveRegex_NoDefaultKey_ThrowsInvalidOperationException` +**ToolConfig_GetEffectiveCommand_NoDefaultKey_ThrowsInvalidOperationException**: No default +key and no matching OS override throws `InvalidOperationException`. This scenario is +tested by `ToolConfig_GetEffectiveCommand_NoDefaultKey_ThrowsInvalidOperationException`. + +**ToolConfig_GetEffectiveRegex_NoOverride_ReturnsDefaultRegex**: No OS override returns the +default regex. This scenario is tested by +`ToolConfig_GetEffectiveRegex_NoOverride_ReturnsDefaultRegex`. + +**ToolConfig_GetEffectiveRegex_WithExplicitOs_ReturnsCorrectRegex**: An explicit OS +argument returns the matching override regex. This scenario is tested by +`ToolConfig_GetEffectiveRegex_WithExplicitOs_ReturnsCorrectRegex`. + +**ToolConfig_GetEffectiveRegex_WindowsOverride_ReturnsWindowsRegex**: The Windows regex +override is selected when running on Windows. This scenario is tested by +`ToolConfig_GetEffectiveRegex_WindowsOverride_ReturnsWindowsRegex`. + +**ToolConfig_GetEffectiveRegex_LinuxOverride_ReturnsLinuxRegex**: The Linux regex override +is selected when running on Linux. This scenario is tested by +`ToolConfig_GetEffectiveRegex_LinuxOverride_ReturnsLinuxRegex`. + +**ToolConfig_GetEffectiveRegex_MacOsOverride_ReturnsMacOsRegex**: The macOS regex override +is selected when running on macOS. This scenario is tested by +`ToolConfig_GetEffectiveRegex_MacOsOverride_ReturnsMacOsRegex`. + +**ToolConfig_GetEffectiveRegex_NoDefaultKey_ThrowsInvalidOperationException**: No default +key and no matching OS override throws `InvalidOperationException`. This scenario is +tested by `ToolConfig_GetEffectiveRegex_NoDefaultKey_ThrowsInvalidOperationException`. diff --git a/docs/verification/version-mark/configuration/version-mark-config.md b/docs/verification/version-mark/configuration/version-mark-config.md index 571085b..b0b7449 100644 --- a/docs/verification/version-mark/configuration/version-mark-config.md +++ b/docs/verification/version-mark/configuration/version-mark-config.md @@ -1,111 +1,166 @@ -### VersionMarkConfig Unit Verification +### VersionMarkConfig -#### Overview +#### Verification Approach The `VersionMarkConfig` unit is the top-level configuration container. It reads and validates `.versionmark.yaml` files and provides `FindVersions` to execute tool commands and extract version strings. Tests are split across two files: `Configuration/VersionMarkConfigTests.cs` (covering `ReadFromFile` and `FindVersions`) -and `Configuration/VersionMarkConfigLoadTests.cs` (covering the `Load` method). - -#### Test Scenarios — ReadFromFile - -The following test scenarios verify `VersionMarkConfig.ReadFromFile`: - -- **`VersionMarkConfig_ReadFromFile_ValidFile_ReturnsConfig`**: Valid file returns a populated config object. -- **`VersionMarkConfig_ReadFromFile_WithAllOsOverrides_ReturnsConfig`**: File with all OS overrides returns config. -- **`VersionMarkConfig_ReadFromFile_NonExistentFile_ThrowsArgumentException`**: Non-existent file throws ArgumentException. -- **`VersionMarkConfig_ReadFromFile_InvalidYaml_ThrowsArgumentException`**: Invalid YAML throws ArgumentException. -- **`VersionMarkConfig_ReadFromFile_NoTools_ThrowsArgumentException`**: No tools section throws ArgumentException. - -#### Test Scenarios — FindVersions - -The following test scenarios verify `VersionMarkConfig.FindVersions`: - -- **`VersionMarkConfig_FindVersions_DotnetCommand_ReturnsVersionInfo`**: `dotnet --version` executes and returns version. -- **`VersionMarkConfig_FindVersions_MultipleTools_ReturnsAllVersions`**: Multiple tools returns all version entries. -- **`VersionMarkConfig_FindVersions_NonExistentTool_ThrowsArgumentException`**: Tool not in config throws ArgumentException. -- **`VersionMarkConfig_FindVersions_InvalidCommand_ThrowsInvalidOperationException`**: - Invalid command throws InvalidOperationException. -- **`VersionMarkConfig_FindVersions_RegexNoMatch_ThrowsInvalidOperationException`**: - No regex match throws InvalidOperationException. -- **`VersionMarkConfig_FindVersions_RegexNoVersionGroup_ThrowsInvalidOperationException`**: - Missing version group throws InvalidOperationException. -- **`VersionMarkConfig_FindVersions_OsOnlyCommand_MatchingOs_ReturnsVersionInfo`**: - Tool with only an OS-specific command succeeds when the matching OS is specified. -- **`VersionMarkConfig_FindVersions_OsOnlyCommand_WrongOs_ThrowsInvalidOperationException`**: - Tool with only an OS-specific command throws when a non-matching OS is specified. - -#### Test Scenarios — Load - -The following test scenarios verify `VersionMarkConfig.Load`: - -- **`VersionMarkConfig_Load_ValidConfig_ReturnsConfig`**: Valid config returns a config with no issues. -- **`VersionMarkConfig_Load_MissingFile_ReturnsNullConfig`**: Missing file returns a null config with an issue. -- **`VersionMarkConfig_Load_InvalidYaml_ReturnsNullConfig`**: Invalid YAML returns a null config with an issue. -- **`VersionMarkConfig_Load_MissingToolsSection_ReturnsNullConfig`**: Missing tools section returns a null config. -- **`VersionMarkConfig_Load_EmptyToolsSection_ReturnsNullConfig`**: Empty tools section returns a null config. -- **`VersionMarkConfig_Load_MissingCommand_ReturnsNullConfig`**: Missing command returns a null config with an issue. -- **`VersionMarkConfig_Load_EmptyCommand_ReturnsNullConfig`**: Empty command returns a null config with an issue. -- **`VersionMarkConfig_Load_MissingRegex_ReturnsNullConfig`**: Missing regex returns a null config with an issue. -- **`VersionMarkConfig_Load_EmptyRegex_ReturnsNullConfig`**: Empty regex returns a null config with an issue. -- **`VersionMarkConfig_Load_InvalidRegex_ReturnsNullConfig`**: Invalid regex returns a null config with an issue. -- **`VersionMarkConfig_Load_RegexMissingVersionGroup_ReturnsNullConfig`**: Regex without version group returns a null config. -- **`VersionMarkConfig_Load_UnknownTopLevelKey_ReturnsConfig`**: Unknown top-level key is tolerated; config is returned. -- **`VersionMarkConfig_Load_UnknownToolKey_ReturnsConfig`**: Unknown tool-level key is tolerated; config is returned. -- **`VersionMarkConfig_Load_OsSpecificEmptyCommand_ReturnsNullConfig`**: Empty OS-specific command returns a null config. -- **`VersionMarkConfig_Load_OsSpecificEmptyRegex_ReturnsNullConfig`**: Empty OS-specific regex returns a null config. -- **`VersionMarkConfig_Load_OsSpecificRegexMissingVersionGroup_ReturnsNullConfig`**: - OS-specific regex without version group returns null. -- **`VersionMarkConfig_Load_OsSpecificInvalidRegex_ReturnsNullConfig`**: OS-specific invalid regex returns a null config. -- **`VersionMarkConfig_Load_OsOnlyCommand_ReturnsConfig`**: Tool with only OS-specific commands - (no default command) is valid. -- **`VersionMarkConfig_Load_OsOnlyRegex_ReturnsConfig`**: Tool with only OS-specific regex (no default regex) is valid. -- **`VersionMarkConfig_Load_OsOnlyCommandAndRegex_ReturnsConfig`**: Tool with only OS-specific commands and regex is valid. -- **`VersionMarkConfig_Load_MultipleErrors_ReportsAll`**: Multiple errors are all reported in the issue list. -- **`VersionMarkConfig_Load_IssuesContainFilePath`**: Issue records include the config file path. - -#### Dependencies - -No external mocks are required. Tests write temporary YAML files to disk. - -#### Requirements Coverage - -The following list maps `VersionMarkConfig` unit requirements to test scenarios: - -- **`VersionMark-VersionMarkConfig-ReadFromFile`**: `VersionMarkConfig_ReadFromFile_ValidFile_ReturnsConfig`, - `VersionMarkConfig_ReadFromFile_WithAllOsOverrides_ReturnsConfig`, - `VersionMarkConfig_ReadFromFile_NonExistentFile_ThrowsArgumentException`, - `VersionMarkConfig_ReadFromFile_InvalidYaml_ThrowsArgumentException`, - `VersionMarkConfig_ReadFromFile_NoTools_ThrowsArgumentException` -- **`VersionMark-VersionMarkConfig-FindVersions`**: `VersionMarkConfig_FindVersions_DotnetCommand_ReturnsVersionInfo`, - `VersionMarkConfig_FindVersions_MultipleTools_ReturnsAllVersions`, - `VersionMarkConfig_FindVersions_NonExistentTool_ThrowsArgumentException`, - `VersionMarkConfig_FindVersions_InvalidCommand_ThrowsInvalidOperationException`, - `VersionMarkConfig_FindVersions_RegexNoMatch_ThrowsInvalidOperationException`, - `VersionMarkConfig_FindVersions_RegexNoVersionGroup_ThrowsInvalidOperationException`, - `VersionMarkConfig_FindVersions_OsOnlyCommand_MatchingOs_ReturnsVersionInfo`, - `VersionMarkConfig_FindVersions_OsOnlyCommand_WrongOs_ThrowsInvalidOperationException` -- **`VersionMark-VersionMarkConfig-Load`**: All `VersionMarkConfig_Load_*` test scenarios above -- **`VersionMark-Load-Method`**: `VersionMarkConfig_Load_ValidConfig_ReturnsConfig` -- **`VersionMark-Load-FileExistence`**: `VersionMarkConfig_Load_MissingFile_ReturnsNullConfig` -- **`VersionMark-Load-YamlParsing`**: `VersionMarkConfig_Load_InvalidYaml_ReturnsNullConfig` -- **`VersionMark-Load-ToolsSection`**: `VersionMarkConfig_Load_MissingToolsSection_ReturnsNullConfig`, - `VersionMarkConfig_Load_EmptyToolsSection_ReturnsNullConfig` -- **`VersionMark-Load-ToolCommand`**: `VersionMarkConfig_Load_MissingCommand_ReturnsNullConfig`, - `VersionMarkConfig_Load_EmptyCommand_ReturnsNullConfig` -- **`VersionMark-Load-ToolRegex`**: `VersionMarkConfig_Load_MissingRegex_ReturnsNullConfig`, - `VersionMarkConfig_Load_EmptyRegex_ReturnsNullConfig` -- **`VersionMark-Load-RegexValid`**: `VersionMarkConfig_Load_InvalidRegex_ReturnsNullConfig` -- **`VersionMark-Load-RegexVersion`**: `VersionMarkConfig_Load_RegexMissingVersionGroup_ReturnsNullConfig` -- **`VersionMark-Load-OsOverrides`**: `VersionMarkConfig_Load_OsSpecificEmptyCommand_ReturnsNullConfig`, - `VersionMarkConfig_Load_OsSpecificEmptyRegex_ReturnsNullConfig`, - `VersionMarkConfig_Load_OsSpecificRegexMissingVersionGroup_ReturnsNullConfig`, - `VersionMarkConfig_Load_OsSpecificInvalidRegex_ReturnsNullConfig`, - `VersionMarkConfig_Load_OsOnlyCommand_ReturnsConfig`, - `VersionMarkConfig_Load_OsOnlyRegex_ReturnsConfig`, - `VersionMarkConfig_Load_OsOnlyCommandAndRegex_ReturnsConfig` -- **`VersionMark-Load-UnknownKeys`**: `VersionMarkConfig_Load_UnknownTopLevelKey_ReturnsConfig`, - `VersionMarkConfig_Load_UnknownToolKey_ReturnsConfig` -- **`VersionMark-Load-ErrorLocation`**: `VersionMarkConfig_Load_IssuesContainFilePath` -- **`VersionMark-Load-AllIssues`**: `VersionMarkConfig_Load_MultipleErrors_ReportsAll` +and `Configuration/VersionMarkConfigLoadTests.cs` (covering the `Load` method). Tests +write temporary YAML files to disk and call the relevant method, then assert on the returned +configuration object or exception. No external mocks are required. + +#### Test Environment + +N/A - standard test environment. Tests write temporary YAML files to disk during setup and +clean them up afterwards. No additional environment configuration is required. + +#### Acceptance Criteria + +- All unit tests for `VersionMarkConfig` pass with zero failures across all supported OS + and .NET version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the `VersionMarkConfig` unit is covered by at least one named test + scenario. + +#### Test Scenarios + +**VersionMarkConfig_ReadFromFile_ValidFile_ReturnsConfig**: A valid YAML file returns a +populated config object. This scenario is tested by +`VersionMarkConfig_ReadFromFile_ValidFile_ReturnsConfig`. + +**VersionMarkConfig_ReadFromFile_WithAllOsOverrides_ReturnsConfig**: A file with all OS +overrides returns a config with the correct override values. This scenario is tested by +`VersionMarkConfig_ReadFromFile_WithAllOsOverrides_ReturnsConfig`. + +**VersionMarkConfig_ReadFromFile_NonExistentFile_ThrowsArgumentException**: A path to a +non-existent file throws `ArgumentException`. This scenario is tested by +`VersionMarkConfig_ReadFromFile_NonExistentFile_ThrowsArgumentException`. + +**VersionMarkConfig_ReadFromFile_InvalidYaml_ThrowsArgumentException**: A file containing +invalid YAML throws `ArgumentException`. This scenario is tested by +`VersionMarkConfig_ReadFromFile_InvalidYaml_ThrowsArgumentException`. + +**VersionMarkConfig_ReadFromFile_NoTools_ThrowsArgumentException**: A YAML file with no +tools section throws `ArgumentException`. This scenario is tested by +`VersionMarkConfig_ReadFromFile_NoTools_ThrowsArgumentException`. + +**VersionMarkConfig_FindVersions_DotnetCommand_ReturnsVersionInfo**: `dotnet --version` +executes and returns a `VersionInfo` entry. This scenario is tested by +`VersionMarkConfig_FindVersions_DotnetCommand_ReturnsVersionInfo`. + +**VersionMarkConfig_FindVersions_MultipleTools_ReturnsAllVersions**: Multiple tools in +the config return all version entries. This scenario is tested by +`VersionMarkConfig_FindVersions_MultipleTools_ReturnsAllVersions`. + +**VersionMarkConfig_FindVersions_NonExistentTool_ThrowsArgumentException**: A tool name +not present in the config throws `ArgumentException`. This scenario is tested by +`VersionMarkConfig_FindVersions_NonExistentTool_ThrowsArgumentException`. + +**VersionMarkConfig_FindVersions_InvalidCommand_ThrowsInvalidOperationException**: An +invalid command throws `InvalidOperationException`. This scenario is tested by +`VersionMarkConfig_FindVersions_InvalidCommand_ThrowsInvalidOperationException`. + +**VersionMarkConfig_FindVersions_RegexNoMatch_ThrowsInvalidOperationException**: A regex +that does not match the command output throws `InvalidOperationException`. This scenario +is tested by `VersionMarkConfig_FindVersions_RegexNoMatch_ThrowsInvalidOperationException`. + +**VersionMarkConfig_FindVersions_RegexNoVersionGroup_ThrowsInvalidOperationException**: A +regex without a named version group throws `InvalidOperationException`. This scenario is +tested by +`VersionMarkConfig_FindVersions_RegexNoVersionGroup_ThrowsInvalidOperationException`. + +**VersionMarkConfig_FindVersions_OsOnlyCommand_MatchingOs_ReturnsVersionInfo**: A tool +with only an OS-specific command succeeds when the matching OS is specified. This scenario +is tested by +`VersionMarkConfig_FindVersions_OsOnlyCommand_MatchingOs_ReturnsVersionInfo`. + +**VersionMarkConfig_FindVersions_OsOnlyCommand_WrongOs_ThrowsInvalidOperationException**: A +tool with only an OS-specific command throws `InvalidOperationException` when a +non-matching OS is specified. This scenario is tested by +`VersionMarkConfig_FindVersions_OsOnlyCommand_WrongOs_ThrowsInvalidOperationException`. + +**VersionMarkConfig_Load_ValidConfig_ReturnsConfig**: A valid config file returns a config +with no issues. This scenario is tested by `VersionMarkConfig_Load_ValidConfig_ReturnsConfig`. + +**VersionMarkConfig_Load_MissingFile_ReturnsNullConfig**: A missing file returns a null +config with an issue. This scenario is tested by +`VersionMarkConfig_Load_MissingFile_ReturnsNullConfig`. + +**VersionMarkConfig_Load_InvalidYaml_ReturnsNullConfig**: Invalid YAML returns a null +config with an issue. This scenario is tested by +`VersionMarkConfig_Load_InvalidYaml_ReturnsNullConfig`. + +**VersionMarkConfig_Load_MissingToolsSection_ReturnsNullConfig**: A missing tools section +returns a null config. This scenario is tested by +`VersionMarkConfig_Load_MissingToolsSection_ReturnsNullConfig`. + +**VersionMarkConfig_Load_EmptyToolsSection_ReturnsNullConfig**: An empty tools section +returns a null config. This scenario is tested by +`VersionMarkConfig_Load_EmptyToolsSection_ReturnsNullConfig`. + +**VersionMarkConfig_Load_MissingCommand_ReturnsNullConfig**: A missing command field +returns a null config with an issue. This scenario is tested by +`VersionMarkConfig_Load_MissingCommand_ReturnsNullConfig`. + +**VersionMarkConfig_Load_EmptyCommand_ReturnsNullConfig**: An empty command field returns +a null config with an issue. This scenario is tested by +`VersionMarkConfig_Load_EmptyCommand_ReturnsNullConfig`. + +**VersionMarkConfig_Load_MissingRegex_ReturnsNullConfig**: A missing regex field returns +a null config with an issue. This scenario is tested by +`VersionMarkConfig_Load_MissingRegex_ReturnsNullConfig`. + +**VersionMarkConfig_Load_EmptyRegex_ReturnsNullConfig**: An empty regex field returns a +null config with an issue. This scenario is tested by +`VersionMarkConfig_Load_EmptyRegex_ReturnsNullConfig`. + +**VersionMarkConfig_Load_InvalidRegex_ReturnsNullConfig**: An invalid regex returns a null +config with an issue. This scenario is tested by +`VersionMarkConfig_Load_InvalidRegex_ReturnsNullConfig`. + +**VersionMarkConfig_Load_RegexMissingVersionGroup_ReturnsNullConfig**: A regex without a +version capture group returns a null config. This scenario is tested by +`VersionMarkConfig_Load_RegexMissingVersionGroup_ReturnsNullConfig`. + +**VersionMarkConfig_Load_UnknownTopLevelKey_ReturnsConfig**: An unknown top-level key is +tolerated and the config is returned. This scenario is tested by +`VersionMarkConfig_Load_UnknownTopLevelKey_ReturnsConfig`. + +**VersionMarkConfig_Load_UnknownToolKey_ReturnsConfig**: An unknown tool-level key is +tolerated and the config is returned. This scenario is tested by +`VersionMarkConfig_Load_UnknownToolKey_ReturnsConfig`. + +**VersionMarkConfig_Load_OsSpecificEmptyCommand_ReturnsNullConfig**: An empty OS-specific +command field returns a null config. This scenario is tested by +`VersionMarkConfig_Load_OsSpecificEmptyCommand_ReturnsNullConfig`. + +**VersionMarkConfig_Load_OsSpecificEmptyRegex_ReturnsNullConfig**: An empty OS-specific +regex field returns a null config. This scenario is tested by +`VersionMarkConfig_Load_OsSpecificEmptyRegex_ReturnsNullConfig`. + +**VersionMarkConfig_Load_OsSpecificRegexMissingVersionGroup_ReturnsNullConfig**: An +OS-specific regex without a version group returns a null config. This scenario is tested by +`VersionMarkConfig_Load_OsSpecificRegexMissingVersionGroup_ReturnsNullConfig`. + +**VersionMarkConfig_Load_OsSpecificInvalidRegex_ReturnsNullConfig**: An OS-specific invalid +regex returns a null config. This scenario is tested by +`VersionMarkConfig_Load_OsSpecificInvalidRegex_ReturnsNullConfig`. + +**VersionMarkConfig_Load_OsOnlyCommand_ReturnsConfig**: A tool with only OS-specific +commands (no default command) is valid and returns a config. This scenario is tested by +`VersionMarkConfig_Load_OsOnlyCommand_ReturnsConfig`. + +**VersionMarkConfig_Load_OsOnlyRegex_ReturnsConfig**: A tool with only OS-specific regex +(no default regex) is valid and returns a config. This scenario is tested by +`VersionMarkConfig_Load_OsOnlyRegex_ReturnsConfig`. + +**VersionMarkConfig_Load_OsOnlyCommandAndRegex_ReturnsConfig**: A tool with only +OS-specific commands and regex (no defaults) is valid and returns a config. This scenario +is tested by `VersionMarkConfig_Load_OsOnlyCommandAndRegex_ReturnsConfig`. + +**VersionMarkConfig_Load_MultipleErrors_ReportsAll**: Multiple validation errors are all +reported in the issue list. This scenario is tested by +`VersionMarkConfig_Load_MultipleErrors_ReportsAll`. + +**VersionMarkConfig_Load_IssuesContainFilePath**: Issue records include the config file +path. This scenario is tested by `VersionMarkConfig_Load_IssuesContainFilePath`. diff --git a/docs/verification/version-mark/publishing.md b/docs/verification/version-mark/publishing.md index 9e9b817..050fed2 100644 --- a/docs/verification/version-mark/publishing.md +++ b/docs/verification/version-mark/publishing.md @@ -1,53 +1,65 @@ -## Publishing Subsystem Verification +## Publishing -### Overview +### Verification Approach The Publishing subsystem is responsible for reading captured version JSON files and generating a consolidated markdown report. It consists of one unit: `MarkdownFormatter` -(the version report formatter). +(the version report formatter). Subsystem-level integration tests are in +`Publishing/PublishingTests.cs` and cover the full publish workflow including glob +pattern resolution, JSON file loading, report generation, and error handling. Tests use +temporary directories containing pre-built JSON capture files. No external mocks are +required. -Subsystem-level integration tests are in `Publishing/PublishingTests.cs` and cover the -full publish workflow including glob pattern resolution, JSON file loading, report -generation, and error handling. Unit-level verification for `MarkdownFormatter` is in the -chapter that follows. +### Test Environment -### Verification Approach +N/A - standard test environment. Tests create temporary directories and pre-built JSON +capture files during setup and clean them up afterwards. + +### Acceptance Criteria -Integration tests use temporary directories containing pre-built JSON capture files. -Tests invoke publish operations via `Program.RunPublish` and assert on the contents of -the generated report or the error output. No external mocks are required. +- All subsystem integration tests pass with zero failures across all supported OS and .NET + version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the Publishing subsystem is covered by at least one named test + scenario. ### Test Scenarios -The following integration test scenarios verify Publishing subsystem requirements: - -- **`Publishing_Format_MultipleCaptureFiles_ProducesConsolidatedReport`**: Multiple files produce a consolidated report. -- **`Publishing_Format_IdenticalVersionsAcrossJobs_ConsolidatesVersions`**: Identical versions across jobs are consolidated. -- **`Publishing_Format_ConflictingVersions_ShowsJobIds`**: Conflicting versions show individual job IDs. -- **`Publishing_Format_WithCustomDepth_UsesCorrectHeadingLevel`**: Custom depth produces the correct heading level. -- **`Publishing_Run_WithoutReport_ReportsError`**: Missing `--report` flag reports an error. -- **`Publishing_Run_WithGlobPattern_ReadsMatchingFiles`**: Glob pattern reads all matching files. -- **`Publishing_Run_WithGlobPatternMatchingNoFiles_ReportsError`**: No matching files reports an error. -- **`Publishing_Run_WithMalformedJsonFile_ReportsError`**: Malformed JSON file reports an error. -- **`Publishing_Run_WithReportDepth_UsesCorrectDepth`**: Report depth flag is applied to heading levels. - -### Dependencies - -No external mocks are required. Tests use temporary directories and pre-built JSON -capture files created during test setup. - -### Requirements Coverage - -The following list maps Publishing subsystem requirements to test scenarios: - -- **`VersionMark-Publish-Publish`**: `Publishing_Format_MultipleCaptureFiles_ProducesConsolidatedReport` -- **`VersionMark-Publish-Report`**: `Publishing_Format_MultipleCaptureFiles_ProducesConsolidatedReport` -- **`VersionMark-Publish-ReportDepth`**: `Publishing_Format_WithCustomDepth_UsesCorrectHeadingLevel`, - `Publishing_Run_WithReportDepth_UsesCorrectDepth` -- **`VersionMark-Publish-RequireReport`**: `Publishing_Run_WithoutReport_ReportsError` -- **`VersionMark-Publish-GlobPattern`**: `Publishing_Run_WithGlobPattern_ReadsMatchingFiles` -- **`VersionMark-Publish-Consolidate`**: `Publishing_Format_MultipleCaptureFiles_ProducesConsolidatedReport`, - `Publishing_Format_IdenticalVersionsAcrossJobs_ConsolidatesVersions` -- **`VersionMark-Publish-ConflictReport`**: `Publishing_Run_WithGlobPatternMatchingNoFiles_ReportsError` -- **`VersionMark-Publish-ConflictDisplay`**: `Publishing_Format_ConflictingVersions_ShowsJobIds` -- **`VersionMark-Publish-FileError`**: `Publishing_Run_WithMalformedJsonFile_ReportsError` +**Publishing_Format_MultipleCaptureFiles_ProducesConsolidatedReport**: Multiple capture +files produce a single consolidated report. This scenario is tested by +`Publishing_Format_MultipleCaptureFiles_ProducesConsolidatedReport`. + +**Publishing_Format_IdenticalVersionsAcrossJobs_ConsolidatesVersions**: Identical versions +across multiple jobs are consolidated into a single line. This scenario is tested by +`Publishing_Format_IdenticalVersionsAcrossJobs_ConsolidatesVersions`. + +**Publishing_Format_ConflictingVersions_ShowsJobIds**: Conflicting versions across jobs +show individual job IDs in the report. This scenario is tested by +`Publishing_Format_ConflictingVersions_ShowsJobIds`. + +**Publishing_Format_WithCustomDepth_UsesCorrectHeadingLevel**: A custom depth value +produces the correct markdown heading level. This scenario is tested by +`Publishing_Format_WithCustomDepth_UsesCorrectHeadingLevel`. + +**Publishing_Run_WithoutReport_ReportsError**: Running publish without the `--report` +flag reports an error. This scenario is tested by +`Publishing_Run_WithoutReport_ReportsError`. + +**Publishing_Run_WithGlobPattern_ReadsMatchingFiles**: A glob pattern reads all matching +capture files. This scenario is tested by +`Publishing_Run_WithGlobPattern_ReadsMatchingFiles`. + +**Publishing_Run_WithGlobPatternMatchingNoFiles_ReportsError**: A glob pattern that matches +no files reports an error. This scenario is tested by +`Publishing_Run_WithGlobPatternMatchingNoFiles_ReportsError`. + +**Publishing_Run_WithMalformedJsonFile_ReportsError**: A malformed JSON capture file +reports an error. This scenario is tested by +`Publishing_Run_WithMalformedJsonFile_ReportsError`. + +**Publishing_Run_WithReportDepth_UsesCorrectDepth**: The `--report-depth` flag is applied +to the heading level in the generated report. This scenario is tested by +`Publishing_Run_WithReportDepth_UsesCorrectDepth`. + +**Publishing_Run_WithAbsoluteGlobPattern_ReadsMatchingFiles**: An absolute glob pattern +reads all matching capture files even when the working directory differs. This scenario is +tested by `Publishing_Run_WithAbsoluteGlobPattern_ReadsMatchingFiles`. diff --git a/docs/verification/version-mark/publishing/markdown-formatter.md b/docs/verification/version-mark/publishing/markdown-formatter.md index b00fd0c..641a1b0 100644 --- a/docs/verification/version-mark/publishing/markdown-formatter.md +++ b/docs/verification/version-mark/publishing/markdown-formatter.md @@ -1,60 +1,78 @@ -### MarkdownFormatter Unit Verification +### MarkdownFormatter -#### Overview +#### Verification Approach The `MarkdownFormatter` unit generates consolidated markdown version reports from a collection of `VersionInfo` records. It sorts tools and job IDs alphabetically, collapses uniform versions across jobs into a single line, and uses the configured heading depth for -section headers. Tests are in `Publishing/MarkdownFormatterTests.cs`. +section headers. Tests are in `Publishing/MarkdownFormatterTests.cs` and call +`MarkdownFormatter.Format` directly with constructed lists of `VersionInfo` objects. No +external mocks or file system access is required. + +#### Test Environment + +N/A - standard test environment. All tests run using `dotnet test` with no additional +environment setup required. + +#### Acceptance Criteria + +- All unit tests for `MarkdownFormatter` pass with zero failures across all supported OS + and .NET version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the `MarkdownFormatter` unit is covered by at least one named + test scenario. #### Test Scenarios -The following test scenarios verify `MarkdownFormatter`: - -- **`MarkdownFormatter_FormatVersions_SortsToolsAlphabetically`**: Tools appear in alphabetical order in the report. -- **`MarkdownFormatter_FormatVersions_WithUniformVersions_ShowsVersionOnly`**: - Same version across all jobs shows version without job IDs. -- **`MarkdownFormatter_FormatVersions_WithDifferentVersions_ShowsIndividualJobs`**: - Different versions across jobs show each job ID and version. -- **`MarkdownFormatter_FormatVersions_WithCustomDepth_UsesCorrectHeadingLevel`**: - Custom depth produces the correct Markdown heading level. -- **`MarkdownFormatter_FormatVersions_EmptyList_ProducesHeaderOnly`**: Empty input list produces a header with no tool entries. -- **`MarkdownFormatter_FormatVersions_SingleJob_ShowsAllJobs`**: Single job shows all version entries. -- **`MarkdownFormatter_FormatVersions_MixedVersions_HandlesCorrectly`**: - Mix of uniform and differing versions is handled correctly. -- **`MarkdownFormatter_FormatVersions_SortsJobIdsAlphabetically`**: Job IDs appear in alphabetical order within a tool entry. -- **`MarkdownFormatter_FormatVersions_WithSpecialCharacters_PreservesVersions`**: - Version strings with special characters are preserved. -- **`MarkdownFormatter_FormatVersions_CaseInsensitiveSorting`**: Alphabetical sorting is case-insensitive. -- **`MarkdownFormatter_FormatVersions_SortsVersionsAlphabetically`**: - Version strings are sorted alphabetically within a tool entry. -- **`MarkdownFormatter_Format_WithZeroDepth_ThrowsArgumentOutOfRangeException`**: - A depth of zero throws ArgumentOutOfRangeException. -- **`MarkdownFormatter_Format_WithPartialToolCoverage_ShowsAllContributingTools`**: - Partial tool coverage across jobs shows all contributing tools. - -#### Dependencies - -No external mocks or file system access is required. Tests call `MarkdownFormatter.Format` -directly with constructed lists of `VersionInfo` objects. - -#### Requirements Coverage - -The following list maps `MarkdownFormatter` unit requirements to test scenarios: - -- **`VersionMark-Formatter-Structure`**: `MarkdownFormatter_FormatVersions_SortsToolsAlphabetically`, - `MarkdownFormatter_FormatVersions_EmptyList_ProducesHeaderOnly`, - `MarkdownFormatter_FormatVersions_SortsJobIdsAlphabetically`, - `MarkdownFormatter_FormatVersions_CaseInsensitiveSorting`, - `MarkdownFormatter_FormatVersions_SortsVersionsAlphabetically`, - `MarkdownFormatter_Format_WithPartialToolCoverage_ShowsAllContributingTools` -- **`VersionMark-Formatter-JobId`**: `MarkdownFormatter_FormatVersions_WithUniformVersions_ShowsVersionOnly`, - `MarkdownFormatter_FormatVersions_MixedVersions_HandlesCorrectly` -- **`VersionMark-Formatter-Versions`**: `MarkdownFormatter_FormatVersions_WithDifferentVersions_ShowsIndividualJobs`, - `MarkdownFormatter_FormatVersions_SingleJob_ShowsAllJobs`, - `MarkdownFormatter_FormatVersions_MixedVersions_HandlesCorrectly`, - `MarkdownFormatter_FormatVersions_WithSpecialCharacters_PreservesVersions` -- **`VersionMark-Formatter-MarkdownList`**: `MarkdownFormatter_FormatVersions_WithDifferentVersions_ShowsIndividualJobs` -- **`VersionMark-Formatter-MarkdownConsolidation`**: - `MarkdownFormatter_FormatVersions_WithCustomDepth_UsesCorrectHeadingLevel`, - `MarkdownFormatter_Format_WithZeroDepth_ThrowsArgumentOutOfRangeException` +**MarkdownFormatter_Format_SortsToolsAlphabetically**: Tools appear in +alphabetical order in the generated report. This scenario is tested by +`MarkdownFormatter_Format_SortsToolsAlphabetically`. + +**MarkdownFormatter_Format_WithUniformVersions_ShowsVersionOnly**: When all jobs +report the same version for a tool, the version is shown without job IDs. This scenario is +tested by `MarkdownFormatter_Format_WithUniformVersions_ShowsVersionOnly`. + +**MarkdownFormatter_Format_WithDifferentVersions_ShowsIndividualJobs**: When jobs +report different versions for a tool, each job ID and version is shown individually. This +scenario is tested by +`MarkdownFormatter_Format_WithDifferentVersions_ShowsIndividualJobs`. + +**MarkdownFormatter_Format_WithCustomDepth_UsesCorrectHeadingLevel**: A custom +depth value produces the correct markdown heading level. This scenario is tested by +`MarkdownFormatter_Format_WithCustomDepth_UsesCorrectHeadingLevel`. + +**MarkdownFormatter_Format_EmptyList_ProducesHeaderOnly**: An empty input list +produces a header with no tool entries. This scenario is tested by +`MarkdownFormatter_Format_EmptyList_ProducesHeaderOnly`. + +**MarkdownFormatter_Format_SingleJob_SuppressesJobId**: A single job suppresses +the job ID and shows only the version. This scenario is tested by +`MarkdownFormatter_Format_SingleJob_SuppressesJobId`. + +**MarkdownFormatter_Format_MixedVersions_HandlesCorrectly**: A mix of uniform and +differing versions across tools is handled correctly. This scenario is tested by +`MarkdownFormatter_Format_MixedVersions_HandlesCorrectly`. + +**MarkdownFormatter_Format_SortsJobIdsAlphabetically**: Job IDs appear in +alphabetical order within each tool entry. This scenario is tested by +`MarkdownFormatter_Format_SortsJobIdsAlphabetically`. + +**MarkdownFormatter_Format_WithSpecialCharacters_PreservesVersions**: Version +strings with special characters are preserved in the report. This scenario is tested by +`MarkdownFormatter_Format_WithSpecialCharacters_PreservesVersions`. + +**MarkdownFormatter_Format_CaseInsensitiveSorting**: Alphabetical sorting of tools +and job IDs is case-insensitive. This scenario is tested by +`MarkdownFormatter_Format_CaseInsensitiveSorting`. + +**MarkdownFormatter_Format_SortsVersionsAlphabetically**: Version strings within a +tool entry are sorted alphabetically. This scenario is tested by +`MarkdownFormatter_Format_SortsVersionsAlphabetically`. + +**MarkdownFormatter_Format_WithZeroDepth_ThrowsArgumentOutOfRangeException**: A depth of +zero throws `ArgumentOutOfRangeException`. This scenario is tested by +`MarkdownFormatter_Format_WithZeroDepth_ThrowsArgumentOutOfRangeException`. + +**MarkdownFormatter_Format_WithPartialToolCoverage_ShowsAllContributingTools**: Partial +tool coverage across jobs (where not every job reports every tool) shows all contributing +tools. This scenario is tested by +`MarkdownFormatter_Format_WithPartialToolCoverage_ShowsAllContributingTools`. diff --git a/docs/verification/version-mark/self-test.md b/docs/verification/version-mark/self-test.md index 8bc1785..3189dbe 100644 --- a/docs/verification/version-mark/self-test.md +++ b/docs/verification/version-mark/self-test.md @@ -1,39 +1,50 @@ -## SelfTest Subsystem Verification +## SelfTest -### Overview +### Verification Approach The SelfTest subsystem provides built-in self-validation for the VersionMark tool. It -consists of one unit: `Validation` (the self-validation test runner). +consists of one unit: `Validation` (the self-validation test runner). Subsystem-level +integration tests are in `SelfTest/SelfTestTests.cs` and cover the full self-validation +workflow including TRX and JUnit results file writing and heading depth handling. Tests use +a temporary directory for results files. No external mocks are required. -Subsystem-level integration tests are in `SelfTest/SelfTestTests.cs` and cover the full -self-validation workflow including TRX/JUnit results file writing and heading depth -handling. Unit-level verification for `Validation` is in the chapter that follows. +### Test Environment -### Verification Approach +N/A - standard test environment. Tests create temporary directories during setup and clean +them up afterwards. + +### Acceptance Criteria -Integration tests invoke the SelfTest subsystem with various flag combinations and assert -on the written results files and output. Tests use a temporary directory for results -files. No external mocks are required. +- All subsystem integration tests pass with zero failures across all supported OS and .NET + version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the SelfTest subsystem is covered by at least one named test + scenario. ### Test Scenarios -The following integration test scenarios verify SelfTest subsystem requirements: +**SelfTest_Run_WithResultsFlag_WritesResultsFile**: The `--results` flag causes a TRX +results file to be written at the specified path. This scenario is tested by +`SelfTest_Run_WithResultsFlag_WritesResultsFile`. -- **`SelfTest_Run_WithResultsFlag_WritesResultsFile`**: `--results` flag writes a TRX results file. -- **`SelfTest_Run_WithResultsXmlFlag_WritesJUnitResultsFile`**: `--results-xml` flag writes a JUnit results file. -- **`SelfTest_Run_WithDepthTwo_WritesHashHashHeader`**: Depth 2 produces a `##` heading in the output. +**SelfTest_Run_WithResultsXmlFlag_WritesJUnitResultsFile**: The `--results-xml` flag +causes a JUnit-format results file to be written at the specified path. This scenario is +tested by `SelfTest_Run_WithResultsXmlFlag_WritesJUnitResultsFile`. -### Dependencies +**SelfTest_Run_WithDepthTwo_WritesHashHashHeader**: Running with depth 2 produces a `##` +heading level in the output. This scenario is tested by +`SelfTest_Run_WithDepthTwo_WritesHashHashHeader`. -Tests use a temporary directory for results files. No external mocks are required. +**SelfTest_Run_Capture_CapturesToolVersions**: The capture sub-test within self-validation +runs successfully. This scenario is tested by `SelfTest_Run_Capture_CapturesToolVersions`. -### Requirements Coverage +**SelfTest_Run_Publish_GeneratesMarkdownReport**: The publish sub-test within +self-validation runs successfully. This scenario is tested by +`SelfTest_Run_Publish_GeneratesMarkdownReport`. -The following list maps SelfTest subsystem requirements to test scenarios: +**SelfTest_Run_LintValid_PassesForValidConfig**: The lint-valid sub-test within +self-validation accepts a valid configuration with exit code 0. This scenario is tested by +`SelfTest_Run_LintValid_PassesForValidConfig`. -- **`VersionMark-Validate-Capture`**: `SelfTest_Run_WithResultsFlag_WritesResultsFile` -- **`VersionMark-Validate-Publish`**: `SelfTest_Run_WithResultsFlag_WritesResultsFile` -- **`VersionMark-Validate-Lint`**: `SelfTest_Run_WithResultsFlag_WritesResultsFile` -- **`VersionMark-Validate-Results`**: `SelfTest_Run_WithResultsFlag_WritesResultsFile`, - `SelfTest_Run_WithResultsXmlFlag_WritesJUnitResultsFile` -- **`VersionMark-Validation-HeaderDepth`**: `SelfTest_Run_WithDepthTwo_WritesHashHashHeader` +**SelfTest_Run_LintInvalid_RejectsInvalidConfig**: The lint-invalid sub-test within +self-validation rejects an invalid configuration with a non-zero exit code. This scenario +is tested by `SelfTest_Run_LintInvalid_RejectsInvalidConfig`. diff --git a/docs/verification/version-mark/self-test/validation.md b/docs/verification/version-mark/self-test/validation.md index 45a1da4..91ddff0 100644 --- a/docs/verification/version-mark/self-test/validation.md +++ b/docs/verification/version-mark/self-test/validation.md @@ -1,43 +1,41 @@ -### Validation Unit Verification +### Validation -#### Overview +#### Verification Approach The `Validation` unit implements the self-test suite that runs during `--validate`. It exercises capture, publish, and lint workflows end-to-end inside a temporary directory and -writes results to a TRX or JUnit XML file. Tests are in `SelfTest/SelfTestTests.cs`. - -The internal test names used by `Validation` are `VersionMark_CapturesVersions`, +writes results to a TRX or JUnit XML file. Tests are in `SelfTest/SelfTestTests.cs`. The +internal test names used by `Validation` are `VersionMark_CapturesVersions`, `VersionMark_GeneratesMarkdownReport`, `VersionMark_LintPassesForValidConfig`, and -`VersionMark_LintReportsErrorsForInvalidConfig`. These names appear in TRX results files -and serve as platform-level traceability evidence. - -#### Test Scenarios +`VersionMark_LintReportsErrorsForInvalidConfig`; these appear in results files and serve +as platform-level traceability evidence. Tests use a temporary directory for results files +and intermediate artifacts. No external mocks are required. -The following test scenarios verify the `Validation` unit: +#### Test Environment -- **`SelfTest_Run_WithResultsFlag_WritesResultsFile`**: Full validation run with `--results` - writes a TRX file containing all internal test results. -- **`SelfTest_Run_WithResultsXmlFlag_WritesJUnitResultsFile`**: Full validation run with a - JUnit results flag writes a JUnit XML file. -- **`SelfTest_Run_WithDepthTwo_WritesHashHashHeader`**: Running with depth 2 produces a `##` heading in the validation output. +N/A - standard test environment. Tests create temporary directories during setup and clean +them up afterwards. -Note: `SelfTest_Run_WithResultsFlag_WritesResultsFile` verifies that the `Validation` -class internally exercises all four self-test scenarios: `VersionMark_CapturesVersions`, -`VersionMark_GeneratesMarkdownReport`, `VersionMark_LintPassesForValidConfig`, and -`VersionMark_LintReportsErrorsForInvalidConfig`. +#### Acceptance Criteria -#### Dependencies +- All unit tests for `Validation` pass with zero failures across all supported OS and + .NET version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the `Validation` unit is covered by at least one named test + scenario. -Tests use a temporary directory for results files and intermediate capture and publish -artifacts. No external mocks are required. +#### Test Scenarios -#### Requirements Coverage +**SelfTest_Run_WithResultsFlag_WritesResultsFile**: A full validation run with `--results` +writes a TRX file containing all internal test results, verifying that the `Validation` +class exercises all four self-test scenarios: `VersionMark_CapturesVersions`, +`VersionMark_GeneratesMarkdownReport`, `VersionMark_LintPassesForValidConfig`, and +`VersionMark_LintReportsErrorsForInvalidConfig`. This scenario is tested by +`SelfTest_Run_WithResultsFlag_WritesResultsFile`. -The following list maps `Validation` unit requirements to test scenarios: +**SelfTest_Run_WithResultsXmlFlag_WritesJUnitResultsFile**: A full validation run with a +JUnit results flag writes a JUnit XML file containing all internal test results. This +scenario is tested by `SelfTest_Run_WithResultsXmlFlag_WritesJUnitResultsFile`. -- **`VersionMark-Validation-Capture`**: `SelfTest_Run_WithResultsFlag_WritesResultsFile` -- **`VersionMark-Validation-Publish`**: `SelfTest_Run_WithResultsFlag_WritesResultsFile` -- **`VersionMark-Validation-Lint`**: `SelfTest_Run_WithResultsFlag_WritesResultsFile` -- **`VersionMark-Validation-WriteResults`**: `SelfTest_Run_WithResultsFlag_WritesResultsFile`, - `SelfTest_Run_WithResultsXmlFlag_WritesJUnitResultsFile` -- **`VersionMark-Validation-HeaderDepth`**: `SelfTest_Run_WithDepthTwo_WritesHashHashHeader` +**SelfTest_Run_WithDepthTwo_WritesHashHashHeader**: Running validation with depth 2 produces +a `##` heading level in the validation output. This scenario is tested by +`SelfTest_Run_WithDepthTwo_WritesHashHashHeader`. diff --git a/docs/verification/version-mark/utilities.md b/docs/verification/version-mark/utilities.md index 03ad757..bda1cc8 100644 --- a/docs/verification/version-mark/utilities.md +++ b/docs/verification/version-mark/utilities.md @@ -1,84 +1,107 @@ -## Utilities Subsystem Verification +## Utilities -### Overview +### Verification Approach The Utilities subsystem provides general-purpose helper classes for use within VersionMark. -It consists of two units: `GlobMatcher` (the glob-pattern file matcher) and `PathHelpers` -(the safe path combination utility). +It consists of two units: `GlobMatcher` (the glob-pattern file matcher) and +`PathHelpers` (the safe path combination utility). Unit tests invoke `GlobMatcher` and +`PathHelpers` directly with various inputs and assert on the returned results. Tests use +temporary directories for file-system scenarios, ensuring isolation and repeatability +across platforms. No external mocks are required. -Unit-level verification for `GlobMatcher` and `PathHelpers` is in the chapters that follow. +### Test Environment -### Verification Approach +N/A - standard test environment. Tests create temporary directories during setup and clean +them up afterwards. No additional environment configuration is required. + +### Acceptance Criteria -Unit tests invoke `GlobMatcher` and `PathHelpers` directly with various inputs and assert -on the returned results. Tests use temporary directories for file-system scenarios, -ensuring isolation and repeatability across platforms. No external mocks are required. +- All subsystem tests pass with zero failures across all supported OS and .NET version + matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the Utilities subsystem is covered by at least one named test + scenario. ### Test Scenarios -The following test scenarios verify Utilities subsystem requirements: - -- **`GlobMatcher_FindMatchingFiles_RelativePattern_ReturnsMatchingFiles`**: - Relative glob pattern matches files in the current directory. -- **`GlobMatcher_FindMatchingFiles_AbsolutePattern_ReturnsMatchingFiles`**: - Absolute glob pattern matches files regardless of the working directory. -- **`GlobMatcher_FindMatchingFiles_SingleFileAbsolutePath_ReturnsSingleFile`**: - Absolute path without wildcard returns that single file. -- **`GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles`**: - Mixed absolute and relative patterns are combined correctly. -- **`GlobMatcher_FindMatchingFiles_EmptyPatterns_ReturnsEmptyList`**: - Empty pattern array returns an empty list. -- **`GlobMatcher_FindMatchingFiles_PatternMatchingNoFiles_ReturnsEmptyList`**: - Pattern with no matches returns an empty list. -- **`GlobMatcher_SplitAbsolutePattern_PatternWithWildcard_SplitsCorrectly`**: - Pattern with wildcard is split at the last separator before the wildcard. -- **`GlobMatcher_SplitAbsolutePattern_PatternWithoutWildcard_SplitsAtLastSeparator`**: - Pattern without wildcard is split at the last separator. -- **`GlobMatcher_SplitAbsolutePattern_ForwardSlashRootPattern_SplitsToRootAndRelative`**: - Root-relative forward-slash pattern (e.g. `/*.json`) splits to the platform path root (`/` on Unix, - `\` on Windows) and relative pattern on all platforms. -- **`GlobMatcher_SplitAbsolutePattern_WindowsDriveRootPattern_SplitsToDriveRootAndRelative`**: - Windows drive-root pattern (e.g. `C:\*.json`) splits to `C:\` root and relative pattern (Windows only). -- **`PathHelpers_SafePathCombine_ValidPaths_CombinesCorrectly`**: A simple relative path is combined with the base path. -- **`PathHelpers_SafePathCombine_PathTraversalWithDoubleDots_ThrowsArgumentException`**: - A path beginning with `../` throws ArgumentException. -- **`PathHelpers_SafePathCombine_DoubleDotsInMiddle_ThrowsArgumentException`**: - A path containing `..` in the middle throws ArgumentException. -- **`PathHelpers_SafePathCombine_AbsolutePath_ThrowsArgumentException`**: - A rooted absolute path throws ArgumentException. -- **`PathHelpers_SafePathCombine_CurrentDirectoryReference_CombinesCorrectly`**: - A path containing `.` (current directory) combines correctly. -- **`PathHelpers_SafePathCombine_NestedPaths_CombinesCorrectly`**: A nested relative path combines correctly. -- **`PathHelpers_SafePathCombine_EmptyRelativePath_ReturnsBasePath`**: - An empty relative path returns the base path unchanged. -- **`PathHelpers_SafePathCombine_DotDotAsNamePrefix_CombinesCorrectly`**: - A filename that starts with `..` but is not a traversal combines correctly. -- **`PathHelpers_SafePathCombine_NullBasePath_ThrowsArgumentNullException`**: - A null base path throws ArgumentNullException. -- **`PathHelpers_SafePathCombine_NullRelativePath_ThrowsArgumentNullException`**: - A null relative path throws ArgumentNullException. - -### Dependencies - -Tests use temporary directories for file-system scenarios. No external mocks are required. - -### Requirements Coverage - -The following list maps Utilities subsystem requirements to test scenarios: - -- **`VersionMark-Utilities-GlobMatch`**: `GlobMatcher_FindMatchingFiles_RelativePattern_ReturnsMatchingFiles`, - `GlobMatcher_FindMatchingFiles_AbsolutePattern_ReturnsMatchingFiles`, - `GlobMatcher_FindMatchingFiles_SingleFileAbsolutePath_ReturnsSingleFile`, - `GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles`, - `GlobMatcher_FindMatchingFiles_EmptyPatterns_ReturnsEmptyList`, - `GlobMatcher_FindMatchingFiles_PatternMatchingNoFiles_ReturnsEmptyList` -- **`VersionMark-Utilities-SafePath`**: `PathHelpers_SafePathCombine_ValidPaths_CombinesCorrectly`, - `PathHelpers_SafePathCombine_PathTraversalWithDoubleDots_ThrowsArgumentException`, - `PathHelpers_SafePathCombine_DoubleDotsInMiddle_ThrowsArgumentException`, - `PathHelpers_SafePathCombine_AbsolutePath_ThrowsArgumentException`, - `PathHelpers_SafePathCombine_CurrentDirectoryReference_CombinesCorrectly`, - `PathHelpers_SafePathCombine_NestedPaths_CombinesCorrectly`, - `PathHelpers_SafePathCombine_EmptyRelativePath_ReturnsBasePath`, - `PathHelpers_SafePathCombine_DotDotAsNamePrefix_CombinesCorrectly`, - `PathHelpers_SafePathCombine_NullBasePath_ThrowsArgumentNullException`, - `PathHelpers_SafePathCombine_NullRelativePath_ThrowsArgumentNullException` +**GlobMatcher_FindMatchingFiles_RelativePattern_ReturnsMatchingFiles**: A relative glob +pattern matches files in the current directory. This scenario is tested by +`GlobMatcher_FindMatchingFiles_RelativePattern_ReturnsMatchingFiles`. + +**GlobMatcher_FindMatchingFiles_AbsolutePattern_ReturnsMatchingFiles**: An absolute glob +pattern matches files regardless of the working directory. This scenario is tested by +`GlobMatcher_FindMatchingFiles_AbsolutePattern_ReturnsMatchingFiles`. + +**GlobMatcher_FindMatchingFiles_SingleFileAbsolutePath_ReturnsSingleFile**: An absolute +path without a wildcard returns that single file. This scenario is tested by +`GlobMatcher_FindMatchingFiles_SingleFileAbsolutePath_ReturnsSingleFile`. + +**GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles**: Mixed absolute and +relative patterns produce a combined deduplicated result. This scenario is tested by +`GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles`. + +**GlobMatcher_FindMatchingFiles_EmptyPatterns_ReturnsEmptyList**: An empty pattern array +returns an empty list. This scenario is tested by +`GlobMatcher_FindMatchingFiles_EmptyPatterns_ReturnsEmptyList`. + +**GlobMatcher_FindMatchingFiles_PatternMatchingNoFiles_ReturnsEmptyList**: A pattern that +matches no files returns an empty list. This scenario is tested by +`GlobMatcher_FindMatchingFiles_PatternMatchingNoFiles_ReturnsEmptyList`. + +**GlobMatcher_SplitAbsolutePattern_PatternWithWildcard_SplitsCorrectly**: A pattern with a +wildcard is split at the last path separator before the wildcard. This scenario is tested +by `GlobMatcher_SplitAbsolutePattern_PatternWithWildcard_SplitsCorrectly`. + +**GlobMatcher_SplitAbsolutePattern_PatternWithoutWildcard_SplitsAtLastSeparator**: A +pattern without a wildcard is split at the last path separator. This scenario is tested by +`GlobMatcher_SplitAbsolutePattern_PatternWithoutWildcard_SplitsAtLastSeparator`. + +**GlobMatcher_SplitAbsolutePattern_ForwardSlashRootPattern_SplitsToRootAndRelative**: A +root-relative forward-slash pattern (e.g. `/*.json`) splits to the platform path root +(`/` on Unix, `\` on Windows) and the relative pattern on all platforms. This scenario +is tested by +`GlobMatcher_SplitAbsolutePattern_ForwardSlashRootPattern_SplitsToRootAndRelative`. + +**GlobMatcher_SplitAbsolutePattern_WindowsDriveRootPattern_SplitsToDriveRootAndRelative**: +A Windows drive-root pattern (e.g. `C:\*.json`) splits to the `C:\` root and the +relative pattern (Windows only). This scenario is tested by +`GlobMatcher_SplitAbsolutePattern_WindowsDriveRootPattern_SplitsToDriveRootAndRelative`. + +**PathHelpers_SafePathCombine_ValidPaths_CombinesCorrectly**: A simple relative path is +combined with the base path. This scenario is tested by +`PathHelpers_SafePathCombine_ValidPaths_CombinesCorrectly`. + +**PathHelpers_SafePathCombine_PathTraversalWithDoubleDots_ThrowsArgumentException**: A path +beginning with `../` throws `ArgumentException`. This scenario is tested by +`PathHelpers_SafePathCombine_PathTraversalWithDoubleDots_ThrowsArgumentException`. + +**PathHelpers_SafePathCombine_DoubleDotsInMiddle_ThrowsArgumentException**: A path +containing `..` in the middle throws `ArgumentException`. This scenario is tested by +`PathHelpers_SafePathCombine_DoubleDotsInMiddle_ThrowsArgumentException`. + +**PathHelpers_SafePathCombine_AbsolutePath_ThrowsArgumentException**: A rooted absolute +path throws `ArgumentException`. This scenario is tested by +`PathHelpers_SafePathCombine_AbsolutePath_ThrowsArgumentException`. + +**PathHelpers_SafePathCombine_CurrentDirectoryReference_CombinesCorrectly**: A path +containing `.` (current directory) combines correctly. This scenario is tested by +`PathHelpers_SafePathCombine_CurrentDirectoryReference_CombinesCorrectly`. + +**PathHelpers_SafePathCombine_NestedPaths_CombinesCorrectly**: A nested relative path +combines correctly. This scenario is tested by +`PathHelpers_SafePathCombine_NestedPaths_CombinesCorrectly`. + +**PathHelpers_SafePathCombine_EmptyRelativePath_ReturnsBasePath**: An empty relative path +returns the base path unchanged. This scenario is tested by +`PathHelpers_SafePathCombine_EmptyRelativePath_ReturnsBasePath`. + +**PathHelpers_SafePathCombine_DotDotAsNamePrefix_CombinesCorrectly**: A filename that +starts with `..` but is not a traversal combines correctly. This scenario is tested by +`PathHelpers_SafePathCombine_DotDotAsNamePrefix_CombinesCorrectly`. + +**PathHelpers_SafePathCombine_NullBasePath_ThrowsArgumentNullException**: A null base path +throws `ArgumentNullException`. This scenario is tested by +`PathHelpers_SafePathCombine_NullBasePath_ThrowsArgumentNullException`. + +**PathHelpers_SafePathCombine_NullRelativePath_ThrowsArgumentNullException**: A null +relative path throws `ArgumentNullException`. This scenario is tested by +`PathHelpers_SafePathCombine_NullRelativePath_ThrowsArgumentNullException`. diff --git a/docs/verification/version-mark/utilities/glob-matcher.md b/docs/verification/version-mark/utilities/glob-matcher.md index 5d16127..a4c0413 100644 --- a/docs/verification/version-mark/utilities/glob-matcher.md +++ b/docs/verification/version-mark/utilities/glob-matcher.md @@ -1,54 +1,70 @@ -### GlobMatcher Unit Verification +### GlobMatcher -#### Overview +#### Verification Approach -The `GlobMatcher` unit provides `FindMatchingFiles` and `SplitAbsolutePattern` methods for -glob-pattern file matching. It supports relative and absolute patterns and returns a sorted, -deduplicated list of full paths. Tests are in `Utilities/GlobMatcherTests.cs`. +The `GlobMatcher` unit provides `FindMatchingFiles` and `SplitAbsolutePattern` methods +for glob-pattern file matching. It supports relative and absolute patterns and returns a +sorted, deduplicated list of full paths. Tests are in `Utilities/GlobMatcherTests.cs` and +call `GlobMatcher` methods directly. Tests use temporary directories created with +`Path.GetTempPath()` for all file-system scenarios. No external mocks are required. + +#### Test Environment + +N/A - standard test environment. Tests create temporary directories during setup and clean +them up afterwards. No additional environment configuration is required. + +#### Acceptance Criteria + +- All unit tests for `GlobMatcher` pass with zero failures across all supported OS and + .NET version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the `GlobMatcher` unit is covered by at least one named test + scenario. #### Test Scenarios -The following test scenarios verify `GlobMatcher`: - -- **`GlobMatcher_FindMatchingFiles_EmptyPatterns_ReturnsEmptyList`**: - An empty pattern array returns an empty list. -- **`GlobMatcher_FindMatchingFiles_PatternMatchingNoFiles_ReturnsEmptyList`**: - A pattern that matches no files returns an empty list. -- **`GlobMatcher_FindMatchingFiles_RelativePattern_ReturnsMatchingFiles`**: - A relative glob pattern is matched against the current directory. -- **`GlobMatcher_FindMatchingFiles_AbsolutePattern_ReturnsMatchingFiles`**: - An absolute glob pattern is matched from its root directory. -- **`GlobMatcher_FindMatchingFiles_SingleFileAbsolutePath_ReturnsSingleFile`**: - An absolute path with no wildcard returns that single file. -- **`GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles`**: - Mixed absolute and relative patterns produce a combined deduplicated result. -- **`GlobMatcher_SplitAbsolutePattern_PatternWithWildcard_SplitsCorrectly`**: - A pattern with a wildcard is split at the last separator before the wildcard. -- **`GlobMatcher_SplitAbsolutePattern_PatternWithoutWildcard_SplitsAtLastSeparator`**: - A pattern without a wildcard is split at the final separator. -- **`GlobMatcher_SplitAbsolutePattern_ForwardSlashRootPattern_SplitsToRootAndRelative`**: - A root-relative forward-slash pattern (e.g. `/*.json`) splits to the platform path root (`/` on Unix, - `\` on Windows) and relative pattern on all platforms. -- **`GlobMatcher_SplitAbsolutePattern_WindowsDriveRootPattern_SplitsToDriveRootAndRelative`**: - A Windows drive-root pattern (e.g. `C:\*.json`) splits to `C:\` root and relative pattern (Windows only). - -#### Dependencies - -Tests use temporary directories created with `Path.GetTempPath()` for all file-system -scenarios. No external mocks are required. Tests call `GlobMatcher` methods directly. - -#### Requirements Coverage - -The following list maps `GlobMatcher` unit requirements to test scenarios: - -- **`VersionMark-GlobMatcher-FindFiles`**: `GlobMatcher_FindMatchingFiles_RelativePattern_ReturnsMatchingFiles`, - `GlobMatcher_FindMatchingFiles_EmptyPatterns_ReturnsEmptyList`, - `GlobMatcher_FindMatchingFiles_PatternMatchingNoFiles_ReturnsEmptyList`, - `GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles` -- **`VersionMark-GlobMatcher-AbsolutePaths`**: `GlobMatcher_FindMatchingFiles_AbsolutePattern_ReturnsMatchingFiles`, - `GlobMatcher_FindMatchingFiles_SingleFileAbsolutePath_ReturnsSingleFile`, - `GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles`, - `GlobMatcher_SplitAbsolutePattern_PatternWithWildcard_SplitsCorrectly`, - `GlobMatcher_SplitAbsolutePattern_PatternWithoutWildcard_SplitsAtLastSeparator`, - `GlobMatcher_SplitAbsolutePattern_ForwardSlashRootPattern_SplitsToRootAndRelative`, - `GlobMatcher_SplitAbsolutePattern_WindowsDriveRootPattern_SplitsToDriveRootAndRelative` +**GlobMatcher_FindMatchingFiles_EmptyPatterns_ReturnsEmptyList**: An empty pattern array +returns an empty list. This scenario is tested by +`GlobMatcher_FindMatchingFiles_EmptyPatterns_ReturnsEmptyList`. + +**GlobMatcher_FindMatchingFiles_PatternMatchingNoFiles_ReturnsEmptyList**: A pattern that +matches no files returns an empty list. This scenario is tested by +`GlobMatcher_FindMatchingFiles_PatternMatchingNoFiles_ReturnsEmptyList`. + +**GlobMatcher_FindMatchingFiles_RelativePattern_ReturnsMatchingFiles**: A relative glob +pattern is matched against the current directory. This scenario is tested by +`GlobMatcher_FindMatchingFiles_RelativePattern_ReturnsMatchingFiles`. + +**GlobMatcher_FindMatchingFiles_AbsolutePattern_ReturnsMatchingFiles**: An absolute glob +pattern is matched from its root directory. This scenario is tested by +`GlobMatcher_FindMatchingFiles_AbsolutePattern_ReturnsMatchingFiles`. + +**GlobMatcher_FindMatchingFiles_SingleFileAbsolutePath_ReturnsSingleFile**: An absolute +path with no wildcard returns that single file. This scenario is tested by +`GlobMatcher_FindMatchingFiles_SingleFileAbsolutePath_ReturnsSingleFile`. + +**GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles**: Mixed absolute and +relative patterns produce a combined deduplicated result. This scenario is tested by +`GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles`. + +**GlobMatcher_FindMatchingFiles_OverlappingPatterns_DeduplicatesResults**: A file matched +by two overlapping patterns appears only once in the result. This scenario is tested by +`GlobMatcher_FindMatchingFiles_OverlappingPatterns_DeduplicatesResults`. + +**GlobMatcher_SplitAbsolutePattern_PatternWithWildcard_SplitsCorrectly**: A pattern with a +wildcard is split at the last path separator before the wildcard. This scenario is tested +by `GlobMatcher_SplitAbsolutePattern_PatternWithWildcard_SplitsCorrectly`. + +**GlobMatcher_SplitAbsolutePattern_PatternWithoutWildcard_SplitsAtLastSeparator**: A +pattern without a wildcard is split at the final path separator. This scenario is tested by +`GlobMatcher_SplitAbsolutePattern_PatternWithoutWildcard_SplitsAtLastSeparator`. + +**GlobMatcher_SplitAbsolutePattern_ForwardSlashRootPattern_SplitsToRootAndRelative**: A +root-relative forward-slash pattern (e.g. `/*.json`) splits to the platform path root +(`/` on Unix, `\` on Windows) and the relative pattern on all platforms. This scenario +is tested by +`GlobMatcher_SplitAbsolutePattern_ForwardSlashRootPattern_SplitsToRootAndRelative`. + +**GlobMatcher_SplitAbsolutePattern_WindowsDriveRootPattern_SplitsToDriveRootAndRelative**: +A Windows drive-root pattern (e.g. `C:\*.json`) splits to the `C:\` root and the +relative pattern (Windows only). This scenario is tested by +`GlobMatcher_SplitAbsolutePattern_WindowsDriveRootPattern_SplitsToDriveRootAndRelative`. diff --git a/docs/verification/version-mark/utilities/path-helpers.md b/docs/verification/version-mark/utilities/path-helpers.md index 42e9808..55f4477 100644 --- a/docs/verification/version-mark/utilities/path-helpers.md +++ b/docs/verification/version-mark/utilities/path-helpers.md @@ -1,51 +1,76 @@ -### PathHelpers Unit Verification +### PathHelpers -#### Overview +#### Verification Approach The `PathHelpers` unit provides a `SafePathCombine` method that combines a base path and a relative path while preventing path traversal attacks. It rejects relative paths that contain `..` components that would escape the base directory, as well as absolute paths. -Tests are in `Utilities/PathHelpersTests.cs`. +Tests are in `Utilities/PathHelpersTests.cs` and call `PathHelpers.SafePathCombine` +directly. No external mocks or file system access is required. + +#### Test Environment + +N/A - standard test environment. All tests run using `dotnet test` with no additional +environment setup required. + +#### Acceptance Criteria + +- All unit tests for `PathHelpers` pass with zero failures across all supported OS and + .NET version matrix combinations (Windows, Linux, macOS x .NET 8, .NET 9, .NET 10). +- Every requirement for the `PathHelpers` unit is covered by at least one named test + scenario. #### Test Scenarios -The following test scenarios verify `PathHelpers`: - -- **`PathHelpers_SafePathCombine_ValidPaths_CombinesCorrectly`**: A simple relative path is combined with the base path. -- **`PathHelpers_SafePathCombine_PathTraversalWithDoubleDots_ThrowsArgumentException`**: - A path beginning with `../` throws ArgumentException. -- **`PathHelpers_SafePathCombine_DoubleDotsInMiddle_ThrowsArgumentException`**: - A path containing `..` in the middle throws ArgumentException. -- **`PathHelpers_SafePathCombine_AbsolutePath_ThrowsArgumentException`**: - A rooted absolute path throws ArgumentException. -- **`PathHelpers_SafePathCombine_CurrentDirectoryReference_CombinesCorrectly`**: - A path containing `.` (current directory) combines correctly. -- **`PathHelpers_SafePathCombine_NestedPaths_CombinesCorrectly`**: A nested relative path combines correctly. -- **`PathHelpers_SafePathCombine_EmptyRelativePath_ReturnsBasePath`**: - An empty relative path returns the base path unchanged. -- **`PathHelpers_SafePathCombine_DotDotAsNamePrefix_CombinesCorrectly`**: - A filename that starts with `..` but is not a traversal combines correctly. -- **`PathHelpers_SafePathCombine_NullBasePath_ThrowsArgumentNullException`**: - A null base path throws ArgumentNullException. -- **`PathHelpers_SafePathCombine_NullRelativePath_ThrowsArgumentNullException`**: - A null relative path throws ArgumentNullException. - -#### Dependencies - -No external mocks or file system access is required. Tests call `PathHelpers.SafePathCombine` -directly. - -#### Requirements Coverage - -The following list maps `PathHelpers` unit requirements to test scenarios: - -- **`VersionMark-PathHelpers-SafeCombine`**: `PathHelpers_SafePathCombine_ValidPaths_CombinesCorrectly`, - `PathHelpers_SafePathCombine_PathTraversalWithDoubleDots_ThrowsArgumentException`, - `PathHelpers_SafePathCombine_DoubleDotsInMiddle_ThrowsArgumentException`, - `PathHelpers_SafePathCombine_AbsolutePath_ThrowsArgumentException`, - `PathHelpers_SafePathCombine_CurrentDirectoryReference_CombinesCorrectly`, - `PathHelpers_SafePathCombine_NestedPaths_CombinesCorrectly`, - `PathHelpers_SafePathCombine_EmptyRelativePath_ReturnsBasePath`, - `PathHelpers_SafePathCombine_DotDotAsNamePrefix_CombinesCorrectly`, - `PathHelpers_SafePathCombine_NullBasePath_ThrowsArgumentNullException`, - `PathHelpers_SafePathCombine_NullRelativePath_ThrowsArgumentNullException` +**PathHelpers_SafePathCombine_ValidPaths_CombinesCorrectly**: A simple relative path is +combined with the base path. This scenario is tested by +`PathHelpers_SafePathCombine_ValidPaths_CombinesCorrectly`. + +**PathHelpers_SafePathCombine_PathTraversalWithDoubleDots_ThrowsArgumentException**: A path +beginning with `../` throws `ArgumentException`. This scenario is tested by +`PathHelpers_SafePathCombine_PathTraversalWithDoubleDots_ThrowsArgumentException`. + +**PathHelpers_SafePathCombine_DoubleDotsInMiddle_ThrowsArgumentException**: A path +containing `..` in the middle throws `ArgumentException`. This scenario is tested by +`PathHelpers_SafePathCombine_DoubleDotsInMiddle_ThrowsArgumentException`. + +**PathHelpers_SafePathCombine_AbsolutePath_ThrowsArgumentException**: A rooted absolute +path throws `ArgumentException`. This scenario is tested by +`PathHelpers_SafePathCombine_AbsolutePath_ThrowsArgumentException`. + +**PathHelpers_SafePathCombine_CurrentDirectoryReference_CombinesCorrectly**: A path +containing `.` (current directory) combines correctly. This scenario is tested by +`PathHelpers_SafePathCombine_CurrentDirectoryReference_CombinesCorrectly`. + +**PathHelpers_SafePathCombine_NestedPaths_CombinesCorrectly**: A nested relative path +combines correctly. This scenario is tested by +`PathHelpers_SafePathCombine_NestedPaths_CombinesCorrectly`. + +**PathHelpers_SafePathCombine_EmptyRelativePath_ReturnsBasePath**: An empty relative path +returns the base path unchanged. This scenario is tested by +`PathHelpers_SafePathCombine_EmptyRelativePath_ReturnsBasePath`. + +**PathHelpers_SafePathCombine_DotDotAsNamePrefix_CombinesCorrectly**: A filename that +starts with `..` but is not a traversal combines correctly. This scenario is tested by +`PathHelpers_SafePathCombine_DotDotAsNamePrefix_CombinesCorrectly`. + +**PathHelpers_SafePathCombine_NullBasePath_ThrowsArgumentNullException**: A null base path +throws `ArgumentNullException`. This scenario is tested by +`PathHelpers_SafePathCombine_NullBasePath_ThrowsArgumentNullException`. + +**PathHelpers_SafePathCombine_NullRelativePath_ThrowsArgumentNullException**: A null +relative path throws `ArgumentNullException`. This scenario is tested by +`PathHelpers_SafePathCombine_NullRelativePath_ThrowsArgumentNullException`. + +**PathHelpers_SafePathCombine_DeepPathTraversal_ThrowsArgumentException**: A deep path +traversal attempt (e.g. `../../../etc/passwd`) throws `ArgumentException`. This scenario is +tested by `PathHelpers_SafePathCombine_DeepPathTraversal_ThrowsArgumentException`. + +**PathHelpers_SafePathCombine_MultiSegmentRelativePath_ProducesExpectedPath**: A valid +multi-segment relative path (e.g. `test-results/output.trx`) is combined with a base +directory correctly. This scenario is tested by +`PathHelpers_SafePathCombine_MultiSegmentRelativePath_ProducesExpectedPath`. + +**PathHelpers_SafePathCombine_DllInBaseDirectory_FileExists**: A path pointing to +`DemaConsulting.VersionMark.dll` in the base directory resolves to an existing file. This +scenario is tested by `PathHelpers_SafePathCombine_DllInBaseDirectory_FileExists`. diff --git a/fix.ps1 b/fix.ps1 index 6369087..495c329 100644 --- a/fix.ps1 +++ b/fix.ps1 @@ -14,10 +14,6 @@ # Only modify this file to add project-specific operations at the designated # [PROJECT-SPECIFIC] extension points, or to update tool versions as needed. -# ============================================================================== -# HELPER FUNCTIONS -# ============================================================================== - function Get-VenvActivateScript { if (Test-Path ".venv/Scripts/Activate.ps1") { return ".venv/Scripts/Activate.ps1" } # Windows if (Test-Path ".venv/bin/Activate.ps1") { return ".venv/bin/Activate.ps1" } # Linux/macOS @@ -58,7 +54,7 @@ function Normalize-YamlLineEndings { $utf8NoBom = New-Object System.Text.UTF8Encoding($false) Get-ChildItem -Recurse -Include "*.yaml", "*.yml" | - Where-Object { $_.FullName -notmatch '[/\\](\.git|node_modules|\.venv|thirdparty|third-party|3rd-party|\.agent-logs)[/\\]' } | + Where-Object { $_.FullName -notmatch '[/\\](\.git|node_modules|\.venv|thirdparty|third-party|3rd-party|\.agent-logs|generated)[/\\]' } | ForEach-Object { $raw = [System.IO.File]::ReadAllText($_.FullName) $fixed = $raw.Replace("`r`n", "`n") @@ -68,12 +64,6 @@ function Normalize-YamlLineEndings { } } -# ============================================================================== -# AUTO-FIX -# Applies all auto-fixers with progress output. Never fails — applies what it can and -# exits 0 so agents do not react to any output as a problem to solve. -# ============================================================================== - # --- YAML Auto-Fix --- Write-Host "Fixing: YAML..." if (Initialize-PythonVenv -Silent) { diff --git a/lint.ps1 b/lint.ps1 index e4d68ac..5650ff3 100644 --- a/lint.ps1 +++ b/lint.ps1 @@ -15,10 +15,6 @@ # Only modify this file to add project-specific operations at the designated # [PROJECT-SPECIFIC] extension points, or to update tool versions as needed. -# ============================================================================== -# HELPER FUNCTIONS -# ============================================================================== - function Get-VenvActivateScript { if (Test-Path ".venv/Scripts/Activate.ps1") { return ".venv/Scripts/Activate.ps1" } # Windows if (Test-Path ".venv/bin/Activate.ps1") { return ".venv/bin/Activate.ps1" } # Linux/macOS @@ -49,15 +45,9 @@ function Initialize-PythonVenv { } } -# ============================================================================== -# LINT CHECKS -# Runs all lint checks. Exits 1 if any check fails. -# ============================================================================== - $lintError = $false -# --- PYTHON SECTION --- -# Sets up a virtual environment and runs yamllint. +# --- YAML --- Write-Host "Linting: YAML..." $skipPython = -not (Initialize-PythonVenv) if ($skipPython) { $lintError = $true } @@ -75,8 +65,7 @@ if (-not $skipPython) { # if ($LASTEXITCODE -ne 0) { $lintError = $true } # } -# --- NPM SECTION --- -# Installs npm dependencies and runs cspell and markdownlint-cli2. +# --- Spelling and Markdown --- Write-Host "Linting: spelling and markdown..." $skipNpm = $false $env:PUPPETEER_SKIP_DOWNLOAD = "true" @@ -98,8 +87,7 @@ if (-not $skipNpm) { # if ($LASTEXITCODE -ne 0) { $lintError = $true } # } -# --- DOTNET LINTING SECTION --- -# Runs compliance tools: reqstream, versionmark, reviewmark. +# --- Compliance Tools --- Write-Host "Linting: compliance tools..." $skipDotnetTools = $false dotnet tool restore > $null @@ -123,8 +111,7 @@ if (-not $skipDotnetTools) { # if ($LASTEXITCODE -ne 0) { $lintError = $true } # } -# --- DOTNET FORMATTING SECTION --- -# Verifies C# code formatting matches .editorconfig rules. +# --- dotnet Format --- Write-Host "Linting: dotnet format..." $skipDotnetFormat = $false dotnet restore > $null diff --git a/requirements.yaml b/requirements.yaml index 18feba9..4e900bb 100644 --- a/requirements.yaml +++ b/requirements.yaml @@ -2,7 +2,8 @@ # VersionMark Requirements # # This file defines the requirements for the VersionMark tool. -# Requirements are verified through tests and documented in docs/requirements_doc/ and docs/requirements_report/. +# Requirements are verified through tests and documented in +# docs/requirements_doc/ and docs/requirements_report/. includes: - docs/reqstream/version-mark.yaml @@ -18,6 +19,7 @@ includes: - docs/reqstream/version-mark/configuration/version-mark-config.yaml - docs/reqstream/version-mark/configuration/tool-config.yaml - docs/reqstream/version-mark/configuration/load.yaml + - docs/reqstream/version-mark/configuration/lint-issue.yaml - docs/reqstream/version-mark/self-test.yaml - docs/reqstream/version-mark/self-test/validation.yaml - docs/reqstream/version-mark/utilities.yaml diff --git a/src/DemaConsulting.VersionMark/Cli/Context.cs b/src/DemaConsulting.VersionMark/Cli/Context.cs index 3e42baf..c36fa75 100644 --- a/src/DemaConsulting.VersionMark/Cli/Context.cs +++ b/src/DemaConsulting.VersionMark/Cli/Context.cs @@ -23,6 +23,7 @@ namespace DemaConsulting.VersionMark.Cli; /// /// Context class that handles command-line arguments and program output. /// +/// This class is not thread-safe and is intended for single-threaded use only. internal sealed class Context : IDisposable { /// @@ -132,7 +133,11 @@ private Context() /// /// Command-line arguments. /// A new Context instance. + /// Thrown when is null. /// Thrown when arguments are invalid. + /// + /// Thrown when the log file specified by --log cannot be opened. + /// public static Context Create(string[] args) { ArgumentNullException.ThrowIfNull(args); @@ -286,6 +291,8 @@ private sealed class ArgumentParser /// Parses command-line arguments /// /// Command-line arguments. + /// Thrown when is null. + /// Thrown when an unrecognized or invalid argument is encountered. public void ParseArguments(string[] args) { ArgumentNullException.ThrowIfNull(args); @@ -306,6 +313,7 @@ public void ParseArguments(string[] args) // In capture mode, these are tool names // In publish mode, these are glob patterns + // Outside capture/publish context, -- is not valid if (Capture) { ToolNames = [.. remainingArgs]; @@ -314,6 +322,12 @@ public void ParseArguments(string[] args) { GlobPatterns = [.. remainingArgs]; } + else + { + throw new ArgumentException( + "The -- separator is only valid in capture or publish mode", + nameof(args)); + } break; } @@ -417,6 +431,7 @@ private int ParseArgument(string arg, string[] args, int index) /// Current index /// Description of what's required /// Argument value + /// Thrown when the required argument value is missing. private static string GetRequiredStringArgument(string arg, string[] args, int index, string description) { if (index >= args.Length) @@ -457,6 +472,8 @@ private static string GetRequiredStringArgument(string arg, string[] args, int i /// Current index /// Description of what's required /// Argument value + /// Thrown when the required argument value is missing. + /// Thrown when the argument value is not a valid integer. private static int GetRequiredIntArgument(string arg, string[] args, int index, string description) { if (index >= args.Length) @@ -498,7 +515,10 @@ public void WriteError(string message) // Mark that we have encountered errors _hasErrors = true; - // Write to error console unless silent mode is enabled + // Write to stderr unless silent mode is enabled. In silent mode, the caller + // detects failures via the process exit code (ExitCode property) rather than + // stderr output — this supports self-validation scenarios that deliberately + // trigger errors without producing unwanted output. if (!Silent) { var previousColor = Console.ForegroundColor; diff --git a/src/DemaConsulting.VersionMark/Configuration/VersionMarkConfig.cs b/src/DemaConsulting.VersionMark/Configuration/VersionMarkConfig.cs index 9984dd3..b588518 100644 --- a/src/DemaConsulting.VersionMark/Configuration/VersionMarkConfig.cs +++ b/src/DemaConsulting.VersionMark/Configuration/VersionMarkConfig.cs @@ -1,15 +1,15 @@ -// Copyright (c) DEMA Consulting -// +// Copyright (c) 2025 DEMA Consulting +// // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/src/DemaConsulting.VersionMark/Program.cs b/src/DemaConsulting.VersionMark/Program.cs index bdcd7a9..84cd74b 100644 --- a/src/DemaConsulting.VersionMark/Program.cs +++ b/src/DemaConsulting.VersionMark/Program.cs @@ -33,6 +33,16 @@ namespace DemaConsulting.VersionMark; /// internal static class Program { + /// + /// Default configuration file name used when no explicit path is provided. + /// + private const string DefaultConfigFile = ".versionmark.yaml"; + + /// + /// Default glob pattern used to locate capture JSON files when none are specified. + /// + private const string DefaultGlobPattern = "versionmark-*.json"; + /// /// Gets the application version string. /// @@ -201,8 +211,8 @@ private static void PrintHelp(Context context) /// The context containing command line arguments and program state. private static void RunLint(Context context) { - // Use specified file, or default to .versionmark.yaml - var configFile = context.LintFile ?? ".versionmark.yaml"; + // Use specified file, or default to the standard config file name + var configFile = context.LintFile ?? DefaultConfigFile; // Load the configuration, which performs all validation in a single pass var result = VersionMarkConfig.Load(configFile); @@ -231,7 +241,7 @@ private static void RunCapture(Context context) context.WriteLine($"Output file: {outputFile}"); // Load and validate configuration, reporting all issues before proceeding - var loadResult = VersionMarkConfig.Load(".versionmark.yaml"); + var loadResult = VersionMarkConfig.Load(DefaultConfigFile); loadResult.ReportIssues(context); // Abort capture if the configuration could not be loaded @@ -290,10 +300,10 @@ private static void RunPublish(Context context) try { - // Get glob patterns (default to versionmark-*.json if none specified) + // Get glob patterns (default to the standard pattern if none specified) var globPatterns = context.GlobPatterns.Length > 0 ? context.GlobPatterns - : new[] { "versionmark-*.json" }; + : new[] { DefaultGlobPattern }; context.WriteLine($"Searching for JSON files with patterns: {string.Join(", ", globPatterns)}"); diff --git a/src/DemaConsulting.VersionMark/Utilities/PathHelpers.cs b/src/DemaConsulting.VersionMark/Utilities/PathHelpers.cs index 2014d72..7d322d0 100644 --- a/src/DemaConsulting.VersionMark/Utilities/PathHelpers.cs +++ b/src/DemaConsulting.VersionMark/Utilities/PathHelpers.cs @@ -33,6 +33,13 @@ internal static class PathHelpers /// The base path. /// The relative path to combine. /// The combined path. + /// + /// This method prevents path-traversal attacks by verifying that the combined path stays + /// within the base directory. After combining the paths, it resolves both to their absolute + /// forms and checks that the result is not rooted outside, or otherwise escaping, the base + /// directory. Any attempt to use .. segments or absolute paths that would escape the + /// base directory results in an . + /// /// Thrown when or is . /// /// Thrown when the resolved combined path escapes the base directory, or when a supplied path is invalid. diff --git a/test/DemaConsulting.VersionMark.Tests/Capture/CaptureTests.cs b/test/DemaConsulting.VersionMark.Tests/Capture/CaptureTests.cs index 0f36ae7..48424ff 100644 --- a/test/DemaConsulting.VersionMark.Tests/Capture/CaptureTests.cs +++ b/test/DemaConsulting.VersionMark.Tests/Capture/CaptureTests.cs @@ -419,4 +419,144 @@ public void Capture_Run_MissingConfig_ReportsError() } } } + + /// + /// Test that the capture pipeline reports an error and exits non-zero when the configured command is invalid. + /// + [Fact] + public void Capture_Run_InvalidCommand_ReportsErrorAndNonZeroExitCode() + { + // Arrange - Set up temp directory with a config referencing a non-existent command + var currentDir = Directory.GetCurrentDirectory(); + var tempDir = PathHelpers.SafePathCombine(Path.GetTempPath(), Path.GetRandomFileName()); + var outputFile = PathHelpers.SafePathCombine(tempDir, "output.json"); + try + { + Directory.CreateDirectory(tempDir); + File.WriteAllText( + PathHelpers.SafePathCombine(tempDir, ".versionmark.yaml"), + """ + tools: + nonexistent-tool-xyz: + command: nonexistent-tool-xyz-command-that-does-not-exist --version + regex: '(?\d+\.\d+\.\d+)' + """); + Directory.SetCurrentDirectory(tempDir); + + var originalError = Console.Error; + try + { + using var errWriter = new StringWriter(); + Console.SetError(errWriter); + using var context = Context.Create([ + "--capture", "--job-id", "fail-job", "--output", outputFile + ]); + + // Act - Run capture with an invalid command + Program.Run(context); + + // Assert - Non-zero exit code and an error message should be reported + Assert.Equal(1, context.ExitCode); + Assert.True( + errWriter.ToString().Length > 0, + "An error message should be written when the configured command fails"); + } + finally + { + Console.SetError(originalError); + } + } + finally + { + Directory.SetCurrentDirectory(currentDir); + if (Directory.Exists(tempDir)) + { + Directory.Delete(tempDir, recursive: true); + } + } + } + + /// + /// Test that the capture pipeline reports an error and exits non-zero when the command output does not match + /// the configured regex. + /// + [Fact] + public void Capture_Run_RegexNoMatch_ReportsErrorAndNonZeroExitCode() + { + // Arrange - Set up temp directory with a config whose regex will never match dotnet output + var currentDir = Directory.GetCurrentDirectory(); + var tempDir = PathHelpers.SafePathCombine(Path.GetTempPath(), Path.GetRandomFileName()); + var outputFile = PathHelpers.SafePathCombine(tempDir, "output.json"); + try + { + Directory.CreateDirectory(tempDir); + File.WriteAllText( + PathHelpers.SafePathCombine(tempDir, ".versionmark.yaml"), + """ + tools: + dotnet: + command: dotnet --version + regex: '(?WILL_NEVER_MATCH_ANYTHING_XYZ_12345)' + """); + Directory.SetCurrentDirectory(tempDir); + + var originalError = Console.Error; + try + { + using var errWriter = new StringWriter(); + Console.SetError(errWriter); + using var context = Context.Create([ + "--capture", "--job-id", "regex-fail-job", "--output", outputFile + ]); + + // Act - Run capture with a regex that will not match + Program.Run(context); + + // Assert - Non-zero exit code and an error message should be reported + Assert.Equal(1, context.ExitCode); + Assert.True( + errWriter.ToString().Length > 0, + "An error message should be written when the regex does not match command output"); + } + finally + { + Console.SetError(originalError); + } + } + finally + { + Directory.SetCurrentDirectory(currentDir); + if (Directory.Exists(tempDir)) + { + Directory.Delete(tempDir, recursive: true); + } + } + } + + /// + /// Test that the capture pipeline reports an error and exits non-zero when --capture is used without --job-id. + /// + [Fact] + public void Capture_Run_MissingJobId_ReportsErrorAndNonZeroExitCode() + { + // Arrange - Create a capture context without --job-id + var originalError = Console.Error; + try + { + using var errWriter = new StringWriter(); + Console.SetError(errWriter); + using var context = Context.Create(["--capture"]); + + // Act - Run capture without providing --job-id + Program.Run(context); + + // Assert - Non-zero exit code and an error message referencing --job-id should be reported + Assert.Equal(1, context.ExitCode); + Assert.Contains("--job-id", errWriter.ToString()); + } + finally + { + Console.SetError(originalError); + } + } } diff --git a/test/DemaConsulting.VersionMark.Tests/Cli/ContextTests.cs b/test/DemaConsulting.VersionMark.Tests/Cli/ContextTests.cs index 361cea8..dcd6e89 100644 --- a/test/DemaConsulting.VersionMark.Tests/Cli/ContextTests.cs +++ b/test/DemaConsulting.VersionMark.Tests/Cli/ContextTests.cs @@ -181,16 +181,20 @@ public void Context_Create_ResultFlag_SetsResultsFile() [Fact] public void Context_Create_LogFlag_OpensLogFile() { + // Arrange var logFile = Path.GetTempFileName(); try { + // Act using (var context = Context.Create(["--log", logFile])) { context.WriteLine("Test message"); + + // Assert - Verify exit code is success Assert.Equal(0, context.ExitCode); } - // Verify log file was written + // Assert - Verify log file was written Assert.True(File.Exists(logFile)); var logContent = File.ReadAllText(logFile); Assert.Contains("Test message", logContent); @@ -272,12 +276,12 @@ public void Context_WriteLine_Silent_DoesNotWriteToConsole() } /// - /// Test that WriteError does not write to console when silent. - /// What is tested: WriteError is suppressed when Silent flag is set - /// What the assertions prove: WriteError respects the Silent flag + /// Test that WriteError suppresses stderr when the silent flag is set. + /// What is tested: WriteError respects Silent — no stderr output when silent is active + /// What the assertions prove: Silent mode suppresses all output; callers detect failure via ExitCode /// [Fact] - public void Context_WriteError_Silent_DoesNotWriteToConsole() + public void Context_WriteError_Silent_SuppressesStderr() { // Arrange - Redirect console error output var originalError = Console.Error; @@ -290,9 +294,12 @@ public void Context_WriteError_Silent_DoesNotWriteToConsole() using var context = Context.Create(["--silent"]); context.WriteError("Error message"); - // Assert - Verify error message was not written to console + // Assert - Verify no output was written to stderr in silent mode var output = errorWriter.ToString(); - Assert.DoesNotContain("Error message", output); + Assert.Empty(output); + + // Assert - Verify errors are still tracked via exit code + Assert.Equal(1, context.ExitCode); } finally { @@ -770,4 +777,34 @@ public void Context_Create_OutputFlag_SetsOutputFile() Assert.Equal("output.json", context.OutputFile); Assert.Equal(0, context.ExitCode); } + + /// + /// Test creating a context with --depth flag without a value throws ArgumentException. + /// What is tested: --depth flag requires a value argument + /// What the assertions prove: Missing value for --depth raises ArgumentException + /// + [Fact] + public void Context_Create_DepthFlag_WithoutValue_ThrowsArgumentException() + { + // Arrange & Act - Create context with --depth flag but no value + var exception = Assert.Throws(() => Context.Create(["--depth"])); + + // Assert - Verify exception is thrown with relevant message + Assert.Contains("--depth", exception.Message); + } + + /// + /// Test that using the -- separator outside capture or publish mode throws ArgumentException. + /// What is tested: -- separator is only valid in capture or publish mode + /// What the assertions prove: ArgumentException is thrown when -- is used outside those modes + /// + [Fact] + public void Context_Create_SeparatorOutsideMode_ThrowsArgumentException() + { + // Arrange & Act - Use -- separator without --capture or --publish + var exception = Assert.Throws(() => Context.Create(["--", "tool1"])); + + // Assert - Verify exception mentions the constraint + Assert.Contains("capture or publish", exception.Message); + } } diff --git a/test/DemaConsulting.VersionMark.Tests/Configuration/ConfigurationTests.cs b/test/DemaConsulting.VersionMark.Tests/Configuration/ConfigurationTests.cs index 1510967..af4f018 100644 --- a/test/DemaConsulting.VersionMark.Tests/Configuration/ConfigurationTests.cs +++ b/test/DemaConsulting.VersionMark.Tests/Configuration/ConfigurationTests.cs @@ -194,4 +194,152 @@ public void Configuration_ReadFromFile_InvalidYaml_ThrowsArgumentException() File.Delete(tempFile); } } + + /// + /// Test that reading a configuration file whose root node is not a mapping throws an ArgumentException. + /// + [Fact] + public void Configuration_ReadFromFile_NonMappingRoot_ThrowsArgumentException() + { + // Arrange - A YAML scalar at the root is not a valid config file + var tempFile = Path.GetTempFileName(); + File.WriteAllText(tempFile, "just a plain scalar"); + + try + { + // Act & Assert + Assert.Throws(() => VersionMarkConfig.ReadFromFile(tempFile)); + } + finally + { + File.Delete(tempFile); + } + } + + /// + /// Test that reading a configuration without a 'tools' section throws an ArgumentException. + /// + [Fact] + public void Configuration_ReadFromFile_MissingToolsSection_ThrowsArgumentException() + { + // Arrange - A YAML mapping without a 'tools' key + var tempFile = Path.GetTempFileName(); + File.WriteAllText(tempFile, """ + version: 1 + """); + + try + { + // Act & Assert + Assert.Throws(() => VersionMarkConfig.ReadFromFile(tempFile)); + } + finally + { + File.Delete(tempFile); + } + } + + /// + /// Test that reading a configuration with an invalid regex throws an ArgumentException. + /// + [Fact] + public void Configuration_ReadFromFile_InvalidRegex_ThrowsArgumentException() + { + // Arrange - regex pattern has an unclosed group + var tempFile = Path.GetTempFileName(); + File.WriteAllText(tempFile, """ + tools: + dotnet: + command: dotnet --version + regex: '(?(() => VersionMarkConfig.ReadFromFile(tempFile)); + } + finally + { + File.Delete(tempFile); + } + } + + /// + /// Test that reading a configuration with a regex that has no 'version' group throws an ArgumentException. + /// + [Fact] + public void Configuration_ReadFromFile_RegexMissingVersionGroup_ThrowsArgumentException() + { + // Arrange - regex is valid but missing the required named 'version' capture group + var tempFile = Path.GetTempFileName(); + File.WriteAllText(tempFile, """ + tools: + dotnet: + command: dotnet --version + regex: '\d+\.\d+\.\d+' + """); + + try + { + // Act & Assert + Assert.Throws(() => VersionMarkConfig.ReadFromFile(tempFile)); + } + finally + { + File.Delete(tempFile); + } + } + + /// + /// Test that reading a configuration with an empty command throws an ArgumentException. + /// + [Fact] + public void Configuration_ReadFromFile_EmptyCommand_ThrowsArgumentException() + { + // Arrange - command is empty string + var tempFile = Path.GetTempFileName(); + File.WriteAllText(tempFile, """ + tools: + dotnet: + command: '' + regex: '(?\d+\.\d+\.\d+)' + """); + + try + { + // Act & Assert + Assert.Throws(() => VersionMarkConfig.ReadFromFile(tempFile)); + } + finally + { + File.Delete(tempFile); + } + } + + /// + /// Test that reading a configuration with an empty regex throws an ArgumentException. + /// + [Fact] + public void Configuration_ReadFromFile_EmptyRegex_ThrowsArgumentException() + { + // Arrange - regex is empty string + var tempFile = Path.GetTempFileName(); + File.WriteAllText(tempFile, """ + tools: + dotnet: + command: dotnet --version + regex: '' + """); + + try + { + // Act & Assert + Assert.Throws(() => VersionMarkConfig.ReadFromFile(tempFile)); + } + finally + { + File.Delete(tempFile); + } + } } diff --git a/test/DemaConsulting.VersionMark.Tests/Configuration/LintIssueTests.cs b/test/DemaConsulting.VersionMark.Tests/Configuration/LintIssueTests.cs index 3652d1c..1b941a3 100644 --- a/test/DemaConsulting.VersionMark.Tests/Configuration/LintIssueTests.cs +++ b/test/DemaConsulting.VersionMark.Tests/Configuration/LintIssueTests.cs @@ -177,4 +177,18 @@ public void VersionMarkLoadResult_ReportIssues_Warning_WritesToStdOut() Console.SetError(originalError); } } + + /// + /// Test that throws + /// when context is null. + /// + [Fact] + public void VersionMarkLoadResult_ReportIssues_NullContext_ThrowsArgumentNullException() + { + // Arrange + var loadResult = new VersionMarkLoadResult(null, []); + + // Act & Assert + Assert.Throws(() => loadResult.ReportIssues(null!)); + } } diff --git a/test/DemaConsulting.VersionMark.Tests/Configuration/VersionMarkConfigLoadTests.cs b/test/DemaConsulting.VersionMark.Tests/Configuration/VersionMarkConfigLoadTests.cs index b65cceb..85c022d 100644 --- a/test/DemaConsulting.VersionMark.Tests/Configuration/VersionMarkConfigLoadTests.cs +++ b/test/DemaConsulting.VersionMark.Tests/Configuration/VersionMarkConfigLoadTests.cs @@ -136,6 +136,32 @@ public void VersionMarkConfig_Load_MissingToolsSection_ReturnsNullConfig() } } + /// + /// Test that a YAML file whose root node is not a mapping returns null config with an error issue. + /// + [Fact] + public void VersionMarkConfig_Load_NonMappingRoot_ReturnsNullConfig() + { + // Arrange - Write a YAML file whose root is a plain scalar, not a mapping + var tempFile = Path.GetTempFileName(); + try + { + const string yaml = "just a plain scalar"; + File.WriteAllText(tempFile, yaml); + + // Act - Attempt to load config with a non-mapping root node + var (config, issues) = VersionMarkConfig.Load(tempFile); + + // Assert - Config should be null because the root must be a mapping + Assert.Null(config); + Assert.Contains(issues, i => i.Severity == LintSeverity.Error); + } + finally + { + File.Delete(tempFile); + } + } + /// /// Test that a config with an empty 'tools' section returns null config with an error issue. /// diff --git a/test/DemaConsulting.VersionMark.Tests/Configuration/VersionMarkConfigTests.cs b/test/DemaConsulting.VersionMark.Tests/Configuration/VersionMarkConfigTests.cs index a8fae42..555c435 100644 --- a/test/DemaConsulting.VersionMark.Tests/Configuration/VersionMarkConfigTests.cs +++ b/test/DemaConsulting.VersionMark.Tests/Configuration/VersionMarkConfigTests.cs @@ -1,15 +1,15 @@ -// Copyright (c) DEMA Consulting -// +// Copyright (c) 2025 DEMA Consulting +// // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/test/DemaConsulting.VersionMark.Tests/Publishing/MarkdownFormatterTests.cs b/test/DemaConsulting.VersionMark.Tests/Publishing/MarkdownFormatterTests.cs index 3393f26..4ae5028 100644 --- a/test/DemaConsulting.VersionMark.Tests/Publishing/MarkdownFormatterTests.cs +++ b/test/DemaConsulting.VersionMark.Tests/Publishing/MarkdownFormatterTests.cs @@ -34,7 +34,7 @@ public class MarkdownFormatterTests /// What the assertions prove: The output lists tools in the correct alphabetical sequence /// [Fact] - public void MarkdownFormatter_FormatVersions_SortsToolsAlphabetically() + public void MarkdownFormatter_Format_SortsToolsAlphabetically() { // Arrange - Create VersionInfo with tools in non-alphabetical order var versionInfos = new[] @@ -71,7 +71,7 @@ public void MarkdownFormatter_FormatVersions_SortsToolsAlphabetically() /// What the assertions prove: The output displays only the version when all jobs have the same version /// [Fact] - public void MarkdownFormatter_FormatVersions_WithUniformVersions_ShowsVersionOnly() + public void MarkdownFormatter_Format_WithUniformVersions_ShowsVersionOnly() { // Arrange - Create multiple VersionInfos with the same version across jobs var versionInfos = new[] @@ -119,7 +119,7 @@ public void MarkdownFormatter_FormatVersions_WithUniformVersions_ShowsVersionOnl /// What the assertions prove: The output displays job IDs in parentheses when versions differ /// [Fact] - public void MarkdownFormatter_FormatVersions_WithDifferentVersions_ShowsIndividualJobs() + public void MarkdownFormatter_Format_WithDifferentVersions_ShowsIndividualJobs() { // Arrange - Create VersionInfos with different versions across jobs var versionInfos = new[] @@ -168,7 +168,7 @@ public void MarkdownFormatter_FormatVersions_WithDifferentVersions_ShowsIndividu /// What the assertions prove: The output heading level matches the specified depth /// [Fact] - public void MarkdownFormatter_FormatVersions_WithCustomDepth_UsesCorrectHeadingLevel() + public void MarkdownFormatter_Format_WithCustomDepth_UsesCorrectHeadingLevel() { // Arrange - Create simple VersionInfo var versionInfos = new[] @@ -208,7 +208,7 @@ public void MarkdownFormatter_FormatVersions_WithCustomDepth_UsesCorrectHeadingL /// What the assertions prove: The formatter produces valid output with just the header /// [Fact] - public void MarkdownFormatter_FormatVersions_EmptyList_ProducesHeaderOnly() + public void MarkdownFormatter_Format_EmptyList_ProducesHeaderOnly() { // Arrange - Create empty VersionInfo list var versionInfos = Array.Empty(); @@ -224,11 +224,11 @@ public void MarkdownFormatter_FormatVersions_EmptyList_ProducesHeaderOnly() /// /// Test that MarkdownFormatter handles a single job correctly. - /// What is tested: Edge case - single job shows just the version - /// What the assertions prove: Single job is treated as uniform (shows version only) + /// What is tested: Edge case - single job suppresses job ID + /// What the assertions prove: Single job is treated as uniform (shows version only, no job ID) /// [Fact] - public void MarkdownFormatter_FormatVersions_SingleJob_ShowsAllJobs() + public void MarkdownFormatter_Format_SingleJob_SuppressesJobId() { // Arrange - Create single VersionInfo var versionInfos = new[] @@ -258,7 +258,7 @@ public void MarkdownFormatter_FormatVersions_SingleJob_ShowsAllJobs() /// What the assertions prove: The formatter correctly handles both uniform and varying versions /// [Fact] - public void MarkdownFormatter_FormatVersions_MixedVersions_HandlesCorrectly() + public void MarkdownFormatter_Format_MixedVersions_HandlesCorrectly() { // Arrange - Create VersionInfos with some tools uniform, some different var versionInfos = new[] @@ -298,7 +298,7 @@ public void MarkdownFormatter_FormatVersions_MixedVersions_HandlesCorrectly() /// What the assertions prove: Job IDs appear in alphabetical order within version groups /// [Fact] - public void MarkdownFormatter_FormatVersions_SortsJobIdsAlphabetically() + public void MarkdownFormatter_Format_SortsJobIdsAlphabetically() { // Arrange - Create VersionInfos where two jobs share a version and one job has a different version var versionInfos = new[] @@ -329,7 +329,7 @@ public void MarkdownFormatter_FormatVersions_SortsJobIdsAlphabetically() /// What the assertions prove: Special characters in versions are preserved in output /// [Fact] - public void MarkdownFormatter_FormatVersions_WithSpecialCharacters_PreservesVersions() + public void MarkdownFormatter_Format_WithSpecialCharacters_PreservesVersions() { // Arrange - Create VersionInfo with special version strings var versionInfos = new[] @@ -360,7 +360,7 @@ public void MarkdownFormatter_FormatVersions_WithSpecialCharacters_PreservesVers /// What the assertions prove: Sorting is case-insensitive (Dotnet comes before node) /// [Fact] - public void MarkdownFormatter_FormatVersions_CaseInsensitiveSorting() + public void MarkdownFormatter_Format_CaseInsensitiveSorting() { // Arrange - Create VersionInfo with mixed-case tool names var versionInfos = new[] @@ -397,7 +397,7 @@ public void MarkdownFormatter_FormatVersions_CaseInsensitiveSorting() /// What the assertions prove: Version groups are sorted alphabetically /// [Fact] - public void MarkdownFormatter_FormatVersions_SortsVersionsAlphabetically() + public void MarkdownFormatter_Format_SortsVersionsAlphabetically() { // Arrange - Create VersionInfos with multiple different versions var versionInfos = new[] diff --git a/test/DemaConsulting.VersionMark.Tests/Publishing/PublishingTests.cs b/test/DemaConsulting.VersionMark.Tests/Publishing/PublishingTests.cs index 458aa79..03f7b34 100644 --- a/test/DemaConsulting.VersionMark.Tests/Publishing/PublishingTests.cs +++ b/test/DemaConsulting.VersionMark.Tests/Publishing/PublishingTests.cs @@ -377,4 +377,21 @@ public void Publishing_Run_WithReportDepth_UsesCorrectDepth() } } } + + /// + /// Test that throws + /// when reportDepth is zero or negative. + /// + [Fact] + public void Publishing_Format_ReportDepthZeroOrNegative_ThrowsArgumentOutOfRangeException() + { + // Arrange + var versionInfos = new[] { new VersionInfo("job-1", new Dictionary { ["dotnet"] = "8.0.100" }) }; + + // Act & Assert - depth 0 should throw + Assert.Throws(() => MarkdownFormatter.Format(versionInfos, reportDepth: 0)); + + // Act & Assert - negative depth should also throw + Assert.Throws(() => MarkdownFormatter.Format(versionInfos, reportDepth: -1)); + } } diff --git a/test/DemaConsulting.VersionMark.Tests/SelfTest/SelfTestTests.cs b/test/DemaConsulting.VersionMark.Tests/SelfTest/SelfTestTests.cs index f05f053..8bd282a 100644 --- a/test/DemaConsulting.VersionMark.Tests/SelfTest/SelfTestTests.cs +++ b/test/DemaConsulting.VersionMark.Tests/SelfTest/SelfTestTests.cs @@ -20,7 +20,6 @@ using DemaConsulting.VersionMark.Cli; using DemaConsulting.VersionMark.SelfTest; -using DemaConsulting.VersionMark.Utilities; namespace DemaConsulting.VersionMark.Tests.SelfTest; @@ -29,55 +28,6 @@ namespace DemaConsulting.VersionMark.Tests.SelfTest; /// public class SelfTestTests { - /// - /// Test that PathHelpers prevents path traversal attacks within the self-test subsystem context. - /// - [Fact] - public void SelfTest_PathHelpers_PathTraversal_ThrowsArgumentException() - { - // Arrange - Define a base directory and an attacker-controlled traversal path - var baseDir = AppContext.BaseDirectory; - const string traversalPath = "../../../etc/passwd"; - - // Act & Assert - The self-test subsystem path helper should reject traversal attempts - Assert.Throws(() => - PathHelpers.SafePathCombine(baseDir, traversalPath)); - } - - /// - /// Test that PathHelpers correctly combines valid paths within the self-test subsystem context. - /// - [Fact] - public void SelfTest_PathHelpers_ValidRelativePath_ProducesExpectedPath() - { - // Arrange - Use the application base directory as the root - var baseDir = AppContext.BaseDirectory; - const string relativePath = "test-results/output.trx"; - - // Act - Combine the base directory with a valid relative path - var result = PathHelpers.SafePathCombine(baseDir, relativePath); - - // Assert - The combined path should be under the base directory - Assert.False(string.IsNullOrEmpty(result), - "Valid path combination should produce a non-empty result"); - Assert.True(result.StartsWith(baseDir, StringComparison.OrdinalIgnoreCase) || - Path.GetFullPath(result).StartsWith(Path.GetFullPath(baseDir), StringComparison.OrdinalIgnoreCase), - "Combined path should be rooted within the base directory"); - } - - /// - /// Test that the self-test subsystem can locate the main DLL in the base directory. - /// - [Fact] - public void SelfTest_PathHelpers_FindsDllInBaseDirectory_FileExists() - { - // Arrange - var dllPath = PathHelpers.SafePathCombine(AppContext.BaseDirectory, "DemaConsulting.VersionMark.dll"); - - // Act & Assert - Assert.True(File.Exists(dllPath)); - } - /// /// Test that the self-validation pipeline writes results to a TRX file when --results is specified. /// @@ -171,4 +121,84 @@ public void SelfTest_Run_WithDepthTwo_WritesHashHashHeader() Console.SetOut(originalOut); } } + + /// + /// Test that the self-validation capture workflow runs successfully. + /// What is tested: The capture sub-test within Validation.Run passes + /// What the assertions prove: Validation completes with exit code 0, confirming capture ran + /// + [Fact] +#pragma warning disable S4144 // Intentionally identical: each test covers a distinct requirement for traceability + public void SelfTest_Run_Capture_CapturesToolVersions() + { + // Arrange + using var context = Context.Create(["--validate", "--silent"]); + + // Act - Run self-validation; it internally runs the capture sub-test + Validation.Run(context); + + // Assert - Exit code 0 means all self-validation tests (including capture) passed + Assert.Equal(0, context.ExitCode); + } +#pragma warning restore S4144 + + /// + /// Test that the self-validation publish workflow runs successfully. + /// What is tested: The publish sub-test within Validation.Run passes + /// What the assertions prove: Validation completes with exit code 0, confirming publish ran + /// + [Fact] +#pragma warning disable S4144 // Intentionally identical: each test covers a distinct requirement for traceability + public void SelfTest_Run_Publish_GeneratesMarkdownReport() + { + // Arrange + using var context = Context.Create(["--validate", "--silent"]); + + // Act - Run self-validation; it internally runs the publish sub-test + Validation.Run(context); + + // Assert - Exit code 0 means all self-validation tests (including publish) passed + Assert.Equal(0, context.ExitCode); + } +#pragma warning restore S4144 + + /// + /// Test that the self-validation lint-valid workflow accepts a valid configuration. + /// What is tested: The lint-valid sub-test within Validation.Run passes + /// What the assertions prove: Validation completes with exit code 0, confirming valid config accepted + /// + [Fact] +#pragma warning disable S4144 // Intentionally identical: each test covers a distinct requirement for traceability + public void SelfTest_Run_LintValid_PassesForValidConfig() + { + // Arrange + using var context = Context.Create(["--validate", "--silent"]); + + // Act - Run self-validation; it internally runs the lint-valid sub-test + Validation.Run(context); + + // Assert - Exit code 0 means all self-validation tests (including lint-valid) passed + Assert.Equal(0, context.ExitCode); + } +#pragma warning restore S4144 + + /// + /// Test that the self-validation lint-invalid workflow rejects an invalid configuration. + /// What is tested: The lint-invalid sub-test within Validation.Run passes + /// What the assertions prove: Validation completes with exit code 0, confirming invalid config was rejected + /// + [Fact] +#pragma warning disable S4144 // Intentionally identical: each test covers a distinct requirement for traceability + public void SelfTest_Run_LintInvalid_RejectsInvalidConfig() + { + // Arrange + using var context = Context.Create(["--validate", "--silent"]); + + // Act - Run self-validation; it internally runs the lint-invalid sub-test + Validation.Run(context); + + // Assert - Exit code 0 means all self-validation tests (including lint-invalid) passed + Assert.Equal(0, context.ExitCode); + } +#pragma warning restore S4144 } diff --git a/test/DemaConsulting.VersionMark.Tests/Utilities/GlobMatcherTests.cs b/test/DemaConsulting.VersionMark.Tests/Utilities/GlobMatcherTests.cs index 76eccb8..f13bedd 100644 --- a/test/DemaConsulting.VersionMark.Tests/Utilities/GlobMatcherTests.cs +++ b/test/DemaConsulting.VersionMark.Tests/Utilities/GlobMatcherTests.cs @@ -68,6 +68,7 @@ public void GlobMatcher_FindMatchingFiles_PatternMatchingNoFiles_ReturnsEmptyLis } } + // NOTE: This test mutates Environment.CurrentDirectory and requires sequential execution within the class. /// /// Test that FindMatchingFiles returns matching files when given a relative pattern. /// @@ -153,6 +154,7 @@ public void GlobMatcher_FindMatchingFiles_SingleFileAbsolutePath_ReturnsSingleFi } } + // NOTE: This test mutates Environment.CurrentDirectory and requires sequential execution within the class. /// /// Test that FindMatchingFiles combines results from both absolute and relative patterns. /// @@ -192,6 +194,37 @@ public void GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles() } } + /// + /// Test that FindMatchingFiles returns each file only once when overlapping patterns match the same file. + /// + [Fact] + public void GlobMatcher_FindMatchingFiles_OverlappingPatterns_DeduplicatesResults() + { + // Arrange + var tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDir); + try + { + var filePath = Path.Combine(tempDir, "target.json"); + File.WriteAllText(filePath, "{}"); + + // Two patterns that both match the same file + var pattern1 = Path.Combine(tempDir, "*.json"); + var pattern2 = Path.Combine(tempDir, "target.json"); + + // Act + var result = GlobMatcher.FindMatchingFiles([pattern1, pattern2]); + + // Assert - The file should appear only once + Assert.Single(result); + Assert.Equal(Path.GetFullPath(filePath), result[0]); + } + finally + { + Directory.Delete(tempDir, recursive: true); + } + } + /// /// Test that SplitAbsolutePattern correctly splits a pattern with a wildcard at the last /// separator before the wildcard. diff --git a/test/DemaConsulting.VersionMark.Tests/Utilities/PathHelpersTests.cs b/test/DemaConsulting.VersionMark.Tests/Utilities/PathHelpersTests.cs index 5074605..ffaacdc 100644 --- a/test/DemaConsulting.VersionMark.Tests/Utilities/PathHelpersTests.cs +++ b/test/DemaConsulting.VersionMark.Tests/Utilities/PathHelpersTests.cs @@ -200,4 +200,54 @@ public void PathHelpers_SafePathCombine_DotDotAsNamePrefix_CombinesCorrectly() // Assert Assert.Equal(Path.Combine(basePath, relativePath), result); } + + /// + /// Test that SafePathCombine throws ArgumentException for a deep path traversal attempt. + /// + [Fact] + public void PathHelpers_SafePathCombine_DeepPathTraversal_ThrowsArgumentException() + { + // Arrange - Define a base directory and an attacker-controlled traversal path + var baseDir = AppContext.BaseDirectory; + const string traversalPath = "../../../etc/passwd"; + + // Act & Assert - The path helper should reject traversal attempts + Assert.Throws(() => + PathHelpers.SafePathCombine(baseDir, traversalPath)); + } + + /// + /// Test that SafePathCombine correctly combines a valid multi-segment relative path. + /// + [Fact] + public void PathHelpers_SafePathCombine_MultiSegmentRelativePath_ProducesExpectedPath() + { + // Arrange - Use the application base directory as the root + var baseDir = AppContext.BaseDirectory; + const string relativePath = "test-results/output.trx"; + + // Act - Combine the base directory with a valid relative path + var result = PathHelpers.SafePathCombine(baseDir, relativePath); + + // Assert - The combined path should equal the expected combined path exactly + Assert.Equal(Path.Combine(baseDir, relativePath), result); + } + + /// + /// Test that SafePathCombine correctly locates the main DLL in the base directory. + /// + [Fact] + public void PathHelpers_SafePathCombine_DllInBaseDirectory_FileExists() + { + // Arrange + var baseDir = AppContext.BaseDirectory; + const string fileName = "DemaConsulting.VersionMark.dll"; + + // Act + var result = PathHelpers.SafePathCombine(baseDir, fileName); + + // Assert + Assert.Equal(Path.Combine(baseDir, fileName), result); + Assert.True(File.Exists(result)); + } }