Skip to content

Commit 73334c9

Browse files
authored
Merge pull request #14 from tablackburn/chore/template-sync
Template polish: SemVer manifest tests, init refactor, analyzer config, README split
2 parents c1bab46 + dd64c22 commit 73334c9

12 files changed

Lines changed: 849 additions & 113 deletions

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# The docs are generated by the build script and should be considered artifacts
2+
docs/en-US/* linguist-generated

.markdownlint-cli2.jsonc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/DavidAnson/markdownlint-cli2/v0.19.1/schema/markdownlint-cli2-config-schema.json",
3+
"config": {
4+
"MD013": {
5+
"tables": false,
6+
"code_blocks": false
7+
},
8+
"MD024": {
9+
"siblings_only": true
10+
}
11+
},
12+
"ignores": [
13+
"AGENTS.md",
14+
// Intentionally narrow: only ignores docs/en-US (platyPS-generated help) so that
15+
// other markdown files in docs/ are still linted. No other language directories
16+
// are expected.
17+
"docs/en-US/**",
18+
"instructions/**"
19+
]
20+
}

Initialize-Template.ps1

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,8 @@ if (Test-Path -Path $templateModuleFolder) {
241241
# Rename example function files
242242
$publicFolder = Join-Path -Path $moduleFolder -ChildPath 'Public'
243243
$privateFolder = Join-Path -Path $moduleFolder -ChildPath 'Private'
244-
$testPublicFolder = Join-Path -Path $PSScriptRoot -ChildPath 'tests\Unit\Public'
245-
$testPrivateFolder = Join-Path -Path $PSScriptRoot -ChildPath 'tests\Unit\Private'
244+
$testPublicFolder = Join-Path -Path $PSScriptRoot -ChildPath 'tests/Unit/Public'
245+
$testPrivateFolder = Join-Path -Path $PSScriptRoot -ChildPath 'tests/Unit/Private'
246246

247247
$foldersToCheck = @($publicFolder, $privateFolder, $testPublicFolder, $testPrivateFolder)
248248

@@ -262,6 +262,31 @@ if (Test-Path -Path $templateModuleFolder) {
262262
Write-Host ' Renamed example function files' -ForegroundColor Green
263263
}
264264

265+
# Rename files in docs/en-US/ that contain {{ModuleName}} placeholder (e.g., about_{{ModuleName}}.help.md)
266+
$docsFolder = Join-Path -Path $PSScriptRoot -ChildPath 'docs/en-US'
267+
if (Test-Path -Path $docsFolder) {
268+
$docsFiles = Get-ChildItem -Path $docsFolder -File | Where-Object {
269+
$_.Name -match '\{\{ModuleName\}\}'
270+
}
271+
foreach ($file in $docsFiles) {
272+
$newName = $file.Name -replace '\{\{ModuleName\}\}', $ModuleName
273+
Rename-Item -Path $file.FullName -NewName $newName
274+
Write-Verbose "Renamed: $($file.Name) -> $newName"
275+
}
276+
if ($docsFiles) {
277+
Write-Host " Renamed docs/en-US files" -ForegroundColor Green
278+
}
279+
}
280+
281+
# Replace template-facing README.md with the module-facing README.template.md
282+
# (placeholders inside README.template.md were already substituted by the file-processing loop above)
283+
$readmeTemplate = Join-Path -Path $PSScriptRoot -ChildPath 'README.template.md'
284+
$readmePath = Join-Path -Path $PSScriptRoot -ChildPath 'README.md'
285+
if (Test-Path -Path $readmeTemplate) {
286+
Move-Item -Path $readmeTemplate -Destination $readmePath -Force
287+
Write-Host ' Generated module README.md from template' -ForegroundColor Green
288+
}
289+
265290
# Initialize Git repository if requested
266291
if (-not $NoGitInit) {
267292
$gitFolder = Join-Path -Path $PSScriptRoot -ChildPath '.git'
@@ -303,7 +328,7 @@ Write-Host '========================================' -ForegroundColor Green
303328
Write-Host ''
304329
Write-Host 'Next steps:' -ForegroundColor Cyan
305330
Write-Host " 1. Review the generated files in the $ModuleName folder"
306-
Write-Host ' 2. Update the README.md with your project details'
331+
Write-Host ' 2. Review README.md and adjust to taste'
307332
Write-Host ' 3. Add your functions to the Public/ and Private/ folders'
308333
Write-Host ' 4. Run ./build.ps1 -Task Test to verify everything works'
309334
Write-Host ' 5. Push to your GitHub repository'

PSScriptAnalyzerSettings.psd1

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
1+
# https://learn.microsoft.com/en-us/powershell/utility-modules/psscriptanalyzer/using-scriptanalyzer
12
@{
2-
IncludeRules = @('*')
3+
IncludeDefaultRules = $true
4+
5+
IncludeRules = @(
6+
# Default rules
7+
'PS*'
8+
)
9+
10+
# If IncludeRules and ExcludeRules are empty, all rules will be applied
11+
ExcludeRules = @()
12+
13+
Rules = @{
14+
# PSUseCompatibleSyntax = @{
15+
# # This turns the rule on (setting it to false will turn it off)
16+
# Enable = $true
17+
18+
# # List the targeted versions of PowerShell here
19+
# TargetVersions = @(
20+
# '5.1',
21+
# '7.2'
22+
# )
23+
# }
24+
# PSUseCompatibleCmdlets = @{
25+
# compatibility = @('core-7.2.0-windows')
26+
# }
27+
}
328
}

README.md

Lines changed: 108 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,122 @@
1-
# {{ModuleName}}
2-
3-
{{Description}}
4-
5-
## Installation
6-
7-
### From PowerShell Gallery
8-
9-
```powershell
10-
Install-Module -Name {{ModuleName}} -Scope CurrentUser
11-
```
12-
13-
### From Source
14-
15-
```powershell
16-
git clone {{ProjectUri}}.git
17-
cd {{ModuleName}}
18-
./build.ps1 -Task Build -Bootstrap
19-
Import-Module ./Output/{{ModuleName}}/*/{{ModuleName}}.psd1
1+
# PowerShell Module Template
2+
3+
A GitHub repository template for building, testing, and publishing PowerShell modules. Click **Use this template** at the top of the repo, run a one-shot init script, and you have a working module project with CI, tests, and documentation scaffolding ready to go.
4+
5+
## What's included
6+
7+
### Build & test
8+
9+
- **psake + PowerShellBuild** task pipeline (`build.ps1`, `build.psake.ps1`)
10+
- **Pester 5.x** test layout with `tests/Unit/{Public,Private}` scaffolding
11+
- **PSScriptAnalyzer** lint configuration
12+
- **Code coverage** via JaCoCo + Codecov (`codecov.yml`)
13+
- **Manifest validation tests** with SemVer-aware version-constraint checks (`tests/Manifest.tests.ps1`, `tests/ManifestHelpers.psm1`)
14+
- **Help documentation tests** that verify every public function has comment-based help with synopsis, description, and examples (`tests/Help.tests.ps1`)
15+
- **Meta tests** that catch UTF-16 files and tab indentation (`tests/Meta.tests.ps1`)
16+
- **Integration test loader**`tests/local.settings.example.ps1` documents how to wire local secrets without committing them
17+
18+
### CI/CD (GitHub Actions)
19+
20+
- `CI.yaml` — lint + test on push and PR
21+
- `PublishModuleToPowerShellGallery.yaml` — publish on release
22+
- `auto-merge-bots.yml` — auto-merge dependabot/pre-commit PRs
23+
- `ggshield.yaml` — secret scanning
24+
- Dependabot config and FUNDING file
25+
26+
### Developer experience
27+
28+
- **`.devcontainer/`** with Docker Compose + host setup script
29+
- **`.pre-commit-config.yaml`** with ggshield secret scanning
30+
- **`instructions/`** — 12 markdown guides for AI agents (PowerShell style, testing, releases, git workflow, etc.)
31+
- **`AGENTS.md`** — top-level AI agent guidance
32+
- Markdown linting via `.markdownlint-cli2.jsonc`
33+
34+
### Module scaffolding
35+
36+
- Full `.psd1` manifest with PSEdition tags, license/project URIs, and PSData metadata
37+
- `.psm1` with public/private dot-source pattern
38+
- Example public function (`Get-{{Prefix}}Example`) and private helper (`Invoke-{{Prefix}}Helper`)
39+
- `docs/en-US/about_{{ModuleName}}.help.md` stub for `Get-Help about_<Module>`
40+
41+
## Quick start
42+
43+
1. Click **Use this template → Create a new repository** at the top of this repo.
44+
2. Clone your new repository locally.
45+
3. Run the initialization script:
46+
47+
```powershell
48+
./Initialize-Template.ps1
49+
```
50+
51+
You'll be prompted for module name, function prefix, author, description, and project URL. Pass them as parameters for non-interactive use:
52+
53+
```powershell
54+
./Initialize-Template.ps1 `
55+
-ModuleName 'MyAwesomeModule' `
56+
-Prefix 'Mam' `
57+
-Author 'Jane Doe' `
58+
-Description 'Does awesome things' `
59+
-ProjectUri 'https://github.com/janedoe/MyAwesomeModule'
60+
```
61+
62+
4. The script substitutes placeholders, renames files, optionally runs `git init`, and bootstraps build dependencies. Delete `Initialize-Template.ps1` when done.
63+
64+
## Placeholders
65+
66+
`Initialize-Template.ps1` replaces these tokens across all `.ps1`, `.psm1`, `.psd1`, `.md`, `.json`, `.yml`, `.yaml`, `.xml`, and `.txt` files:
67+
68+
| Placeholder | Replaced with | Example |
69+
|---|---|---|
70+
| `{{ModuleName}}` | Module name | `MyAwesomeModule` |
71+
| `{{Prefix}}` | Function noun prefix | `Mam` |
72+
| `{{Author}}` | Author name | `Jane Doe` |
73+
| `{{Description}}` | Module description | `Does awesome things` |
74+
| `{{ProjectUri}}` | Repository URL | `https://github.com/...` |
75+
| `{{GUID}}` | Generated GUID | (new GUID per run) |
76+
| `{{Date}}` | ISO date at init | `2026-04-29` |
77+
| `{{Year}}` | Year at init | `2026` |
78+
79+
The script also renames the `{{ModuleName}}` folder, files containing `{{ModuleName}}` or `{{Prefix}}` in their names (in `Public/`, `Private/`, `tests/Unit/`, `docs/en-US/`), and replaces `README.md` with the post-init module README sourced from `README.template.md`.
80+
81+
## Project structure (post-init)
82+
83+
```text
84+
<ModuleName>/
85+
├── <ModuleName>/ # Module source
86+
│ ├── Public/ # Exported functions
87+
│ └── Private/ # Internal helpers
88+
├── tests/
89+
│ ├── Unit/{Public,Private}/ # Per-function tests
90+
│ ├── Help.tests.ps1 # Comment-based-help validation
91+
│ ├── Manifest.tests.ps1 # Manifest + dependency-version validation
92+
│ ├── Meta.tests.ps1 # Encoding + indentation checks
93+
│ └── ManifestHelpers.psm1 # SemVer comparison helpers
94+
├── docs/en-US/ # platyPS help (generated)
95+
├── instructions/ # AI agent guidance (12 files)
96+
├── .github/workflows/ # CI, publish, auto-merge, secret scan
97+
├── .devcontainer/ # VS Code dev container
98+
├── build.ps1 # Build entry point
99+
├── build.psake.ps1 # psake task definitions
100+
├── build.depend.psd1 # Build/test module dependencies
101+
└── requirements.psd1 # Runtime module dependencies
20102
```
21103

22-
## Requirements
23-
24-
- PowerShell 5.1 or later (Desktop or Core)
25-
- Windows, Linux, or macOS
104+
## Working on the template itself
26105

27-
## Quick Start
106+
If you want to contribute to the template (this repo) rather than use it:
28107

29108
```powershell
30-
# Import the module
31-
Import-Module {{ModuleName}}
32-
33-
# Get help for available commands
34-
Get-Command -Module {{ModuleName}}
35-
36-
# Example usage
37-
Get-{{Prefix}}Example -Name 'World'
109+
./build.ps1 -Task Test -Bootstrap
38110
```
39111

40-
## Available Commands
41-
42-
| Command | Description |
43-
|---------|-------------|
44-
| `Get-{{Prefix}}Example` | Example public function |
112+
The test suite runs against the `{{ModuleName}}` placeholder module to verify the scaffolding is sound. See [AGENTS.md](AGENTS.md) and [`instructions/`](instructions/) for contribution conventions.
45113

46-
## Development
47-
48-
### Prerequisites
114+
## Requirements
49115

50116
- PowerShell 5.1+ or PowerShell 7+
51117
- Git
52-
53-
### Building
54-
55-
```powershell
56-
# Clone the repository
57-
git clone {{ProjectUri}}.git
58-
cd {{ModuleName}}
59-
60-
# Bootstrap dependencies and build
61-
./build.ps1 -Task Build -Bootstrap
62-
63-
# Run tests
64-
./build.ps1 -Task Test
65-
```
66-
67-
### Project Structure
68-
69-
```
70-
{{ModuleName}}/
71-
├── {{ModuleName}}/ # Module source
72-
│ ├── Public/ # Exported functions
73-
│ └── Private/ # Internal helpers
74-
├── tests/ # Pester tests
75-
│ ├── Unit/ # Unit tests
76-
│ ├── Meta.tests.ps1 # Code style tests
77-
│ ├── Manifest.tests.ps1 # Manifest validation
78-
│ └── Help.tests.ps1 # Help documentation tests
79-
├── docs/ # Documentation
80-
├── .github/workflows/ # CI/CD pipelines
81-
└── build.ps1 # Build entry point
82-
```
83-
84-
### Available Build Tasks
85-
86-
```powershell
87-
./build.ps1 -Help
88-
```
89-
90-
| Task | Description |
91-
|------|-------------|
92-
| `Build` | Build the module to Output/ |
93-
| `Test` | Run all tests with code coverage |
94-
| `Analyze` | Run PSScriptAnalyzer |
95-
| `Pester` | Run Pester tests only |
96-
| `Clean` | Remove build artifacts |
97-
| `Publish` | Publish to PowerShell Gallery |
98-
99-
## Contributing
100-
101-
1. Fork the repository
102-
2. Create a feature branch
103-
3. Make your changes
104-
4. Run tests: `./build.ps1 -Task Test`
105-
5. Submit a pull request
118+
- (Optional) Docker for the devcontainer
106119

107120
## License
108121

109-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
110-
111-
## Changelog
112-
113-
See [CHANGELOG.md](CHANGELOG.md) for version history.
122+
[MIT](LICENSE)

0 commit comments

Comments
 (0)