Skip to content

Commit c8b5bc1

Browse files
authored
Merge pull request #36 from AsBuiltReport/dev
v0.3.2 public release
2 parents 852281e + 6799af1 commit c8b5bc1

13 files changed

Lines changed: 951 additions & 43 deletions

.github/copilot-instructions.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# AsBuiltReport.Chart — Copilot Instructions
2+
3+
## Architecture Overview
4+
5+
This project is a **hybrid C# + PowerShell module**. The C# layer compiles into platform-specific DLLs; the PowerShell layer loads the right DLL at import time and exposes cmdlets.
6+
7+
```
8+
Sources/ ← C# library (ScottPlot + SkiaSharp, netstandard2.0)
9+
Chart.cs ← Static base class; all chart settings live here as static props
10+
PieChart.cs / BarChart.cs / StackedBarChart.cs / SignalChart.cs ← Chart implementations
11+
PowerShell/ ← One PSCmdlet class per chart type (New-*Chart cmdlets)
12+
Enums/Enums.cs ← All shared enums (BasicColors, Formats, ColorPalettes, etc.)
13+
14+
AsBuiltReport.Chart/ ← PowerShell module
15+
AsBuiltReport.Chart.psm1 ← Loads the correct DLL based on PSEdition + OS + architecture
16+
AsBuiltReport.Chart.psd1 ← Module manifest; exports: New-PieChart, New-BarChart,
17+
New-StackedBarChart, New-SingleStackedBarChart, New-SignalChart
18+
Src/Assemblies/ ← Pre-compiled DLLs, organized by platform:
19+
Core/linux-x64/
20+
Core/windows-x64/
21+
Core/mac-osx/osx-arm64/ and osx-x64/
22+
Desktop/windows-x64/ ← Windows PowerShell 5.1 (PSEdition Desktop)
23+
24+
Tests/
25+
AsBuiltReport.Chart.Functions.Tests.ps1 ← Pester v5 tests
26+
Invoke-Tests.ps1 ← Test runner script
27+
```
28+
29+
## Build & Test Commands
30+
31+
### Build the C# library (must rebuild after any Sources/ change)
32+
33+
```bash
34+
# Linux
35+
dotnet publish ./Sources -c Release -r linux-x64
36+
cp Sources/bin/Release/netstandard2.0/linux-x64/publish/* AsBuiltReport.Chart/Src/Assemblies/Core/linux-x64/
37+
38+
# Windows (PowerShell 7)
39+
dotnet publish ./Sources -c Release -r win-x64
40+
Copy-Item ./Sources/bin/Release/netstandard2.0/win-x64/publish/* ./AsBuiltReport.Chart/Src/Assemblies/Core/windows-x64 -Recurse
41+
42+
# Windows PowerShell 5.1 (Desktop)
43+
Copy-Item ./Sources/bin/Release/netstandard2.0/win-x64/publish/* ./AsBuiltReport.Chart/Src/Assemblies/Desktop/windows-x64 -Recurse
44+
45+
# macOS (Apple Silicon)
46+
dotnet publish ./Sources -c Release -r osx-arm64
47+
Copy-Item ./Sources/bin/Release/netstandard2.0/osx-arm64/publish/* ./AsBuiltReport.Chart/Src/Assemblies/Core/mac-osx/osx-arm64 -Recurse
48+
```
49+
50+
### Run all Pester tests
51+
52+
```powershell
53+
.\Tests\Invoke-Tests.ps1
54+
```
55+
56+
### Run a single test (by name filter)
57+
58+
```powershell
59+
$cfg = New-PesterConfiguration
60+
$cfg.Run.Path = '.\Tests'
61+
$cfg.Filter.FullName = '*New-PieChart*'
62+
Invoke-Pester -Configuration $cfg
63+
```
64+
65+
### Lint (PSScriptAnalyzer)
66+
67+
```powershell
68+
Invoke-ScriptAnalyzer -Path . -Recurse -Settings .\.github\workflows\PSScriptAnalyzerSettings.psd1
69+
```
70+
71+
Excluded rules: `PSUseToExportFieldsInManifest`, `PSReviewUnusedParameter`, `PSUseDeclaredVarsMoreThanAssignments`, `PSAvoidGlobalVars`.
72+
73+
## Key Conventions
74+
75+
### Adding a new chart type
76+
77+
Every new chart type requires changes in both layers:
78+
79+
1. **C# side** (`Sources/`): Add `<NewType>.cs` (chart logic) and `PowerShell/<NewType>Pwsh.cs` (the PSCmdlet class).
80+
2. **PowerShell side**: Add the exported function name to `FunctionsToExport` in `AsBuiltReport.Chart.psd1`.
81+
3. **Tests**: Add a `Context '<New-XChart>'` block in `Tests/AsBuiltReport.Chart.Functions.Tests.ps1`.
82+
83+
### Chart static-state pattern
84+
85+
`Chart` properties are **static**. Every `ProcessRecord()` override **must** call `Chart.Reset()` first to clear state from the previous invocation before setting new property values.
86+
87+
### Cross-platform path construction
88+
89+
Use `[System.IO.Path]::DirectorySeparatorChar` (not hardcoded `/` or `\`) when building paths in `.psm1` or C# code. The `.psm1` uses the format string pattern:
90+
91+
```powershell
92+
"$PSScriptRoot{0}Src{0}Assemblies{0}..." -f [System.IO.Path]::DirectorySeparatorChar
93+
```
94+
95+
### StackedBarChart Values layout
96+
97+
`-Values` accepts a **jagged array**: one inner `double[]` per bar/label. Wrapping a single array requires the unary comma operator to prevent flattening:
98+
99+
```powershell
100+
New-StackedBarChart -Values @(,[double[]]@(1, 2)) -Labels @('A') -LegendCategories @('X','Y') ...
101+
```
102+
103+
### OutputFolderPath validation
104+
105+
The `ValidatePath` custom attribute (defined in `PieChartPwsh.cs`) validates that the directory exists. Pass `-OutputFolderPath $TestDrive` in tests to use Pester's temp path.
106+
107+
### Chart cmdlet return value
108+
109+
All `New-*Chart` cmdlets return a `System.IO.FileSystemInfo` object (the saved file), not a string path. Tests assert `Should -BeOfType 'System.IO.FileSystemInfo'`.
110+
111+
### Branching
112+
113+
- Target PRs against the **`dev`** branch.
114+
- Follow [Keep a Changelog](https://keepachangelog.com/) when updating `CHANGELOG.md`.
115+
- Follow the [PowerShell Best Practices and Style Guide](https://github.com/PoshCode/PowerShellPracticeAndStyle).
116+
- Use PascalCasing for all public member, type, and namespace names.

.github/workflows/Release.yml

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Publish PowerShell Module
22

33
on:
44
release:
5-
types: [published]
5+
types: [ published ]
66

77
jobs:
88
publish-to-psgallery:
@@ -18,23 +18,28 @@ jobs:
1818
- name: Build the library for MacOS X64
1919
run: dotnet publish ./Sources -c Release -r osx-x64
2020
- name: Copy the library for MacOS X64
21-
run: copy ./Sources/bin/Release/netstandard2.0/osx-x64/publish/*.* ./AsBuiltReport.Chart/Src/Assemblies/Core/mac-osx/osx-x64
21+
run: copy ./Sources/bin/Release/netstandard2.0/osx-x64/publish/*.*
22+
./AsBuiltReport.Chart/Src/Assemblies/Core/mac-osx/osx-x64
2223
- name: Build the library for MacOS Arm64
2324
run: dotnet publish ./Sources -c Release -r osx-arm64
2425
- name: Copy the library for MacOS Arm64
25-
run: copy ./Sources/bin/Release/netstandard2.0/osx-arm64/publish/*.* ./AsBuiltReport.Chart/Src/Assemblies/Core/mac-osx/osx-arm64
26+
run: copy ./Sources/bin/Release/netstandard2.0/osx-arm64/publish/*.*
27+
./AsBuiltReport.Chart/Src/Assemblies/Core/mac-osx/osx-arm64
2628
- name: Build the library for Linux
2729
run: dotnet publish ./Sources -c Release -r linux-x64
2830
- name: Copy the library for Linux
29-
run: copy ./Sources/bin/Release/netstandard2.0/linux-x64/publish/*.* ./AsBuiltReport.Chart/Src/Assemblies/Core/linux-x64
31+
run: copy ./Sources/bin/Release/netstandard2.0/linux-x64/publish/*.*
32+
./AsBuiltReport.Chart/Src/Assemblies/Core/linux-x64
3033
- name: Build the library for Windows
3134
run: dotnet publish ./Sources -c Release -r win-x64
3235
- name: Copy the library for Windows
33-
run: copy ./Sources/bin/Release/netstandard2.0/win-x64/publish/*.* ./AsBuiltReport.Chart/Src/Assemblies/Core/windows-x64
36+
run: copy ./Sources/bin/Release/netstandard2.0/win-x64/publish/*.*
37+
./AsBuiltReport.Chart/Src/Assemblies/Core/windows-x64
3438
- name: Build the library for Windows PowerShell
3539
run: dotnet publish ./Sources -c Release -r win-x64
3640
- name: Copy the library for Windows PowerShell
37-
run: copy ./Sources/bin/Release/netstandard2.0/win-x64/publish/*.* ./AsBuiltReport.Chart/Src/Assemblies/Desktop/windows-x64
41+
run: copy ./Sources/bin/Release/netstandard2.0/win-x64/publish/*.*
42+
./AsBuiltReport.Chart/Src/Assemblies/Desktop/windows-x64
3843
- name: Run tests
3944
run: dotnet test --no-build --verbosity normal ./Sources
4045
- name: Set PSRepository to Trusted for PowerShell Gallery
@@ -49,30 +54,30 @@ jobs:
4954
shell: pwsh
5055
run: |
5156
Publish-Module -Path .\AsBuiltReport.Chart\ -NuGetApiKey ${{ secrets.PSGALLERY_API_KEY }} -Verbose
52-
tweet:
53-
needs: publish-to-psgallery
54-
runs-on: ubuntu-latest
55-
steps:
56-
- uses: Eomm/why-don-t-you-tweet@v2
57-
# We don't want to tweet if the repository is not a public one
58-
if: ${{ !github.event.repository.private }}
59-
with:
60-
# GitHub event payload
61-
# https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release
62-
tweet-message: "[New Release] ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}! Check out what's new! ${{ github.event.release.html_url }} #AsBuiltReport #PowerShell"
63-
env:
64-
TWITTER_CONSUMER_API_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }}
65-
TWITTER_CONSUMER_API_SECRET: ${{ secrets.TWITTER_CONSUMER_API_SECRET }}
66-
TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
67-
TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
68-
bsky-post:
69-
needs: publish-to-psgallery
70-
runs-on: ubuntu-latest
71-
steps:
72-
- uses: zentered/bluesky-post-action@v0.3.0
73-
with:
74-
post: "[New Release] ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}! Check out what's new! ${{ github.event.release.html_url }} #AsBuiltReport #PowerShell"
75-
env:
76-
BSKY_IDENTIFIER: ${{ secrets.BSKY_IDENTIFIER }}
77-
BSKY_PASSWORD: ${{ secrets.BSKY_PASSWORD }}
57+
# tweet:
58+
# needs: publish-to-psgallery
59+
# runs-on: ubuntu-latest
60+
# steps:
61+
# - uses: Eomm/why-don-t-you-tweet@v2
62+
# # We don't want to tweet if the repository is not a public one
63+
# if: ${{ !github.event.repository.private }}
64+
# with:
65+
# # GitHub event payload
66+
# # https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release
67+
# tweet-message: "[New Release] ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}! Check out what's new! ${{ github.event.release.html_url }} #AsBuiltReport #PowerShell"
68+
# env:
69+
# TWITTER_CONSUMER_API_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }}
70+
# TWITTER_CONSUMER_API_SECRET: ${{ secrets.TWITTER_CONSUMER_API_SECRET }}
71+
# TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
72+
# TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
73+
# bsky-post:
74+
# needs: publish-to-psgallery
75+
# runs-on: ubuntu-latest
76+
# steps:
77+
# - uses: zentered/bluesky-post-action@v0.3.0
78+
# with:
79+
# post: "[New Release] ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}! Check out what's new! ${{ github.event.release.html_url }} #AsBuiltReport #PowerShell"
80+
# env:
81+
# BSKY_IDENTIFIER: ${{ secrets.BSKY_IDENTIFIER }}
82+
# BSKY_PASSWORD: ${{ secrets.BSKY_PASSWORD }}
7883

AsBuiltReport.Chart/AsBuiltReport.Chart.psd1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
RootModule = 'AsBuiltReport.Chart.psm1'
1313

1414
# Version number of this module.
15-
ModuleVersion = '0.3.1'
15+
ModuleVersion = '0.3.2'
1616

1717
# Supported PSEditions
1818
# CompatiblePSEditions = @()
@@ -69,7 +69,7 @@
6969
# NestedModules = @()
7070

7171
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
72-
FunctionsToExport = 'New-PieChart', 'New-BarChart', 'New-StackedBarChart', 'New-SignalChart'
72+
FunctionsToExport = 'New-PieChart', 'New-BarChart', 'New-StackedBarChart', 'New-SignalChart', 'New-SingleStackedBarChart'
7373

7474
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
7575
# CmdletsToExport = '*'

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.3.2] - 2026-05-05
9+
10+
### Added
11+
12+
- Add New-SingleStackedBarChart cmdlet and update module exports
13+
- Add pester test to validate the functionality of the New-SingleStackedBarChart cmdlet
14+
15+
### Changed
16+
17+
- Update module version to 0.3.2
18+
819
## [0.3.1] - 2026-04-23
920

1021
### Changed

Examples/Example10.ps1

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<#
2+
.SYNOPSIS
3+
Example 10 - Basic Single Stacked Bar Chart
4+
5+
.DESCRIPTION
6+
This example demonstrates how to create a Single Stacked Bar Chart using the
7+
AsBuiltReport.Chart module.
8+
9+
A Single Stacked Bar Chart displays one bar divided into stacked segments, each representing
10+
a percentage contribution to the total. It is ideal for visualising how a whole is broken
11+
down into parts — for example, storage capacity usage across different categories.
12+
13+
The chart displays a vSAN datastore capacity breakdown as a percentage of total raw capacity:
14+
- VM Data : 55 %
15+
- Snapshots : 15 %
16+
- Deduplication : 10 %
17+
- Overhead : 12 %
18+
- Free : 8 %
19+
#>
20+
21+
[CmdletBinding()]
22+
param (
23+
[System.IO.DirectoryInfo] $Path = (Get-Location).Path,
24+
[string] $Format = 'png'
25+
)
26+
27+
<#
28+
Starting with PowerShell v3, modules are auto-imported when needed. Importing the module here
29+
ensures clarity and avoids ambiguity.
30+
#>
31+
32+
# Import-Module AsBuiltReport.Chart -Force -Verbose:$false
33+
34+
<#
35+
Since the chart output is a file, specify the output folder path using $OutputFolderPath.
36+
#>
37+
38+
$OutputFolderPath = Resolve-Path $Path
39+
40+
<#
41+
Define the data to be displayed in the chart.
42+
43+
For a Single Stacked Bar Chart:
44+
- $Values is a flat double array. Each element is the percentage value of one segment.
45+
- $Label is a single string that labels the one bar on the category axis.
46+
- $LegendCategories is an array of strings, one name per segment, shown in the legend.
47+
48+
In this example, the segment labels rendered inside the bar include the '%' suffix
49+
because `-ValueSuffix '%'` is specified when the chart is created.
50+
The value axis (Y-axis for Vertical, X-axis for Horizontal) is also formatted as percentages.
51+
#>
52+
53+
$ChartTitle = 'vSAN Datastore - Capacity Breakdown (%)'
54+
$Label = 'Total'
55+
$LegendCategories = @('VM Data', 'Snapshots', 'Deduplication', 'Overhead', 'Free')
56+
$Values = [double[]]@(55, 15, 10, 12, 8)
57+
58+
<#
59+
The New-SingleStackedBarChart cmdlet generates the Single Stacked Bar Chart image.
60+
61+
-Title : Sets the chart title displayed at the top of the image.
62+
-Values : Flat double array — one value per segment, expressed as a percentage.
63+
-Label : String label for the single bar on the category axis.
64+
-LegendCategories : Array of segment names shown in the legend.
65+
-EnableLegend : Enables the legend on the chart.
66+
-LegendOrientation : Sets legend layout direction (Vertical or Horizontal).
67+
-LegendAlignment : Positions the legend (e.g. LowerCenter, UpperRight).
68+
-LegendFontSize : Sets the legend text font size in points.
69+
-LabelFontSize : Sets the font size of the axis and segment labels.
70+
-AreaOrientation : Vertical (default) renders the bar growing upward; Horizontal grows right.
71+
-Width : Output image width in pixels.
72+
-Height : Output image height in pixels.
73+
-Format : Output file format (e.g. png, jpg, svg).
74+
-OutputFolderPath : Directory where the generated chart file will be saved.
75+
-Filename : Name of the output file (without extension).
76+
-ValueSuffix : Appends a suffix to the segment labels and value axis labels (e.g. '%').
77+
#>
78+
79+
New-SingleStackedBarChart `
80+
-Title $ChartTitle `
81+
-Values $Values `
82+
-Label $Label `
83+
-LegendCategories $LegendCategories `
84+
-EnableLegend `
85+
-LegendOrientation Horizontal `
86+
-LegendAlignment LowerCenter `
87+
-LegendFontSize 12 `
88+
-LabelFontSize 13 `
89+
-AreaOrientation Vertical `
90+
-Width 400 `
91+
-Height 500 `
92+
-Format $Format `
93+
-OutputFolderPath $OutputFolderPath `
94+
-Filename 'Example10-SingleStackedBarChart' `
95+
-ValueSuffix '%'

0 commit comments

Comments
 (0)