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: 2 additions & 0 deletions .gitattributes
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
# The docs are generated by the build script and should be considered artifacts
docs/en-US/* linguist-generated
20 changes: 20 additions & 0 deletions .markdownlint-cli2.jsonc
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://raw.githubusercontent.com/DavidAnson/markdownlint-cli2/v0.19.1/schema/markdownlint-cli2-config-schema.json",
"config": {
"MD013": {
"tables": false,
"code_blocks": false
},
"MD024": {
"siblings_only": true
}
},
"ignores": [
"AGENTS.md",
// Intentionally narrow: only ignores docs/en-US (platyPS-generated help) so that
// other markdown files in docs/ are still linted. No other language directories
// are expected.
"docs/en-US/**",
"instructions/**"
]
}
31 changes: 28 additions & 3 deletions Initialize-Template.ps1
Original file line numberDiff line numberDiff line change
Expand Up@@ -241,8 +241,8 @@ if (Test-Path -Path $templateModuleFolder) {
# Rename example function files
$publicFolder = Join-Path -Path $moduleFolder -ChildPath 'Public'
$privateFolder = Join-Path -Path $moduleFolder -ChildPath 'Private'
$testPublicFolder = Join-Path -Path $PSScriptRoot -ChildPath 'tests\Unit\Public'
$testPrivateFolder = Join-Path -Path $PSScriptRoot -ChildPath 'tests\Unit\Private'
$testPublicFolder = Join-Path -Path $PSScriptRoot -ChildPath 'tests/Unit/Public'
$testPrivateFolder = Join-Path -Path $PSScriptRoot -ChildPath 'tests/Unit/Private'

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

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

# Rename files in docs/en-US/ that contain {{ModuleName}} placeholder (e.g., about_{{ModuleName}}.help.md)
$docsFolder = Join-Path -Path $PSScriptRoot -ChildPath 'docs/en-US'
if (Test-Path -Path $docsFolder) {
$docsFiles = Get-ChildItem -Path $docsFolder -File | Where-Object {
$_.Name -match '\{\{ModuleName\}\}'
}
foreach ($file in $docsFiles) {
$newName = $file.Name -replace '\{\{ModuleName\}\}', $ModuleName
Rename-Item -Path $file.FullName -NewName $newName
Write-Verbose "Renamed: $($file.Name) -> $newName"
}
if ($docsFiles) {
Write-Host " Renamed docs/en-US files" -ForegroundColor Green
}
}

# Replace template-facing README.md with the module-facing README.template.md
# (placeholders inside README.template.md were already substituted by the file-processing loop above)
$readmeTemplate = Join-Path -Path $PSScriptRoot -ChildPath 'README.template.md'
$readmePath = Join-Path -Path $PSScriptRoot -ChildPath 'README.md'
if (Test-Path -Path $readmeTemplate) {
Move-Item -Path $readmeTemplate -Destination $readmePath -Force
Write-Host ' Generated module README.md from template' -ForegroundColor Green
}

# Initialize Git repository if requested
if (-not $NoGitInit) {
$gitFolder = Join-Path -Path $PSScriptRoot -ChildPath '.git'
Expand DownExpand Up@@ -303,7 +328,7 @@ Write-Host '========================================' -ForegroundColor Green
Write-Host ''
Write-Host 'Next steps:' -ForegroundColor Cyan
Write-Host " 1. Review the generated files in the $ModuleName folder"
Write-Host ' 2. Update the README.md with your project details'
Write-Host ' 2. Review README.md and adjust to taste'
Write-Host ' 3. Add your functions to the Public/ and Private/ folders'
Write-Host ' 4. Run ./build.ps1 -Task Test to verify everything works'
Write-Host ' 5. Push to your GitHub repository'
Expand Down
27 changes: 26 additions & 1 deletion PSScriptAnalyzerSettings.psd1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
# https://learn.microsoft.com/en-us/powershell/utility-modules/psscriptanalyzer/using-scriptanalyzer
@{
IncludeRules = @('*')
IncludeDefaultRules = $true

IncludeRules = @(
# Default rules
'PS*'
)

# If IncludeRules and ExcludeRules are empty, all rules will be applied
ExcludeRules = @()

Rules = @{
# PSUseCompatibleSyntax = @{
# # This turns the rule on (setting it to false will turn it off)
# Enable = $true

# # List the targeted versions of PowerShell here
# TargetVersions = @(
# '5.1',
# '7.2'
# )
# }
# PSUseCompatibleCmdlets = @{
# compatibility = @('core-7.2.0-windows')
# }
}
}
207 changes: 108 additions & 99 deletions README.md
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,122 @@
# {{ModuleName}}

{{Description}}

## Installation

### From PowerShell Gallery

```powershell
Install-Module -Name {{ModuleName}} -Scope CurrentUser
```

### From Source

```powershell
git clone {{ProjectUri}}.git
cd {{ModuleName}}
./build.ps1 -Task Build -Bootstrap
Import-Module ./Output/{{ModuleName}}/*/{{ModuleName}}.psd1
# PowerShell Module Template

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.

## What's included

### Build & test

- **psake + PowerShellBuild** task pipeline (`build.ps1`, `build.psake.ps1`)
- **Pester 5.x** test layout with `tests/Unit/{Public,Private}` scaffolding
- **PSScriptAnalyzer** lint configuration
- **Code coverage** via JaCoCo + Codecov (`codecov.yml`)
- **Manifest validation tests** with SemVer-aware version-constraint checks (`tests/Manifest.tests.ps1`, `tests/ManifestHelpers.psm1`)
- **Help documentation tests** that verify every public function has comment-based help with synopsis, description, and examples (`tests/Help.tests.ps1`)
- **Meta tests** that catch UTF-16 files and tab indentation (`tests/Meta.tests.ps1`)
- **Integration test loader** — `tests/local.settings.example.ps1` documents how to wire local secrets without committing them

### CI/CD (GitHub Actions)

- `CI.yaml` — lint + test on push and PR
- `PublishModuleToPowerShellGallery.yaml` — publish on release
- `auto-merge-bots.yml` — auto-merge dependabot/pre-commit PRs
- `ggshield.yaml` — secret scanning
- Dependabot config and FUNDING file

### Developer experience

- **`.devcontainer/`** with Docker Compose + host setup script
- **`.pre-commit-config.yaml`** with ggshield secret scanning
- **`instructions/`** — 12 markdown guides for AI agents (PowerShell style, testing, releases, git workflow, etc.)
- **`AGENTS.md`** — top-level AI agent guidance
- Markdown linting via `.markdownlint-cli2.jsonc`

### Module scaffolding

- Full `.psd1` manifest with PSEdition tags, license/project URIs, and PSData metadata
- `.psm1` with public/private dot-source pattern
- Example public function (`Get-{{Prefix}}Example`) and private helper (`Invoke-{{Prefix}}Helper`)
- `docs/en-US/about_{{ModuleName}}.help.md` stub for `Get-Help about_<Module>`

## Quick start

1. Click **Use this template → Create a new repository** at the top of this repo.
2. Clone your new repository locally.
3. Run the initialization script:

```powershell
./Initialize-Template.ps1
```

You'll be prompted for module name, function prefix, author, description, and project URL. Pass them as parameters for non-interactive use:

```powershell
./Initialize-Template.ps1 `
-ModuleName 'MyAwesomeModule' `
-Prefix 'Mam' `
-Author 'Jane Doe' `
-Description 'Does awesome things' `
-ProjectUri 'https://github.com/janedoe/MyAwesomeModule'
```

4. The script substitutes placeholders, renames files, optionally runs `git init`, and bootstraps build dependencies. Delete `Initialize-Template.ps1` when done.

## Placeholders

`Initialize-Template.ps1` replaces these tokens across all `.ps1`, `.psm1`, `.psd1`, `.md`, `.json`, `.yml`, `.yaml`, `.xml`, and `.txt` files:

| Placeholder | Replaced with | Example |
|---|---|---|
| `{{ModuleName}}` | Module name | `MyAwesomeModule` |
| `{{Prefix}}` | Function noun prefix | `Mam` |
| `{{Author}}` | Author name | `Jane Doe` |
| `{{Description}}` | Module description | `Does awesome things` |
| `{{ProjectUri}}` | Repository URL | `https://github.com/...` |
| `{{GUID}}` | Generated GUID | (new GUID per run) |
| `{{Date}}` | ISO date at init | `2026-04-29` |
| `{{Year}}` | Year at init | `2026` |

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`.

## Project structure (post-init)

```text
<ModuleName>/
├── <ModuleName>/ # Module source
│ ├── Public/ # Exported functions
│ └── Private/ # Internal helpers
├── tests/
│ ├── Unit/{Public,Private}/ # Per-function tests
│ ├── Help.tests.ps1 # Comment-based-help validation
│ ├── Manifest.tests.ps1 # Manifest + dependency-version validation
│ ├── Meta.tests.ps1 # Encoding + indentation checks
│ └── ManifestHelpers.psm1 # SemVer comparison helpers
├── docs/en-US/ # platyPS help (generated)
├── instructions/ # AI agent guidance (12 files)
├── .github/workflows/ # CI, publish, auto-merge, secret scan
├── .devcontainer/ # VS Code dev container
├── build.ps1 # Build entry point
├── build.psake.ps1 # psake task definitions
├── build.depend.psd1 # Build/test module dependencies
└── requirements.psd1 # Runtime module dependencies
```

## Requirements

- PowerShell 5.1 or later (Desktop or Core)
- Windows, Linux, or macOS
## Working on the template itself

## Quick Start
If you want to contribute to the template (this repo) rather than use it:

```powershell
# Import the module
Import-Module {{ModuleName}}

# Get help for available commands
Get-Command -Module {{ModuleName}}

# Example usage
Get-{{Prefix}}Example -Name 'World'
./build.ps1 -Task Test -Bootstrap
```

## Available Commands

| Command | Description |
|---------|-------------|
| `Get-{{Prefix}}Example` | Example public function |
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.

## Development

### Prerequisites
## Requirements

- PowerShell 5.1+ or PowerShell 7+
- Git

### Building

```powershell
# Clone the repository
git clone {{ProjectUri}}.git
cd {{ModuleName}}

# Bootstrap dependencies and build
./build.ps1 -Task Build -Bootstrap

# Run tests
./build.ps1 -Task Test
```

### Project Structure

```
{{ModuleName}}/
├── {{ModuleName}}/ # Module source
│ ├── Public/ # Exported functions
│ └── Private/ # Internal helpers
├── tests/ # Pester tests
│ ├── Unit/ # Unit tests
│ ├── Meta.tests.ps1 # Code style tests
│ ├── Manifest.tests.ps1 # Manifest validation
│ └── Help.tests.ps1 # Help documentation tests
├── docs/ # Documentation
├── .github/workflows/ # CI/CD pipelines
└── build.ps1 # Build entry point
```

### Available Build Tasks

```powershell
./build.ps1 -Help
```

| Task | Description |
|------|-------------|
| `Build` | Build the module to Output/ |
| `Test` | Run all tests with code coverage |
| `Analyze` | Run PSScriptAnalyzer |
| `Pester` | Run Pester tests only |
| `Clean` | Remove build artifacts |
| `Publish` | Publish to PowerShell Gallery |

## Contributing

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Run tests: `./build.ps1 -Task Test`
5. Submit a pull request
- (Optional) Docker for the devcontainer

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Changelog

See [CHANGELOG.md](CHANGELOG.md) for version history.
[MIT](LICENSE)
Loading