Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ Usage: versionmark [options]
| `-?`, `-h`, `--help` | Display help message |
| `--silent` | Suppress console output |
| `--log <file>` | Write output to log file |
| `--depth <depth>` | Heading depth for validation and publish mode (default: 1, 1-6) |
| **Lint Mode** | |
| `--lint [<config-file>]` | Check configuration file (default: `.versionmark.yaml`) |
| **Capture Mode** | |
Expand All @@ -98,7 +99,7 @@ Usage: versionmark [options]
| **Publish Mode** | |
| `--publish` | Enable publish mode |
| `--report <file>` | **(Required)** Output markdown file path |
| `--report-depth <depth>` | Heading depth for markdown output (default: 2, min: 1, max: 6) |
| `--report-depth <depth>` | Heading depth for markdown output (default: --depth value, 1-6) |
| `-- <patterns...>` | Glob patterns for JSON files (default: `versionmark-*.json`) |
| **Self-Validation** | |
| `--validate` | Run self-validation tests |
Expand Down Expand Up @@ -186,6 +187,12 @@ its current environment. Run with `--validate`:
versionmark --validate
```

Use `--depth` to control the heading depth of the report (default: 1):

```bash
versionmark --validate --depth 2
```

Example output:

```text
Expand Down
3 changes: 2 additions & 1 deletion docs/design/version-mark/cli/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ command-line state and output routing. It is constructed via the `Create` factor
| `ToolNames` | `string[]` | `[]` | Tool names after `--` separator in capture|
| `Publish` | `bool` | `false` | `--publish` flag |
| `ReportFile` | `string?` | `null` | `--report <file>` |
| `ReportDepth` | `int` | `2` | `--report-depth <depth>` |
| `Depth` | `int` | `1` | `--depth <depth>` (heading depth, def: 1) |
| `ReportDepth` | `int` | `Depth` | `--report-depth <depth>` (def: `Depth`) |
| `GlobPatterns`| `string[]` | `[]` | Patterns after `--` separator in publish |
| `ExitCode` | `int` | `0`/`1` | 0 for success, 1 if errors reported |

Expand Down
12 changes: 11 additions & 1 deletion docs/reqstream/version-mark/cli/context.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,23 @@ sections:
- Context_Create_PublishFlag_SetsPublishTrue
- Context_Create_ReportParameter_SetsReportFile
- Context_Create_ReportDepthParameter_SetsReportDepth
- Context_Create_NoReportDepth_DefaultsToTwo
- 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

- id: VersionMark-Context-WriteLine
title: The Context.WriteLine method shall write output respecting the silent flag.
Expand Down
10 changes: 10 additions & 0 deletions docs/reqstream/version-mark/self-test/validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,13 @@ sections:
tests:
- VersionMark_LintPassesForValidConfig
- VersionMark_LintReportsErrorsForInvalidConfig

- id: VersionMark-Validation-HeaderDepth
title: >-
The Validation class shall use the context Depth for the self-validation
report heading depth.
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
15 changes: 13 additions & 2 deletions docs/user_guide/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ This generates a markdown file consolidating versions from all jobs.
| `-?`, `-h`, `--help` | Display help message |
| `--silent` | Suppress console output |
| `--log <file>` | Write output to log file |
| `--depth <depth>` | Heading depth for validation and publish mode (default: 1, 1-6) |
| **Lint Mode** | |
| `--lint [<config-file>]` | Check configuration file (default: `.versionmark.yaml`) |
| **Capture Mode** | |
Expand All @@ -105,7 +106,7 @@ This generates a markdown file consolidating versions from all jobs.
| **Publish Mode** | |
| `--publish` | Enable publish mode |
| `--report <file>` | **(Required)** Output markdown file path |
| `--report-depth <depth>` | Heading depth for markdown output (default: 2, min: 1, max: 6) |
| `--report-depth <depth>` | Heading depth for markdown output (default: --depth value, 1-6) |
| `-- <patterns...>` | Glob patterns for JSON files (default: `versionmark-*.json`) |
| **Self-Validation** | |
| `--validate` | Run self-validation tests |
Expand Down Expand Up @@ -203,9 +204,12 @@ versionmark --publish --report versions.md
# Use custom glob patterns
versionmark --publish --report docs/tool-versions.md -- captured/*.json build/*.json

# Control heading depth (default is ##, depth 2)
# Control heading depth (default is #, depth 1)
versionmark --publish --report versions.md --report-depth 3

# Use --depth to set both self-validation heading and report-depth default
versionmark --publish --report versions.md --depth 3

# Combine options
versionmark --publish --report docs/versions.md --report-depth 1 -- versionmark-*.json
```
Expand Down Expand Up @@ -395,6 +399,13 @@ Use `--silent` to suppress console output while still writing a results file:
versionmark --silent --validate --results results.trx
```

Use `--depth` to embed the self-validation report at a specific heading level within a larger
markdown document:

```bash
versionmark --validate --depth 2
```

## Validation Report

Example output:
Expand Down
35 changes: 30 additions & 5 deletions src/DemaConsulting.VersionMark/Cli/Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,15 @@ internal sealed class Context : IDisposable
/// </summary>
public string? ReportFile { get; private init; }

/// <summary>
/// Gets the markdown header depth for the self-validation report and default for publish mode.
/// </summary>
public int Depth { get; private init; } = 1;

/// <summary>
/// Gets the report depth for markdown heading levels in publish mode.
/// </summary>
public int ReportDepth { get; private init; } = 2;
public int ReportDepth { get; private init; } = 1;

/// <summary>
/// Gets the list of glob patterns for JSON files in publish mode.
Expand Down Expand Up @@ -149,6 +154,7 @@ public static Context Create(string[] args)
ToolNames = parser.ToolNames,
Publish = parser.Publish,
ReportFile = parser.ReportFile,
Depth = parser.Depth,
ReportDepth = parser.ReportDepth,
GlobPatterns = parser.GlobPatterns
};
Expand Down Expand Up @@ -256,10 +262,20 @@ private sealed class ArgumentParser
/// </summary>
public string? ReportFile { get; private set; }

/// <summary>
/// Gets the markdown header depth for the self-validation report and default for publish mode.
/// </summary>
public int Depth { get; private set; } = 1;

/// <summary>
/// Backing field for the explicitly-specified report depth.
/// </summary>
private int? _explicitReportDepth;

/// <summary>
/// Gets the report depth for markdown heading levels in publish mode.
/// </summary>
public int ReportDepth { get; private set; } = 2;
public int ReportDepth => _explicitReportDepth ?? Depth;

/// <summary>
/// Gets the list of glob patterns for JSON files in publish mode.
Expand Down Expand Up @@ -371,10 +387,19 @@ private int ParseArgument(string arg, string[] args, int index)
return index + 1;

case "--report-depth":
ReportDepth = GetRequiredIntArgument(arg, args, index, "a depth value");
if (ReportDepth < 1)
_explicitReportDepth = GetRequiredIntArgument(arg, args, index, "a depth value");
if (_explicitReportDepth is < 1 or > 6)
{
throw new ArgumentException($"{arg} requires an integer value between 1 and 6", nameof(args));
}

return index + 1;
Comment thread
Malcolmnixon marked this conversation as resolved.

case "--depth":
Depth = GetRequiredIntArgument(arg, args, index, "a depth value");
if (Depth is < 1 or > 6)
{
throw new ArgumentException($"{arg} requires a positive integer value (minimum 1)", nameof(args));
throw new ArgumentException($"{arg} requires an integer value between 1 and 6", nameof(args));
}

return index + 1;
Expand Down
3 changes: 2 additions & 1 deletion src/DemaConsulting.VersionMark/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ private static void PrintHelp(Context context)
context.WriteLine(" -v, --version Display version information");
context.WriteLine(" -?, -h, --help Display this help message");
context.WriteLine(" --silent Suppress console output");
context.WriteLine(" --depth <depth> Heading depth for self-validation and --report-depth default (default: 1, range: 1-6)");
context.WriteLine(" --validate Run self-validation");
context.WriteLine(" --results <file> Write validation results to file (.trx or .xml)");
context.WriteLine(" --log <file> Write output to log file");
Expand All @@ -191,7 +192,7 @@ private static void PrintHelp(Context context)
context.WriteLine("Publish Mode:");
context.WriteLine(" --publish Generate markdown report from JSON files");
context.WriteLine(" --report <file> Output markdown file (required)");
context.WriteLine(" --report-depth <depth> Heading depth for markdown (default: 2)");
context.WriteLine(" --report-depth <depth> Heading depth for markdown (default: --depth value, range: 1-6)");
context.WriteLine(" -- <patterns...> Glob patterns for JSON files (default: versionmark-*.json)");
}

Expand Down
3 changes: 2 additions & 1 deletion src/DemaConsulting.VersionMark/SelfTest/Validation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@
/// <param name="context">The context for output.</param>
private static void PrintValidationHeader(Context context)
{
context.WriteLine("# DEMA Consulting VersionMark");
var headingPrefix = new string('#', context.Depth);
context.WriteLine($"{headingPrefix} DEMA Consulting VersionMark");
context.WriteLine("");
context.WriteLine("| Information | Value |");
context.WriteLine("| :------------------ | :------------------------------------------------- |");
Expand Down Expand Up @@ -125,8 +126,8 @@
// Build command line arguments for capture
var args = new List<string>
{
"--silent",

Check warning on line 129 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Define a constant instead of using this literal '--silent' 4 times.

Check warning on line 129 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Define a constant instead of using this literal '--silent' 4 times.

Check warning on line 129 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Define a constant instead of using this literal '--silent' 4 times.

Check warning on line 129 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Define a constant instead of using this literal '--silent' 4 times.

Check warning on line 129 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Define a constant instead of using this literal '--silent' 4 times.

Check warning on line 129 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Define a constant instead of using this literal '--silent' 4 times.

Check warning on line 129 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Define a constant instead of using this literal '--silent' 4 times.

Check warning on line 129 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Define a constant instead of using this literal '--silent' 4 times.

Check warning on line 129 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Define a constant instead of using this literal '--silent' 4 times.
"--log", logFile,

Check warning on line 130 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Define a constant instead of using this literal '--log' 4 times.

Check warning on line 130 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Define a constant instead of using this literal '--log' 4 times.

Check warning on line 130 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Define a constant instead of using this literal '--log' 4 times.

Check warning on line 130 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Define a constant instead of using this literal '--log' 4 times.

Check warning on line 130 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Define a constant instead of using this literal '--log' 4 times.

Check warning on line 130 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Define a constant instead of using this literal '--log' 4 times.

Check warning on line 130 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Define a constant instead of using this literal '--log' 4 times.

Check warning on line 130 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Define a constant instead of using this literal '--log' 4 times.

Check warning on line 130 in src/DemaConsulting.VersionMark/SelfTest/Validation.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Define a constant instead of using this literal '--log' 4 times.
"--capture",
"--job-id", "test-job",
"--output", outputFile
Expand Down
129 changes: 124 additions & 5 deletions test/DemaConsulting.VersionMark.Tests/Cli/ContextTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -468,17 +468,17 @@ public void Context_Create_ReportDepthParameter_SetsReportDepth()
/// <summary>
/// Test creating a context with default report-depth.
/// What is tested: Default report-depth value when not specified
/// What the assertions prove: The default report depth is 2
/// What the assertions prove: The default report depth is 1 (matching the --depth default)
/// </summary>
[TestMethod]
public void Context_Create_NoReportDepth_DefaultsToTwo()
public void Context_Create_NoReportDepth_DefaultsToDepthOne()
{
// Arrange & Act - Create context without --report-depth
using var context = Context.Create(["--publish", "--report", "output.md"]);

// Assert - Verify default report depth is 2
// What is proved: Report depth defaults to 2 when not specified
Assert.AreEqual(2, context.ReportDepth);
// Assert - Verify default report depth is 1 (the default --depth value)
// What is proved: Report depth defaults to the --depth value (1) when not specified
Assert.AreEqual(1, context.ReportDepth);
}

/// <summary>
Expand Down Expand Up @@ -507,6 +507,125 @@ public void Context_Create_ReportDepthNegative_ThrowsArgumentException()
Context.Create(["--publish", "--report", "output.md", "--report-depth", "-1"]));
}

/// <summary>
/// Test that --report-depth 7 throws ArgumentException.
/// What is tested: Validation rejects a depth value greater than 6
/// What the assertions prove: ArgumentException is thrown for report-depth values greater than 6
/// </summary>
[TestMethod]
public void Context_Create_ReportDepthSeven_ThrowsArgumentException()
{
// Arrange & Act & Assert - 7 exceeds the maximum Markdown heading level of 6
Assert.ThrowsExactly<ArgumentException>(() =>
Context.Create(["--publish", "--report", "output.md", "--report-depth", "7"]));
}

/// <summary>
/// Test creating a context with the depth parameter.
/// What is tested: --depth parameter parsing captures the depth value
/// What the assertions prove: The depth is correctly parsed as an integer
/// </summary>
[TestMethod]
public void Context_Create_DepthParameter_SetsDepth()
{
// Arrange & Act - Create context with --depth flag
using var context = Context.Create(["--depth", "3"]);

// Assert - Verify depth is captured
// What is proved: --depth parameter value is correctly parsed as an integer
Assert.AreEqual(3, context.Depth);
Assert.AreEqual(0, context.ExitCode);
}

/// <summary>
/// Test creating a context with no depth parameter.
/// What is tested: Default depth value when not specified
/// What the assertions prove: The depth defaults to 1
/// </summary>
[TestMethod]
public void Context_Create_NoDepth_DefaultsToOne()
{
// Arrange & Act - Create context without --depth
using var context = Context.Create([]);

// Assert - Verify default depth is 1
// What is proved: Depth defaults to 1 when not specified
Assert.AreEqual(1, context.Depth);
}

/// <summary>
/// Test that --depth sets the default for --report-depth.
/// What is tested: --depth value is used as default for ReportDepth when --report-depth not specified
/// What the assertions prove: ReportDepth equals Depth when --report-depth is not given
/// </summary>
[TestMethod]
public void Context_Create_DepthParameter_SetsDefaultReportDepth()
{
// Arrange & Act - Create context with --depth but no --report-depth
using var context = Context.Create(["--publish", "--report", "output.md", "--depth", "3"]);

// Assert - Verify report depth defaults to the depth value
// What is proved: --depth value is used as the default for ReportDepth
Assert.AreEqual(3, context.ReportDepth);
Assert.AreEqual(0, context.ExitCode);
}

/// <summary>
/// Test that explicit --report-depth overrides --depth.
/// What is tested: --report-depth takes precedence over --depth for ReportDepth
/// What the assertions prove: ReportDepth equals the explicit --report-depth value, not --depth
/// </summary>
[TestMethod]
public void Context_Create_ExplicitReportDepthOverridesDepth()
{
// Arrange & Act - Create context with both --depth and --report-depth
using var context = Context.Create(["--publish", "--report", "output.md", "--depth", "2", "--report-depth", "4"]);

// Assert - Verify explicit --report-depth takes precedence over --depth
// What is proved: --report-depth value (4) overrides --depth value (2) for ReportDepth
Assert.AreEqual(4, context.ReportDepth);
Assert.AreEqual(0, context.ExitCode);
}

/// <summary>
/// Test that --depth 0 throws ArgumentException.
/// What is tested: Validation rejects a depth value less than 1
/// What the assertions prove: ArgumentException is thrown for depth values less than 1
/// </summary>
[TestMethod]
public void Context_Create_DepthZero_ThrowsArgumentException()
{
// Arrange & Act & Assert - Zero is not a valid heading depth
Assert.ThrowsExactly<ArgumentException>(() =>
Context.Create(["--depth", "0"]));
}

/// <summary>
/// Test that a negative --depth throws ArgumentException.
/// What is tested: Validation rejects negative depth values
/// What the assertions prove: ArgumentException is thrown for negative depth values
/// </summary>
[TestMethod]
public void Context_Create_DepthNegative_ThrowsArgumentException()
{
// Arrange & Act & Assert - Negative values are not valid heading depths
Assert.ThrowsExactly<ArgumentException>(() =>
Context.Create(["--depth", "-1"]));
}

/// <summary>
/// Test that --depth 7 throws ArgumentException.
/// What is tested: Validation rejects a depth value greater than 6
/// What the assertions prove: ArgumentException is thrown for depth values greater than 6
/// </summary>
[TestMethod]
public void Context_Create_DepthSeven_ThrowsArgumentException()
{
// Arrange & Act & Assert - 7 exceeds the maximum Markdown heading level of 6
Assert.ThrowsExactly<ArgumentException>(() =>
Context.Create(["--depth", "7"]));
}

/// <summary>
/// Test creating a context with glob patterns after -- separator.
/// What is tested: Glob patterns after -- are captured in GlobPatterns array
Expand Down
2 changes: 1 addition & 1 deletion test/DemaConsulting.VersionMark.Tests/IntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ public void VersionMark_PublishCommand_GeneratesMarkdownReport()
var reportContent = File.ReadAllText(reportFile);

// Verify markdown structure
Assert.Contains("## Tool Versions", reportContent);
Assert.Contains("# Tool Versions", reportContent);

// Verify tools are present and sorted alphabetically
Assert.Contains("dotnet", reportContent);
Expand Down
2 changes: 1 addition & 1 deletion test/DemaConsulting.VersionMark.Tests/ProgramTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ public void Program_Run_WithPublishCommand_GeneratesMarkdownReport()
Assert.IsTrue(File.Exists(reportFile), "Report file was not created");

var reportContent = File.ReadAllText(reportFile);
Assert.Contains("## Tool Versions", reportContent);
Assert.Contains("# Tool Versions", reportContent);
Assert.Contains("dotnet", reportContent);
Assert.Contains("node", reportContent);
Assert.Contains("8.0.0", reportContent);
Expand Down
Loading
Loading