Skip to content

Commit 4f9ce75

Browse files
SamErdeCopilot
andauthored
Create a build script for all functions in a consolidated PSM1 file (#1586)
* fix(style): Fix formatting in optimization plan markdown for clarity and consistency * fix(gitignore): Add module build output directory to .gitignore * feat(build): Add script to consolidate Maester PowerShell module and assets * fix(style): Improve code formatting and consistency in Build-MaesterModule.ps1 * feat(docs): Add comprehensive build documentation for Maester PowerShell module * feat(build): Add -Format parameter to normalize source file indentation during consolidation * feat(docs): Update Build-MaesterModule.md to include -Format parameter details for indentation normalization * feat(docs): Add TODO for simplified README creation in Build-MaesterModule.ps1 * Update build/Build-MaesterModule.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * feat(build): Update .gitignore to refine module and test result exclusions * feat(docs): Remove README.md from the list of copied files in Build-MaesterModule.md * feat(build): Enhance safety checks for OutputRoot and refine function export logic * Add an additional safety check to prevent removal of paths outside the repository root --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent c6604d2 commit 4f9ce75

3 files changed

Lines changed: 802 additions & 5 deletions

File tree

build/Build-MaesterModule.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# Build-MaesterModule
2+
3+
Builds the Maester PowerShell module into a consolidated, publishable artifact
4+
under `./module/`. The source tree (`powershell/`, `tests/`) is never modified.
5+
6+
## Usage
7+
8+
```powershell
9+
# Standard build
10+
./build/Build-MaesterModule.ps1
11+
12+
# Build with indentation normalization (requires PSScriptAnalyzer)
13+
./build/Build-MaesterModule.ps1 -Format
14+
15+
# Build with import-time profiling
16+
./build/Build-MaesterModule.ps1 -Profile
17+
18+
# Custom output location
19+
./build/Build-MaesterModule.ps1 -OutputRoot ./out
20+
```
21+
22+
## Parameters
23+
24+
| Parameter | Type | Default | Description |
25+
| ----------- | ------ | --------- | ------------- |
26+
| `SourceRoot` | string | `../powershell` | Path to the module source directory |
27+
| `TestsRoot` | string | `../tests` | Path to the test suites directory |
28+
| `OutputRoot` | string | `../module` | Output directory (cleaned on every run) |
29+
| `Format` | switch || Normalize indentation to 4 spaces via `Invoke-Formatter` (requires PSScriptAnalyzer) |
30+
| `Profile` | switch || Measure and report `Import-Module` time |
31+
32+
## Build Phases
33+
34+
```mermaid
35+
flowchart TD
36+
A["Phase A\nClean output directory"]
37+
B["Phase B\nAST-parse public functions"]
38+
C["Phase C\nConsolidate Maester.psm1"]
39+
D["Phase D\nConsolidate OrcaClasses.ps1"]
40+
E["Phase E\nCopy static assets"]
41+
F["Phase F\nUpdate module manifest"]
42+
G["Phase G\nCopy test suites"]
43+
H{"Profile?"}
44+
I["Phase H\nImport-Module timing"]
45+
Done["Build complete"]
46+
47+
A --> B
48+
B --> C
49+
C --> D
50+
D --> E
51+
E --> F
52+
F --> G
53+
G --> H
54+
H -- Yes --> I --> Done
55+
H -- No --> Done
56+
```
57+
58+
### Phase A — Clean output directory
59+
60+
Removes and recreates the output directory (`./module/` by default) to ensure a
61+
clean build with no stale artifacts.
62+
63+
### Phase B — AST-parse public functions
64+
65+
Scans every `.ps1` file under `powershell/public/` (excluding `*.Tests.ps1`)
66+
using the PowerShell AST parser to discover top-level function definitions.
67+
68+
- Only functions matching the `Verb-Noun` naming convention are exported.
69+
- Helper functions without a `-` separator are logged and skipped.
70+
- Duplicate function names across files are deduplicated and logged.
71+
- Functions with unapproved verbs or mismatched filenames generate warnings.
72+
73+
### Phase C — Consolidate Maester.psm1
74+
75+
Concatenates all internal and public `.ps1` source files into a single
76+
`Maester.psm1`, organized as:
77+
78+
1. **Module preamble**`#Requires`, `$__MtSession` initialization
79+
2. **Internal functions** — from `powershell/internal/` (excluding
80+
`check-ORCA*.ps1`, which go to Phase D)
81+
3. **Public functions** — from `powershell/public/`
82+
4. **Export-ModuleMember** — auto-generated function and alias exports
83+
5. **Manifest loader**`Import-PowerShellDataFile` for runtime metadata
84+
85+
Two transformations are applied to each source file:
86+
87+
- **Preamble stripping** — Removes file-level `[SuppressMessageAttribute]`,
88+
`param()`, `using module`, and `# Generated by` lines that are only valid at
89+
the top of standalone `.ps1` files. Attributes inside function bodies are
90+
preserved.
91+
- **`$PSScriptRoot` path adjustment** — Strips parent-directory traversals
92+
(`../`) that were needed in the original subdirectory structure but are
93+
incorrect after consolidation to a flat module root. Any remaining
94+
`$PSScriptRoot/..` references trigger a warning for manual review.
95+
- **Indentation normalization** (with `-Format`) — Runs `Invoke-Formatter`
96+
per file to enforce 4-space indentation. Only the
97+
`PSUseConsistentIndentation` rule is applied. If PSScriptAnalyzer is not
98+
installed, a warning is emitted and formatting is skipped.
99+
100+
### Phase D — Consolidate OrcaClasses.ps1
101+
102+
Merges the ORCA class hierarchy into a single `OrcaClasses.ps1`:
103+
104+
1. **Base classes and enums** from `orcaClass.psm1` (preamble preserved since
105+
this file runs standalone via `ScriptsToProcess`)
106+
2. **Derived check classes** from each `check-ORCA*.ps1` file, with preambles
107+
stripped and `using module` directives removed (the base classes are now
108+
defined inline above)
109+
110+
This file is registered as `ScriptsToProcess` in the manifest so that class
111+
definitions are available before the module's `.psm1` loads.
112+
113+
When `-Format` is specified, each derived check class file is also passed
114+
through `Invoke-Formatter` for indentation normalization.
115+
116+
### Phase E — Copy static assets
117+
118+
Copies unchanged files to the output directory:
119+
120+
- `assets/` directory (icons, images)
121+
- `Maester.Format.ps1xml` (type formatting)
122+
123+
This phase runs before the manifest update (Phase F) so that
124+
`FormatsToProcess` references can be validated by `Update-ModuleManifest`.
125+
126+
### Phase F — Update module manifest
127+
128+
Copies the source `Maester.psd1` to the output directory and updates:
129+
130+
- `FunctionsToExport` — set to the sorted, deduplicated list from Phase B
131+
- `ScriptsToProcess` — set to `@('OrcaClasses.ps1')`
132+
133+
All other manifest fields (version, GUID, `RequiredModules`,
134+
`AliasesToExport`, etc.) are preserved from the source.
135+
136+
### Phase G — Copy test suites
137+
138+
Copies the `tests/` directory to `maester-tests/` in the output. Tests are
139+
copied as-is and not consolidated.
140+
141+
### Phase H — Build profiling (optional)
142+
143+
When `-Profile` is specified, imports the built module and reports:
144+
145+
- `Import-Module` wall-clock time
146+
- Number of exported commands
147+
148+
The module is unloaded after profiling.
149+
150+
## Output Structure
151+
152+
```text
153+
module/
154+
├── assets/ # Icons and images
155+
├── maester-tests/ # Test suites (copied from tests/)
156+
├── Maester.Format.ps1xml # Type formatting definitions
157+
├── Maester.psd1 # Updated module manifest
158+
├── Maester.psm1 # Consolidated module script
159+
└── OrcaClasses.ps1 # Consolidated ORCA class definitions
160+
```
161+
162+
## Design Notes
163+
164+
- **Source is never modified.** The build reads from `powershell/` and `tests/`
165+
and writes exclusively to the output directory.
166+
- **Deterministic output.** Files are sorted by full path before concatenation.
167+
The `FunctionsToExport` list is sorted alphabetically. Repeated builds from
168+
the same source produce identical output.
169+
- **Encoding.** All generated PowerShell files use UTF-8 with BOM (`utf8BOM`),
170+
matching the project convention.
171+
- **Preamble stripping is position-aware.** Only lines in the file-level
172+
preamble region (before the first function/class definition) are stripped.
173+
Identical attributes inside function bodies are preserved.
174+
- **The hardcoded PSM1 preamble** (module header, `#Requires`,
175+
`$__MtSession`) is extracted from the source `Maester.psm1`. If new session
176+
variables are added to the source, they must also be added to the build
177+
script's preamble.

0 commit comments

Comments
 (0)