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
167 changes: 167 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Copilot Instructions

## Project Overview

This is **AsBuiltReport.System.Resources**, a PowerShell module for the [AsBuiltReport](https://github.com/AsBuiltReport) framework. It generates "As-Built" documentation reports (HTML, Word, Text) describing system resources (date/time, timezone, uptime, PowerShell host info, and top CPU processes) from a local or remote machine.

## Lint & Validation Commands

```powershell
# Validate the module manifest
Test-ModuleManifest .\AsBuiltReport.System.Resources\AsBuiltReport.System.Resources.psd1

# Run PSScriptAnalyzer with project settings
Invoke-ScriptAnalyzer -Path .\AsBuiltReport.System.Resources\Src -Settings .\.github\workflows\PSScriptAnalyzerSettings.psd1 -Recurse

# Run PSScriptAnalyzer on a single file
Invoke-ScriptAnalyzer -Path .\AsBuiltReport.System.Resources\Src\Private\Get-AbrDate.ps1 -Settings .\.github\workflows\PSScriptAnalyzerSettings.psd1
```

No automated test suite exists. PSScriptAnalyzer linting is the CI quality gate, enforced via `.github/workflows/PSScriptAnalyzer.yml` on every push and PR.

## Generating a Report (Manual Testing)

```powershell
# Install prerequisites
Install-Module AsBuiltReport.Core, AsBuiltReport.Chart, AsBuiltReport.Diagram -Force

# Generate a report against localhost
New-AsBuiltReport -Report System.Resources -Target localhost -Format Html -OutputFolderPath C:\Reports -Verbose

# Generate a default config file
New-AsBuiltReportConfig -Report System.Resources -FolderPath C:\Config
```

## Architecture

### Entry Point Flow

```
New-AsBuiltReport (AsBuiltReport.Core framework)
└─ Invoke-AsBuiltReport.System.Resources [Src/Public/]
└─ For each $Target:
├─ Get-AbrDate
├─ Get-AbrTimeZone
├─ Get-AbrUptime
│ └─ Get-SystemUptime (WMI fallback for PS 5.1)
├─ Get-AbrPSHost
└─ Get-AbrProcessInfo
├─ Get-AbrProcessDiagram
└─ Export-AbrDiagram
```

### Module Loader

`AsBuiltReport.System.Resources.psm1` auto-discovers and dot-sources all `.ps1` files under `Src/Public/` and `Src/Private/`, then exports **both** public and private functions. Private functions must be exported because the AsBuiltReport.Core framework invokes them within its document script block scope.

### Framework-Injected Variables

The AsBuiltReport.Core framework injects these variables into module scope before execution — do not redefine them:

| Variable | Purpose |
|---|---|
| `$ReportConfig` | Parsed JSON configuration object |
| `$InfoLevel` | Per-section detail level (0=disabled, 1=summary, 2=detailed) |
| `$Options` | Feature flags (diagrams, themes, exports) |
| `$Target` | Array of system names to document |
| `$System` | Current target being iterated |
| `$reportTranslate` | Localization string hashtable |
| `$Report` | Report metadata (ShowTableCaptions, etc.) |

### Report Section Pattern

Every section in `Src/Private/` follows the same structure:

1. **Narrow localization scope** — `$reportTranslate = $reportTranslate.GetAbr<Feature>`
2. **Collect data** — use standard PowerShell cmdlets or WMI
3. **Check InfoLevel** — wrap output in `if ($InfoLevel.<Section> -ge 1)` blocks
4. **Render with PScribo** — use `Section`, `Table`, `Paragraph`, `Image` etc.

### PowerShell Edition Detection

Use `$PSVersionTable.PSEdition` to branch between PS 5.1 (Desktop) and PS 7+ (Core):

```powershell
if ($PSVersionTable.PSEdition -eq 'Core') {
# Use modern cmdlets (e.g., Get-Uptime)
} else {
# Use WMI / CIM (e.g., Get-SystemUptime helper)
}
```

## Key Conventions

### Naming

- **Private functions:** `Get-Abr<Feature>` (e.g., `Get-AbrDate`, `Get-AbrProcessInfo`)
- **WMI/compatibility helpers:** `Get-System<Task>` (e.g., `Get-SystemUptime`)
- **Public entry point:** `Invoke-AsBuiltReport.System.Resources`
- One function per file; filename matches function name exactly.

### Table Data Pattern

Always build table rows as ordered hashtables promoted to `[PSCustomObject]`, using localized strings as keys:

```powershell
$InObj = [Ordered]@{
$($reportTranslate.Date) = $Date.ToString('yyyy-MM-dd')
$($reportTranslate.Hour) = $Date.ToString('HH:mm:ss')
}
$SystemDateInfo += [PSCustomObject]$InObj
```

### Table Rendering Pattern

Always set explicit `ColumnWidths` and conditionally add a caption:

```powershell
$TableParams = @{
Name = "Table Name"
List = $true
ColumnWidths = 40, 60
}
if ($Report.ShowTableCaptions) {
$TableParams['Caption'] = "- $($TableParams.Name)"
}
$data | Table @TableParams
```

### Localization

- Each language lives in `Language/<locale>/SystemResources.psd1` as a nested hashtable.
- Narrow to the relevant subtable at the top of each function: `$reportTranslate = $reportTranslate.GetAbr<Feature>`
- When adding new string keys, add them to **all 26+ language files** under `Language/`.

### Diagram Pipeline

Diagrams use Graphviz via `AsBuiltReport.Diagram`. The pipeline is always:
1. `Get-AbrProcessDiagram` — builds the graph object
2. `Export-AbrDiagram` — calls `New-Diagrammer`, embeds base64 in report, and optionally writes to disk

Diagram features are gated by `$Options.EnableDiagrams`.

### PSScriptAnalyzer Exclusions

The following rules are intentionally suppressed (see `.github/workflows/PSScriptAnalyzerSettings.psd1`):
- `PSUseToExportFieldsInManifest`
- `PSReviewUnusedParameter`
- `PSUseDeclaredVarsMoreThanAssignments`
- `PSAvoidGlobalVars`
- `PSUseSingularNouns`
- `PSAvoidUsingWriteHost`

Do not add `[SuppressMessage]` attributes for these — the settings file handles them globally.

### Code Style

- Indentation: **4 spaces** (no tabs)
- Line length: **115 characters** max
- Opening brace on same line; closing brace on its own line
- PascalCase for all function and parameter names
- Use correct PowerShell cmdlet casing (`Get-ChildItem`, not `get-childitem`)

## Branch & Release Strategy

- Feature branches merge into **`dev`** (never directly into `master`)
- `master` is the release branch; a GitHub release publication triggers the CI pipeline to publish to PowerShell Gallery
- Follow [Keep a Changelog](https://keepachangelog.com/) format for `CHANGELOG.md` entries
112 changes: 56 additions & 56 deletions .github/workflows/Release.yml
Original file line number Diff line number Diff line change
@@ -1,61 +1,61 @@
name: Publish PowerShell Module

on:
release:
types: [published]
release:
types: [published]

jobs:
publish-to-gallery:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- name: Set PSRepository to Trusted for PowerShell Gallery
shell: pwsh
run: |
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
- name: Install AsBuiltReport.Core module
shell: pwsh
run: |
Install-Module -Name AsBuiltReport.Core -Repository PSGallery -Force
- name: Install AsBuiltReport.Chart module
shell: pwsh
run: |
Install-Module -Name AsBuiltReport.Chart -Repository PSGallery -Force
- name: Install Diagrammer.Core module
shell: pwsh
run: |
Install-Module -Name Diagrammer.Core -Repository PSGallery -Force
- name: Test Module Manifest
shell: pwsh
run: |
Test-ModuleManifest .\AsBuiltReport.System.Resources.psd1
- name: Publish module to PowerShell Gallery
shell: pwsh
run: |
Publish-Module -Path ./ -NuGetApiKey ${{ secrets.PSGALLERY_API_KEY }} -Verbose
tweet:
needs: publish-to-gallery
runs-on: ubuntu-latest
steps:
- uses: Eomm/why-don-t-you-tweet@v2
# We don't want to tweet if the repository is not a public one
if: ${{ !github.event.repository.private }}
with:
# GitHub event payload
# https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release
tweet-message: "[New Release] ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}! Check out what's new! ${{ github.event.release.html_url }} #System #Resources #AsBuiltReport #PowerShell"
env:
TWITTER_CONSUMER_API_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }}
TWITTER_CONSUMER_API_SECRET: ${{ secrets.TWITTER_CONSUMER_API_SECRET }}
TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
bsky-post:
needs: publish-to-gallery
runs-on: ubuntu-latest
steps:
- uses: zentered/bluesky-post-action@v0.3.0
with:
post: "[New Release] ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}! Check out what's new! ${{ github.event.release.html_url }} #System #Resources #AsBuiltReport #PowerShell"
env:
BSKY_IDENTIFIER: ${{ secrets.BSKY_IDENTIFIER }}
BSKY_PASSWORD: ${{ secrets.BSKY_PASSWORD }}
publish-to-gallery:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- name: Set PSRepository to Trusted for PowerShell Gallery
shell: pwsh
run: |
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
- name: Install AsBuiltReport.Core module
shell: pwsh
run: |
Install-Module -Name AsBuiltReport.Core -Repository PSGallery -Force
- name: Install AsBuiltReport.Chart module
shell: pwsh
run: |
Install-Module -Name AsBuiltReport.Chart -Repository PSGallery -Force
- name: Install AsBuiltReport.Diagram module
shell: pwsh
run: |
Install-Module -Name AsBuiltReport.Diagram -Repository PSGallery -Force
- name: Test Module Manifest
shell: pwsh
run: |
Test-ModuleManifest .\AsBuiltReport.System.Resources\AsBuiltReport.System.Resources.psd1
- name: Publish module to PowerShell Gallery
shell: pwsh
run: |
Publish-Module -Path .\AsBuiltReport.System.Resources\ -NuGetApiKey ${{ secrets.PSGALLERY_API_KEY }} -Verbose
tweet:
needs: publish-to-gallery
runs-on: ubuntu-latest
steps:
- uses: Eomm/why-don-t-you-tweet@v2
# We don't want to tweet if the repository is not a public one
if: ${{ !github.event.repository.private }}
with:
# GitHub event payload
# https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release
tweet-message: "[New Release] ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}! Check out what's new! ${{ github.event.release.html_url }} #System #Resources #AsBuiltReport #PowerShell"
env:
TWITTER_CONSUMER_API_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }}
TWITTER_CONSUMER_API_SECRET: ${{ secrets.TWITTER_CONSUMER_API_SECRET }}
TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
bsky-post:
needs: publish-to-gallery
runs-on: ubuntu-latest
steps:
- uses: zentered/bluesky-post-action@v0.4.0
with:
post: "[New Release] ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}! Check out what's new! ${{ github.event.release.html_url }} #System #Resources #AsBuiltReport #PowerShell"
env:
BSKY_IDENTIFIER: ${{ secrets.BSKY_IDENTIFIER }}
BSKY_PASSWORD: ${{ secrets.BSKY_PASSWORD }}
14 changes: 0 additions & 14 deletions AsBuiltReport.System.Resources.psm1

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,7 @@
"PSHost": 2,
"ProcessInfo": 1
},
"HealthCheck": {}
"HealthCheck": {
"Uptime": true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
RootModule = 'AsBuiltReport.System.Resources.psm1'

# Version number of this module.
ModuleVersion = '0.1.2'
ModuleVersion = '0.1.3'

# Supported PSEditions
# CompatiblePSEditions = @()
Expand Down Expand Up @@ -59,11 +59,11 @@
},
@{
ModuleName = 'AsBuiltReport.Chart';
ModuleVersion = '0.2.0'
ModuleVersion = '0.3.0'
},
@{
ModuleName = 'Diagrammer.Core';
ModuleVersion = '0.2.38'
ModuleName = 'AsBuiltReport.Diagram';
ModuleVersion = '1.0.5'
}
)

Expand Down
32 changes: 32 additions & 0 deletions AsBuiltReport.System.Resources/AsBuiltReport.System.Resources.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Module loader for AsBuiltReport.System.Resources
#
# This script is executed automatically by PowerShell when the module is imported. It uses a
# common AsBuiltReport pattern of separating functions into two directories:
#
# Src/Public/ - Functions exported to the caller (visible after Import-Module).
# Typically a single Invoke-AsBuiltReport.<ModuleName> entry point.
# Src/Private/ - Internal helper functions used only within the module. They are dot-sourced
# into the module scope but are also exported so that AsBuiltReport.Core can
# call them from within the report document script block, which runs in the
# caller's scope.
#
# Any file that fails to dot-source (e.g. due to a syntax error) will emit an error message
# via Write-Error but will not prevent the remaining files from being loaded.

# Collect all public and private function files.
$Public = @(Get-ChildItem -Path $PSScriptRoot\Src\Public\*.ps1 -ErrorAction SilentlyContinue)
$Private = @(Get-ChildItem -Path $PSScriptRoot\Src\Private\*.ps1 -ErrorAction SilentlyContinue)

foreach ($Module in @($Public + $Private)) {
try {
. $Module.FullName
} catch {
Write-Error -Message "Failed to import function $($Module.FullName): $_"
}
}

# Export public functions by name so they are available to module consumers.
Export-ModuleMember -Function $Public.BaseName
# Export private functions so that AsBuiltReport.Core can invoke them from within the
# PScribo document script block, which executes in the caller's session scope.
Export-ModuleMember -Function $Private.BaseName
File renamed without changes
File renamed without changes
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@
ParagraphDetail = تفصل الأقسام التالية وقت تشغيل النظام.
ParagraphSummary = يلخص الجدول التالي وقت تشغيل النظام.
Heading = وقت التشغيل

Uptime = وقت التشغيل
HealthCheck = التحقق من الصحة
CorrectiveActions = الإجراءات التصحيحية:
Downtime = وقت تشغيل النظام أقل من 24 ساعة. راجع سجلات النظام لتحديد أي أحداث قد تكون تسببت في التوقف غير المتوقع.
'@

# Get-AbrPSHost
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,10 @@
ParagraphDetail = Následující části podrobně popisují dobu provozu systému.
ParagraphSummary = Následující tabulka shrnuje dobu provozu systému.
Heading = Doba provozu

Uptime = Doba provozu
HealthCheck = Kontrola stavu
CorrectiveActions = Opatření k nápravě:
Downtime = Doba provozu systému je kratší než 24 hodin. Zkontrolujte systémové protokoly, abyste zjistili jakékoli události, které mohly způsobit neočekávané přerušení.
'@

# Get-AbrPSHost
Expand Down
Loading