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
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ jobs:
--sarif artifacts/csharp.sarif
--report docs/code_quality/codeql-quality.md
--heading "SarifMark CodeQL Analysis"
--report-depth 1
--depth 1

- name: Display CodeQL Quality Report
shell: bash
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Options:
--log <file> Write output to log file
--sarif <file> SARIF file to process
--report <file> Export analysis results to markdown file
--report-depth <depth> Markdown header depth for report (default: 1)
--depth <depth> Markdown header depth for report (default: 1)
--heading <text> Custom heading for report (default: [ToolName] Analysis)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/design/sarifmark/cli/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ The `Cli` subsystem exposes the following interface to the rest of the tool:
| `Enforce` | `bool` | `--enforce` | Enforcement mode flag |
| `SarifFile` | `string?` | `--sarif <file>` | Path to the SARIF input file |
| `ReportFile` | `string?` | `--report <file>` | Path for the markdown report output file |
| `ReportDepth` | `int` | `--report-depth <depth>` | Markdown heading depth for the report |
| `Depth` | `int` | `--depth <depth>` | Markdown heading depth for the report |
| `Heading` | `string?` | `--heading <text>` | Custom heading text for the report |
| `ResultsFile` | `string?` | `--results <file>` | Path for the self-validation results file |
| `ExitCode` | `int` | *(derived)* | 0 until `WriteError` is called, then 1 |
Expand Down
14 changes: 8 additions & 6 deletions docs/design/sarifmark/cli/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@ This satisfies requirements `SarifMark-Context-Create` through `SarifMark-Contex
| `Enforce` | `bool` | `false` | `--enforce` | Enforcement mode flag |
| `SarifFile` | `string?` | `null` | `--sarif <file>` | Path to the SARIF file |
| `ReportFile` | `string?` | `null` | `--report <file>` | Path to the markdown report output file |
| `ReportDepth` | `int` | `1` | `--report-depth <depth>` | Markdown heading depth for the report |
| `Depth` | `int` | `1` | `--depth <depth>` | Markdown heading depth for the report |
| `Heading` | `string?` | `null` | `--heading <text>` | Custom heading text for the report |
| `ResultsFile` | `string?` | `null` | `--results <file>` | Path for the self-validation results file |
| `ExitCode` | `int` | `0`/`1` | *(derived)* | 0 until `WriteError` is called, then 1 |

The `--result` flag is accepted as a legacy alias for `--results`, preserving backwards compatibility
(see requirement `SarifMark-Context-ResultLegacyAlias`).
The `--report-depth` flag is accepted as a legacy alias for `--depth`, preserving backwards compatibility
(see requirement `SarifMark-Context-ReportDepthParam`). The `--result` flag is similarly accepted as a
legacy alias for `--results` (see requirement `SarifMark-Context-ResultLegacyAlias`).

These properties satisfy requirements `SarifMark-Context-VersionFlag`, `SarifMark-Context-HelpFlag`,
`SarifMark-Context-SilentFlag`, `SarifMark-Context-ValidateFlag`, `SarifMark-Context-EnforceFlag`,
Expand All @@ -53,14 +54,15 @@ and `SarifMark-Context-ExitCode`.

`ArgumentParser` is a private, sealed nested class responsible for token-by-token command-line
parsing. Its `ParseArguments(string[] args)` method iterates through tokens in order and delegates
each to `ParseArgument`. Value-bearing flags (e.g. `--sarif`, `--report-depth`) consume the
each to `ParseArgument`. Value-bearing flags (e.g. `--sarif`, `--depth`) consume the
following token as their argument value.

Any unrecognized token causes `ParseArgument` to throw `ArgumentException` with a message
identifying the unsupported argument. This satisfies requirement `SarifMark-Context-UnknownArgs`.

`--report-depth` requires a positive integer value; non-integer or non-positive values also throw
`ArgumentException`. This satisfies requirement `SarifMark-Context-ReportDepthParam`.
`--depth` requires a positive integer value; non-integer or non-positive values also throw
`ArgumentException`. The legacy alias `--report-depth` behaves identically.
This satisfies requirement `SarifMark-Context-ReportDepthParam`.

## WriteLine Method

Expand Down
2 changes: 1 addition & 1 deletion docs/design/sarifmark/program.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ to the context output, listing every supported option with its flag syntax and a
- `--log <file>` — write output to a log file
- `--sarif <file>` — SARIF file to process
- `--report <file>` — export analysis results to a markdown file
- `--report-depth <depth>` — markdown header depth for the report (default: 1)
- `--depth <depth>` — markdown header depth for the report (default: 1)
- `--heading <text>` — custom heading for the report (default: `[ToolName] Analysis`)

This satisfies requirement `SarifMark-Program-Help`.
Expand Down
5 changes: 3 additions & 2 deletions docs/reqstream/sarifmark/cli/cli.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,16 @@ sections:
- Cli_ReportParameter_SetsReportFilePath

- id: SarifMark-Cli-ReportDepth
title: The CLI shall accept a heading depth via the --report-depth parameter.
title: The CLI shall accept a heading depth via the --depth parameter.
justification: >-
Exposing a validated heading depth parameter allows the application layer to control markdown
report heading levels without reimplementing argument parsing or range checking.
tags: [public]
children:
- SarifMark-Context-ReportDepthParam
tests:
- Cli_ReportDepthParameter_SetsReportDepth
- Cli_DepthParameter_SetsDepth
- SelfTest_DepthParameter_AffectsSelfValidationReport

- id: SarifMark-Cli-Heading
title: The CLI shall accept a custom heading via the --heading parameter.
Expand Down
8 changes: 7 additions & 1 deletion docs/reqstream/sarifmark/cli/context.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,19 @@ sections:
- Context_Create_ReportParameter_SetsReportFile

- id: SarifMark-Context-ReportDepthParam
title: The Create method shall parse --report-depth, validate it is a positive integer, and set ReportDepth.
title: >-
The Create method shall parse --depth (and legacy --report-depth), validate it is a positive
integer, and set Depth.
justification: >-
Validating the depth value at parse time prevents invalid heading levels from propagating into
markdown generation, and defaulting to 1 ensures sensible behavior when the parameter is
omitted.
tags: [internal]
tests:
- Context_Create_DepthParameter_SetsDepth
- Context_Create_DepthWithoutValue_ThrowsArgumentException
- Context_Create_DepthInvalidValue_ThrowsArgumentException
- Context_Create_DepthZero_ThrowsArgumentException
- Context_Create_ReportDepthParameter_SetsReportDepth
- Context_Create_ReportDepthWithoutValue_ThrowsArgumentException
- Context_Create_ReportDepthInvalidValue_ThrowsArgumentException
Expand Down
6 changes: 3 additions & 3 deletions docs/user_guide/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ SarifMark supports the following command-line options:
- `--log <file>`: Write console output to log file
- `--sarif <file>`: SARIF file to process (required for analysis)
- `--report <file>`: Export analysis results to markdown file
- `--report-depth <depth>`: Markdown header depth for report (default: 1)
- `--depth <depth>`: Markdown header depth for report (default: 1)
- `--heading <text>`: Custom heading for report (default: [ToolName] Analysis)

# Common Usage Patterns
Expand All @@ -165,7 +165,7 @@ sarifmark --sarif analysis.sarif --report report.md --heading "Security Analysis
Control the markdown header depth in the report:

```bash
sarifmark --sarif analysis.sarif --report report.md --report-depth 2
sarifmark --sarif analysis.sarif --report report.md --depth 2
```

This is useful when including the report in a larger document where you want the sections to be at a deeper level.
Expand Down Expand Up @@ -349,7 +349,7 @@ This is useful in CI/CD pipelines to fail builds when quality issues are detecte
Yes, you can customize:

- **Heading**: Use `--heading "Custom Title"` to set a custom report heading
- **Header Depth**: Use `--report-depth 2` to adjust the markdown header level (useful when including the report in
- **Header Depth**: Use `--depth 2` to adjust the markdown header level (useful when including the report in
a larger document)

The report content format is standardized but these options allow you to integrate reports into different documentation
Expand Down
15 changes: 8 additions & 7 deletions src/DemaConsulting.SarifMark/Cli/Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ internal sealed class Context : IDisposable
public string? ReportFile { get; private init; }

/// <summary>
/// Gets the report markdown depth.
/// Gets the markdown depth.
/// </summary>
public int ReportDepth { get; private init; } = 1;
public int Depth { get; private init; } = 1;

/// <summary>
/// Gets the custom heading for the report.
Expand Down Expand Up @@ -120,7 +120,7 @@ public static Context Create(string[] args)
Enforce = parser.Enforce,
SarifFile = parser.SarifFile,
ReportFile = parser.ReportFile,
ReportDepth = parser.ReportDepth,
Depth = parser.Depth,
Heading = parser.Heading,
ResultsFile = parser.ResultsFile
};
Expand Down Expand Up @@ -196,9 +196,9 @@ private sealed class ArgumentParser
public string? ReportFile { get; private set; }

/// <summary>
/// Gets the report markdown depth.
/// Gets the markdown depth.
/// </summary>
public int ReportDepth { get; private set; } = 1;
public int Depth { get; private set; } = 1;

/// <summary>
/// Gets the custom heading for the report.
Expand Down Expand Up @@ -278,8 +278,9 @@ private int ParseArgument(string arg, string[] args, int index)
ReportFile = GetRequiredStringArgument(arg, args, index, "a filename argument");
return index + 1;

case "--report-depth":
ReportDepth = GetRequiredIntArgument(arg, args, index);
case "--depth":
case "--report-depth": // Legacy alias for --depth
Depth = GetRequiredIntArgument(arg, args, index);
return index + 1;

case "--heading":
Expand Down
4 changes: 2 additions & 2 deletions src/DemaConsulting.SarifMark/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ private static void PrintHelp(Context context)
context.WriteLine(" --log <file> Write output to log file");
context.WriteLine(" --sarif <file> SARIF file to process");
context.WriteLine(" --report <file> Export analysis results to markdown file");
context.WriteLine(" --report-depth <depth> Markdown header depth for report (default: 1)");
context.WriteLine(" --depth <depth> Markdown header depth for report (default: 1)");
context.WriteLine(" --heading <text> Custom heading for report (default: [ToolName] Analysis)");
}

Expand Down Expand Up @@ -193,7 +193,7 @@ private static void ProcessSarifAnalysis(Context context)
context.WriteLine($"Writing report to {context.ReportFile}...");
try
{
var markdown = sarifResults.ToMarkdown(context.ReportDepth, context.Heading);
var markdown = sarifResults.ToMarkdown(context.Depth, context.Heading);
File.WriteAllText(context.ReportFile, markdown);
context.WriteLine("Report generated successfully.");
}
Expand Down
7 changes: 5 additions & 2 deletions src/DemaConsulting.SarifMark/SelfTest/Validation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ private static void RunSarifReadingTest(Context context, DemaConsulting.TestResu
/// <param name="testResults">The test results collection.</param>
private static void RunMarkdownReportGenerationTest(Context context, DemaConsulting.TestResults.TestResults testResults)
{
var depthArgs = new[] { "--depth", context.Depth.ToString() };
var headingPrefix = new string('#', context.Depth);
RunValidationTest(
context,
testResults,
Expand All @@ -147,14 +149,15 @@ private static void RunMarkdownReportGenerationTest(Context context, DemaConsult
return "Report file not created";
}

if (reportContent.Contains("MockTool Analysis") &&
if (reportContent.Contains($"{headingPrefix} MockTool Analysis") &&
reportContent.Contains("Found 2 issues"))
{
return null;
}

return "Report file missing expected content";
});
},
depthArgs);
}

/// <summary>
Expand Down
18 changes: 16 additions & 2 deletions test/DemaConsulting.SarifMark.Tests/Cli/CliTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,21 @@ public void Cli_ReportParameter_SetsReportFilePath()
}

/// <summary>
/// Test that report-depth parameter sets the report depth in context.
/// Test that depth parameter sets the depth in context.
/// </summary>
[TestMethod]
public void Cli_DepthParameter_SetsDepth()
{
// Act
using var context = Context.Create(["--depth", "3"]);

// Assert
Assert.AreEqual(3, context.Depth);
Comment thread
Malcolmnixon marked this conversation as resolved.
Assert.AreEqual(0, context.ExitCode);
}

/// <summary>
/// Test that legacy report-depth parameter sets the report depth in context.
/// </summary>
[TestMethod]
public void Cli_ReportDepthParameter_SetsReportDepth()
Expand All @@ -228,7 +242,7 @@ public void Cli_ReportDepthParameter_SetsReportDepth()
using var context = Context.Create(["--report-depth", "3"]);

// Assert
Assert.AreEqual(3, context.ReportDepth);
Assert.AreEqual(3, context.Depth);
Assert.AreEqual(0, context.ExitCode);
}

Expand Down
56 changes: 51 additions & 5 deletions test/DemaConsulting.SarifMark.Tests/Cli/ContextTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,53 @@ public void Context_Create_ReportParameter_SetsReportFile()
}

/// <summary>
/// Test that creating a context with --report-depth parameter sets ReportDepth property.
/// Test that creating a context with --depth parameter sets Depth property.
/// </summary>
[TestMethod]
public void Context_Create_DepthParameter_SetsDepth()
{
// Act
using var context = Context.Create(["--depth", "3"]);

// Assert
Assert.AreEqual(3, context.Depth);
}

/// <summary>
/// Test that creating a context with --depth but no value throws exception.
/// </summary>
[TestMethod]
public void Context_Create_DepthWithoutValue_ThrowsArgumentException()
{
// Act & Assert
var exception = Assert.Throws<ArgumentException>(() => Context.Create(["--depth"]));
Assert.Contains("--depth requires", exception.Message);
}

/// <summary>
/// Test that creating a context with --depth and invalid value throws exception.
/// </summary>
[TestMethod]
public void Context_Create_DepthInvalidValue_ThrowsArgumentException()
{
// Act & Assert
var exception = Assert.Throws<ArgumentException>(() => Context.Create(["--depth", "invalid"]));
Assert.Contains("--depth requires a positive integer", exception.Message);
}

/// <summary>
/// Test that creating a context with --depth and zero value throws exception.
/// </summary>
[TestMethod]
public void Context_Create_DepthZero_ThrowsArgumentException()
{
// Act & Assert
var exception = Assert.Throws<ArgumentException>(() => Context.Create(["--depth", "0"]));
Assert.Contains("--depth requires a positive integer", exception.Message);
}

/// <summary>
/// Test that creating a context with legacy --report-depth parameter sets Depth property.
/// </summary>
[TestMethod]
public void Context_Create_ReportDepthParameter_SetsReportDepth()
Expand All @@ -433,11 +479,11 @@ public void Context_Create_ReportDepthParameter_SetsReportDepth()
using var context = Context.Create(["--report-depth", "3"]);

// Assert
Assert.AreEqual(3, context.ReportDepth);
Assert.AreEqual(3, context.Depth);
}

/// <summary>
/// Test that creating a context with --report-depth but no value throws exception.
/// Test that creating a context with legacy --report-depth but no value throws exception.
/// </summary>
[TestMethod]
public void Context_Create_ReportDepthWithoutValue_ThrowsArgumentException()
Expand All @@ -448,7 +494,7 @@ public void Context_Create_ReportDepthWithoutValue_ThrowsArgumentException()
}

/// <summary>
/// Test that creating a context with --report-depth and invalid value throws exception.
/// Test that creating a context with legacy --report-depth and invalid value throws exception.
/// </summary>
[TestMethod]
public void Context_Create_ReportDepthInvalidValue_ThrowsArgumentException()
Expand All @@ -459,7 +505,7 @@ public void Context_Create_ReportDepthInvalidValue_ThrowsArgumentException()
}

/// <summary>
/// Test that creating a context with --report-depth and zero value throws exception.
/// Test that creating a context with legacy --report-depth and zero value throws exception.
/// </summary>
[TestMethod]
public void Context_Create_ReportDepthZero_ThrowsArgumentException()
Expand Down
Loading