Skip to content

Add tool version capture to Build Notes PDF#20

Merged
Malcolmnixon merged 12 commits intomainfrom
copilot/add-tool-versions-to-build-notes
Feb 15, 2026
Merged

Add tool version capture to Build Notes PDF#20
Malcolmnixon merged 12 commits intomainfrom
copilot/add-tool-versions-to-build-notes

Conversation

Copy link
Contributor

Copilot AI commented Feb 15, 2026

Pull Request

Description

Build Notes PDF now includes versions of all tools used during build. Captures tool versions from integration-test matrix jobs (2 OSes × 3 .NET versions) and build-docs job, consolidates them, and publishes to docs/buildnotes/versions.md as a top-level section positioned after the main build notes content.

Changes

  • Integration-test job: Added checkout step to access .versionmark.yaml, captures tool versions (dotnet, git) post-validation with shortened job-IDs (e.g., "int-win-8" instead of "integration-windows-latest-dotnet8.x"), uploads JSON artifacts per matrix combination
  • Build-docs job: Downloads integration-test artifacts, captures own tool versions with explicit tool list (dotnet, git, node, npm, pandoc, weasyprint, sarifmark, sonarmark, reqstream, buildmark), publishes consolidated versions.md, and displays the report to console
  • Build Notes PDF: Includes versions.md via updated docs/buildnotes/definition.yaml as a top-level section (heading depth 1) positioned after buildnotes.md
  • Command Execution: Fixed Windows compatibility by running commands through shell (cmd.exe /c on Windows, /bin/sh -c on Unix) using ArgumentList to avoid escaping issues. This enables proper execution of .cmd and .bat files like npm on Windows while maintaining cross-platform compatibility.
  • Markdown Report Formatting: Improved readability by using multiple bullets for different version groups instead of single-line entries. Uniform versions show just the version number without job IDs or extra text. Different versions show job IDs in plain parentheses (e.g., (job-1, job-2)) for clarity.
  • Test Infrastructure: Fixed intermittent test failures by ensuring all tests use isolated temp directories with GUIDs to prevent race conditions during parallel execution. Added TestDirectory helper class to reduce boilerplate and improve test maintainability. All path operations use PathHelpers.SafePathCombine for CodeQL security compliance.
  • Code Quality: Fixed inefficient ContainsKey warning in Validation.cs by using TryGetValue pattern for single dictionary lookup.

Version consolidation handled by VersionMark's publish mode using glob patterns across all captured JSON files. Job-IDs are kept concise to prevent lengthy report lines. Tool lists are explicitly specified in capture commands to only capture tools actually available in each job context, preventing failures from attempting to capture unavailable tools.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code quality improvement

Related Issues

Implements feature request for tool version visibility in build documentation.

Pre-Submission Checklist

Before submitting this pull request, ensure you have completed the following:

Build and Test

  • Code builds successfully: dotnet build --configuration Release
  • All unit tests pass: dotnet test --configuration Release
  • Self-validation tests pass:
    dotnet run --project src/DemaConsulting.VersionMark --configuration Release --framework net10.0
    --no-build -- --validate
  • Code produces zero warnings

Code Quality

  • Code formatting is correct: dotnet format --verify-no-changes
  • New code has appropriate XML documentation comments
  • Static analyzer warnings have been addressed

Quality Checks

Please run the following checks before submitting:

  • Spell checker passes: cspell "**/*.{md,cs}"
  • Markdown linter passes: markdownlint "**/*.md"
  • YAML linter passes: yamllint '**/*.{yml,yaml}'

Testing

  • Added unit tests for new functionality
  • Updated existing tests if behavior changed
  • All tests follow the AAA (Arrange, Act, Assert) pattern
  • Test coverage is maintained or improved

Documentation

  • Updated README.md (if applicable)
  • Updated ARCHITECTURE.md (if applicable)
  • Added code examples for new features (if applicable)
  • Updated requirements.yaml (if applicable)

Additional Notes

Workflow changes and production code modifications to support cross-platform command execution and improved markdown formatting. Local testing confirms capture/publish flow generates expected markdown output. CodeQL security scan passed with no vulnerabilities.

Job-IDs use shell script transformations to create concise identifiers (e.g., "int-win-8", "int-ubuntu-9") preventing lengthy report lines. The consolidated version report is displayed to the console for visibility during the build process.

The integration-test job includes a checkout step to ensure the .versionmark.yaml configuration file is available for version capture operations. Both jobs explicitly specify which tools to capture: integration-test captures dotnet and git (the tools available in that context), while build-docs captures all available tools including dotnet tools restored via dotnet tool restore.

Cross-Platform Command Execution: Modified RunCommand in VersionMarkConfig.cs to execute commands through the appropriate shell (cmd.exe /c on Windows, /bin/sh -c on Unix) using ArgumentList instead of Arguments string. This approach:

  • Supports .cmd and .bat files on Windows (e.g., npm.cmd)
  • Avoids manual quote escaping issues
  • Maintains I/O redirection capabilities
  • Checks process exit codes to properly detect command failures
  • Works consistently across all supported platforms

Markdown Report Formatting: Updated MarkdownFormatter.cs to improve readability:

  • Tools with different versions across jobs now display as multiple bullets (one per version group) instead of cramming all versions on a single line
  • Uniform versions show just the version number without any job ID annotations or "(All jobs)" text
  • Different versions show job IDs in plain parentheses (e.g., 18.0.0 (job-1, job-3)) instead of subscript formatting
  • Report uses top-level heading (depth 1: # Tool Versions) making it a primary section in the Build Notes PDF
  • Example output:
    # Tool Versions
    
    - **dotnet**: 8.0.0
    - **node**: 18.0.0 (job-1, job-3)
    - **node**: 20.0.0 (job-2)

Report Positioning: The Tool Versions section is positioned after the main build notes content in the PDF through the docs/buildnotes/definition.yaml file ordering, providing a logical flow where build details are presented first, followed by the tool versions used to create the build.

Code Quality Improvements: Fixed inefficient dictionary access in Validation.cs by replacing ContainsKey check followed by indexer access with a single TryGetValue call, eliminating duplicate dictionary lookups.

Test Infrastructure Improvements: Addressed intermittent test failure caused by race conditions when tests ran in parallel. The issue was that tests were creating files in shared directories without unique names, causing conflicts. Solution:

  • All tests now use unique temp directories with GUIDs (versionmark-test-{Guid})
  • Created TestDirectory disposable helper class that encapsulates temp directory creation, optional config file copying, directory management, and automatic cleanup
  • Refactored multiple tests to use the helper class, reducing ~38 lines of boilerplate code
  • Tests can now safely run in parallel without file system conflicts
  • All path operations use PathHelpers.SafePathCombine instead of Path.Combine to avoid CodeQL security warnings about unsafe path operations
Original prompt

This section details on the original issue you should resolve

<issue_title>[Feature]: Tool Versions in Build Notes PDF</issue_title>
<issue_description>### Problem Statement

The Build Notes PDF document should contain the tool versions used in the build.

Proposed Solution

The integrqtion-test and build-docs jobs of build.yaml should run VersionMark to capture the tools and dotnet used. The integration-test job will need to include rhe capture json in its artifacts. This would require build-docs to download and install the VersionMark package just like the integration-test job does.

Then the build-docs job should use VersionMark to publish the versions.md in the docs/buildnotes folder and it should be included in the definition.yaml file so it gets included in the PDF.

Alternatives Considered

No response

Usage Examples

Benefits

The Build Notes PDF would list the version of tools used to create the build.

Priority

Medium - Would improve my workflow

Willingness to Contribute

Yes, I can submit a pull request

Additional Context

No response

Checklist

  • I have searched existing issues to ensure this is not a duplicate
  • I have provided a clear description of the feature
  • I have explained why this feature would be useful</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits February 15, 2026 00:52
Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
Copilot AI changed the title [WIP] Add tool versions to Build Notes PDF Add tool version capture to Build Notes PDF Feb 15, 2026
Copilot AI requested a review from Malcolmnixon February 15, 2026 00:56
- Use shortened job-IDs (e.g., int-win-8 instead of integration-windows-latest-dotnet8.x) to reduce report line length
- Add step to display tool versions report to console after publishing
- Update artifact path pattern to match new job-ID format

Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
@Malcolmnixon
Copy link
Contributor

@copilot The integration test failed with:

Run echo "Capturing tool versions..."
Capturing tool versions...
VersionMark version 0.0.0-run.58+a94e120dbdeaf7f8abb68a9efb9989787843ce68
Copyright (c) DEMA Consulting

Capturing tool versions for job 'int-ubuntu-8'...
Output file: versionmark-int-ubuntu-8.json
Error: Configuration file not found: .versionmark.yaml

Why is it not able to find .versionmark.yaml as the file definitely exists in the root of the repo, and the build job seems to try to run the tool from the root as well?

The integration-test job needs to checkout the repository to access the .versionmark.yaml configuration file required for version capture. Without this, the versionmark --capture command fails with "Configuration file not found".

Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 15, 2026

@copilot The integration test failed with:

Run echo "Capturing tool versions..."

...

Fixed in 9025fe7. The integration-test job was missing a checkout step, so the .versionmark.yaml file wasn't present in the working directory. Added checkout step at the beginning of the job to ensure the configuration file is available for version capture.

OS_SHORT=$(echo "${{ matrix.os }}" | sed 's/windows-latest/win/;s/ubuntu-latest/ubuntu/')
DOTNET_SHORT=$(echo "${{ matrix.dotnet-version }}" | sed 's/\.x$//')
JOB_ID="int-${OS_SHORT}-${DOTNET_SHORT}"
versionmark --capture --job-id "${JOB_ID}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should only capture the tool versions used in the job - in this case probably just 'dotnet'. Trying to capture all tool versions will fail because not all tools are installed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in fe5c315. Now explicitly captures only dotnet and git (the tools actually available in the integration-test job).

shell: bash
run: |
echo "Capturing tool versions..."
versionmark --capture --job-id "build-docs"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may want to be explicit about which tools to capture version information for.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in fe5c315. Now explicitly lists all tools to capture: dotnet, git, node, npm, and all dotnet tools (pandoc, weasyprint, sarifmark, sonarmark, reqstream, buildmark).

- integration-test job now captures only dotnet and git (tools actually available in that job)
- build-docs job explicitly lists all tools to capture (dotnet, git, node, npm, and all dotnet tools)
- Prevents failures from trying to capture tools that aren't installed

Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
@Malcolmnixon
Copy link
Contributor

@copilot I got the following test failure:

  Failed IntegrationTest_CaptureCommandWithDefaultOutput_UsesDefaultFilename [382 ms]
  Error Message:
   Assert.IsTrue failed. 'condition' expression: 'File.Exists(outputFile)'. Output file 'versionmark-integration-test-job.json' was not created
  Stack Trace:
     at Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowAssertIsTrueFailed(String message) in /_/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs:line 161
   at Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.ComputeAssertion(String conditionExpression) in /_/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs:line 40
   at Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(Nullable`1 condition, AssertIsTrueInterpolatedStringHandler& message, String conditionExpression) in /_/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs:line 129
   at DemaConsulting.VersionMark.Tests.IntegrationTests.IntegrationTest_CaptureCommandWithDefaultOutput_UsesDefaultFilename() in /home/runner/work/VersionMark/VersionMark/test/DemaConsulting.VersionMark.Tests/IntegrationTests.cs:line 411

There seems to be something intermittent with this - can you evaluate?

- Updated IntegrationTest_CaptureCommandWithDefaultOutput_UsesDefaultFilename to use temp directory with GUID
- Updated IntegrationTest_CaptureCommand_CapturesToolVersions to use temp directory with GUID
- Replaced all Path.GetRandomFileName() with GUID-based temp directories for consistency
- Prevents race conditions when tests run in parallel by ensuring each test uses isolated temp directory
- All tests now copy .versionmark.yaml to their own temp directory instead of sharing repo root

Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 15, 2026

@copilot I got the following test failure:

  Failed IntegrationTest_CaptureCommandWithDefaultOutput_UsesDefaultFilename [382 ms]

...

Fixed in 04b6822. The intermittent failure was caused by a race condition - the test was creating files in the shared repository root directory without using unique names. Multiple parallel test executions could interfere with each other. Now all tests that write/delete files use isolated temp directories with GUIDs, preventing collisions during parallel execution.

- Created TestDirectory disposable helper class for managing isolated test directories
- Handles temp directory creation, optional config file copying, directory changes, and cleanup
- Refactored IntegrationTest_CaptureCommand_CapturesToolVersions to use helper
- Refactored IntegrationTest_CaptureCommandWithDefaultOutput_UsesDefaultFilename to use helper
- Refactored IntegrationTest_CaptureCommandWithMissingConfig_ReturnsError to use helper
- Refactored VersionMark_PublishCommand_GeneratesMarkdownReport to use helper
- Moved FindRepositoryRoot inside TestDirectory class to avoid code duplication
- Reduces boilerplate and improves test maintainability

Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
Copy link
Contributor

@Malcolmnixon Malcolmnixon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You appear to have replace the PathHelpers.SafePathCombine with the Path.Combine. The Path.Combine is considered unsafe by CodeQL and produces warnings. Please make sure to use the save version.

… warnings

- Updated TestDirectory constructor to use PathHelpers.SafePathCombine
- Updated all temp directory creation in tests to use PathHelpers.SafePathCombine
- Fixed IntegrationTest_CaptureCommand_CapturesToolVersions output file path
- Ensures CodeQL security checks pass by using safe path combination method

Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 15, 2026

You appear to have replace the PathHelpers.SafePathCombine with the Path.Combine. The Path.Combine is considered unsafe by CodeQL and produces warnings. Please make sure to use the save version.

Fixed in 48c69df. Replaced all instances of Path.Combine with PathHelpers.SafePathCombine in the test file to avoid CodeQL warnings about unsafe path operations.

@Malcolmnixon
Copy link
Contributor

@copilot I got the following error:

Error: Failed to run command 'npm --version': An error occurred trying to start process 'npm' with working directory 'D:\a\VersionMark\VersionMark'. The system cannot find the file specified.

I suspect it's because VersionMarkConfig.RunCommand doesn't have UseShellExecute=true, and I think npm is actually a cmd file on Windows, so it requires the shell; However UseShellExecute=true prevents I/O redirection which is necessary.

As such I think the best approach is to modify the RunCommand to actually run "cmd.exe /c " or "/bin/sh -c " but use the ProcessStartInfo.ArgumentList approach to put the entire command in as a single string. For example:

var isWin = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
var startInfo = new ProcessStartInfo
{
    FileName = isWin ? "cmd.exe" : "/bin/sh",
    UseShellExecute = false,
    RedirectStandardOutput = true
};
startInfo.ArgumentList.Add(isWin ? "/c" : "-c");
startInfo.ArgumentList.Add(fullCommandLine);

- Modified RunCommand to use cmd.exe /c on Windows and /bin/sh -c on Unix
- Used ArgumentList instead of Arguments to avoid escaping issues
- Check process exit code and throw exception for non-zero exit codes
- This fixes npm.cmd and other batch file execution on Windows
- Cross-platform solution that works without manual quote escaping

Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 15, 2026

@copilot I got the following error:

Error: Failed to run command 'npm --version': An error occurred trying to start process 'npm' with working directory 'D:\a\VersionMark\VersionMark'. The syste...

Fixed in 2c77e51. Implemented your suggested approach using ArgumentList with cmd.exe /c on Windows and /bin/sh -c on Unix. This avoids escaping issues and properly supports .cmd and .bat files like npm on Windows. Added exit code checking to detect command failures.

@Malcolmnixon
Copy link
Contributor

@copilot Please fix the following warning src/DemaConsulting.VersionMark/Validation.cs(160): warning [cs/inefficient-containskey] Inefficient use of 'ContainsKey' and [indexer](1).

Then the markdown report needs to be modified for readability. The current formatting for different versions isn't readable. Instead Replicate multiple bullets (one for each version-group) rather than trying to put all versions in a single bullet.

- Fixed inefficient ContainsKey warning in Validation.cs by using TryGetValue
- Modified markdown formatter to use multiple bullets for different version groups
- Removed "(All jobs)" text - uniform versions now show just the version
- Removed subscript formatting - job IDs now shown in plain parentheses
- Updated all tests to reflect new formatting
- Improves readability when tools have different versions across jobs

Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 15, 2026

@copilot Please fix the following warning src/DemaConsulting.VersionMark/Validation.cs(160): warning [cs/inefficient-containskey] Inefficient use of 'ContainsKey' and [indexer](1).

Then the markd...

Fixed in 2eb14b4.

  1. Fixed the inefficient ContainsKey warning by using TryGetValue instead
  2. Modified markdown formatting:
    • Tools with different versions now show multiple bullets (one per version group)
    • Removed "(All jobs)" text
    • Removed subscript formatting - job IDs now shown in plain parentheses like (job-1, job-2)
    • Uniform versions show just the version without any job IDs

Example output:

- **dotnet**: 8.0.0
- **node**: 18.0.0 (job-1, job-3)
- **node**: 20.0.0 (job-2)

@Malcolmnixon
Copy link
Contributor

@copilot For the Version Report, let's drop the depth from 2 to 1 so it's a top-level section; and also in the Build Notes PDF, let's have it after the buildnotes.md

- Changed --report-depth from 2 to 1 in build workflow to make Tool Versions a top-level section (#)
- Reordered input-files in definition.yaml to place versions.md after buildnotes.md
- This makes the version report a primary section in the Build Notes PDF and positions it after the main build notes content

Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 15, 2026

@copilot For the Version Report, let's drop the depth from 2 to 1 so it's a top-level section; and also in the Build Notes PDF, let's have it after the buildnotes.md

Fixed in 7a126f6. Changed --report-depth from 2 to 1 in the build workflow (now generates # Tool Versions instead of ## Tool Versions), and reordered the input files in definition.yaml so versions.md comes after buildnotes.md in the PDF.

@Malcolmnixon Malcolmnixon marked this pull request as ready for review February 15, 2026 14:19
@Malcolmnixon Malcolmnixon merged commit 2c6cf7d into main Feb 15, 2026
11 checks passed
@Malcolmnixon Malcolmnixon deleted the copilot/add-tool-versions-to-build-notes branch February 15, 2026 14:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Tool Versions in Build Notes PDF

2 participants