| applyTo | **/GitVersion.yml,**/*.psd1,**/CHANGELOG.md |
|---|
Follow Semantic Versioning 2.0.0 for all module and project versioning.
Version Format: MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]
Version Components:
- MAJOR: Incompatible API changes (breaking changes)
- MINOR: Backwards-compatible new functionality
- PATCH: Backwards-compatible bug fixes
- PRERELEASE: Optional pre-release identifier (alpha, beta, rc)
- BUILD: Optional build metadata
Increment MAJOR version when:
- ✅ Making incompatible API changes
- ✅ Removing features or functionality
- ✅ Changing behavior that breaks existing implementations
- ✅ Renaming or removing parameters
- ✅ Changing default values that affect behavior
- ✅ Requiring higher minimum PowerShell version
- ✅ Breaking changes in data structures or schemas
Increment MINOR version when:
- ✅ Adding new features in a backwards-compatible manner
- ✅ Adding new functions, cmdlets, or resources
- ✅ Adding new parameters with default values
- ✅ Deprecating features (but not removing them yet)
- ✅ Substantial internal improvements that add value
Increment PATCH version when:
- ✅ Making backwards-compatible bug fixes
- ✅ Fixing issues that don't change functionality
- ✅ Correcting typos in error messages
- ✅ Performance improvements without API changes
- ✅ Updating dependencies to patch versions
Do NOT increment version for:
- ❌ Documentation-only changes
- ❌ Comment updates
- ❌ Code formatting/style changes
- ❌ Test-only changes (unless fixing test bugs)
- ❌ CI/CD pipeline changes
Use pre-release identifiers for versions not yet ready for production:
1.0.0-alpha.1 # Early testing, unstable
1.0.0-beta.1 # Feature complete, testing
1.0.0-rc.1 # Release candidate, final testing
1.0.0 # Stable release
Pre-release Guidelines:
- Alpha: Early development, expect breaking changes
- Beta: Feature complete, stabilizing
- RC (Release Candidate): Final testing, no new features
- Use numeric suffixes for iterations:
alpha.1,alpha.2, etc.
Major version zero (0.y.z) has special semantics in SemVer:
- 0.y.z is for initial development — anything MAY change at any time
- The public API SHOULD NOT be considered stable
- Breaking changes MAY occur in MINOR or PATCH increments
- Start initial development at
0.1.0and increment MINOR for each release - Transition to
1.0.0when the public API is stable and used in production
When to Release 1.0.0:
- Software is used in production
- Users depend on a stable API
- You are actively managing backward compatibility
- You need to communicate breaking vs. non-breaking changes clearly
Tip: If you are worrying about backward compatibility, you should probably already be at
1.0.0.
Build metadata is appended after a + sign following the patch or pre-release version:
1.0.0+20240115.sha.abc1234
1.0.0-beta.1+build.42
1.2.3+exp.sha.5114f85
Rules:
- Build metadata MUST be ignored when determining version precedence
- Two versions that differ only in build metadata have equal precedence
- Identifiers MUST comprise only ASCII alphanumerics and hyphens
[0-9A-Za-z-] - Use for traceability — embed commit SHA, build number, or timestamp
- Do NOT rely on build metadata for ordering or uniqueness
Precedence determines how versions are compared when ordered:
- Separate version into MAJOR, MINOR, PATCH, and pre-release identifiers (build metadata is excluded)
- Compare MAJOR, MINOR, PATCH numerically from left to right
- When MAJOR.MINOR.PATCH are equal, a pre-release version has lower precedence than the normal version
- Pre-release identifiers are compared dot-segment by dot-segment:
- Numeric identifiers are compared as integers
- Alphanumeric identifiers are compared lexically (ASCII sort order)
- Numeric identifiers always have lower precedence than alphanumeric
- A larger set of fields has higher precedence when all preceding fields are equal
Complete Precedence Example:
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta
< 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0
Use the official regex from semver.org to validate version strings:
^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$PowerShell validation:
$semverPattern = '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'
if ($version -notmatch $semverPattern) {
throw "Invalid SemVer: $version"
}"v1.2.3" is NOT a semantic version. The semantic version is
1.2.3. Thevprefix is a common convention for Git tag names but is not part of SemVer.
- Git tags: use
vprefix (v1.2.3) — this is a tag name, not a version - Module manifests: use bare version (
1.2.3) — novprefix - GitVersion
tag-prefixdefaults to[vV]?and strips the prefix automatically - Be consistent within a project; never mix
v1.2.3and1.2.3tags
CalVer uses the project's release calendar instead of arbitrary numbers.
Common CalVer Formats:
| Token | Meaning | Example |
|---|---|---|
YYYY |
Full year | 2024 |
YY |
Short year (relative to 2000) | 24 |
0Y |
Zero-padded year | 24 |
MM / 0M |
Short / zero-padded month | 1 / 01 |
DD / 0D |
Short / zero-padded day | 5 / 05 |
MINOR |
Incremented number | 3 |
MICRO |
Patch-level increment | 1 |
Notable CalVer Users:
| Project | Scheme | Example |
|---|---|---|
| Ubuntu | YY.0M |
24.04 |
| pip | YY.MINOR.MICRO |
24.0.1 |
| Unity | YYYY.MINOR.MICRO |
2024.1.0 |
| Stripe API | YYYY-MM-DD |
2024-01-15 |
When to Consider CalVer:
- Large systems with constantly-changing scope (frameworks, OSes)
- Time-sensitive projects (security certificates, timezone databases)
- When external changes (not code changes) drive releases
- When support schedules are tied to release dates
When to Prefer SemVer Over CalVer:
- Libraries with a clear public API
- When consumers need to know if an update is safe (non-breaking)
- PowerShell modules published to PSGallery (SemVer is the convention)
Some projects combine CalVer with SemVer:
YY.MM.MINOR # Calendar major, incremented minor
YYYY.MINOR.PATCH # Year-based major, semantic minor/patch
Recommendation for PowerShell modules: Use SemVer. CalVer is better suited for platforms, operating systems, and API versioning.
GitVersion is an automated versioning tool that:
- Calculates semantic versions based on Git history
- Uses branch names and commit messages to determine version increments
- Integrates with CI/CD pipelines
- Ensures consistent versioning across artifacts
- Eliminates manual version management
GitVersion ships with three built-in workflow templates. Specify one in your GitVersion.yml:
| Workflow | Value | Use Case |
|---|---|---|
| GitFlow | GitFlow/v1 |
Projects with develop, release, and hotfix branches |
| GitHubFlow | GitHubFlow/v1 |
Simpler flow — feature branches merge to main |
| Trunk-Based | TrunkBased/preview1 |
Continuous integration; short-lived branches only |
workflow: GitHubFlow/v1 # or GitFlow/v1 or TrunkBased/preview1Run
gitversion /showConfigto view the effective configuration (defaults + overrides).
Each branch can operate in one of three deployment modes:
| Mode | Behaviour | Typical Branch |
|---|---|---|
ManualDeployment |
Version stays on the same pre-release until explicitly deployed/tagged | release, hotfix, feature |
ContinuousDelivery |
Pre-release counter increments on every commit; stable version only after tagging | develop, pull-request |
ContinuousDeployment |
Every commit produces a unique, deployable version; no tagging needed | main (trunk-based) |
branches:
main:
mode: ContinuousDeployment # every merge increments automatically
develop:
mode: ContinuousDelivery # counter increments; tag to release
feature:
mode: ManualDeployment # version stays stable during developmentCreate GitVersion.yml in repository root:
workflow: GitHubFlow/v1
branches:
main:
label: ''
feature:
label: '{BranchName}'
ignore:
sha: []Key Settings:
workflow- Base template (GitFlow/v1, GitHubFlow/v1, TrunkBased/preview1)mode- Deployment mode per branch (ManualDeployment, ContinuousDelivery, ContinuousDeployment)label- Pre-release label for versions (empty string''= stable release)increment- Which part to bump: Major, Minor, Patch, Inherit, Nonebranches- Per-branch versioning strategiesignore- Commits or paths to exclude from versioning
For PowerShell modules following DSC Community patterns:
mode: Mainline
assembly-versioning-scheme: MajorMinorPatch
assembly-file-versioning-scheme: MajorMinorPatch
next-version: 1.0.0
major-version-bump-message: '\+semver:\s?(breaking|major)'
minor-version-bump-message: '\+semver:\s?(feature|minor)'
patch-version-bump-message: '\+semver:\s?(fix|patch)'
no-bump-message: '\+semver:\s?none'
legacy-semver-padding: 4
build-metadata-padding: 4
commits-since-version-source-padding: 4
branches:
main:
tag: ''
regex: ^master$|^main$
source-branches: ['develop', 'release']
is-release-branch: true
develop:
tag: 'preview'
regex: ^dev(elop)?(ment)?$
source-branches: []
is-release-branch: false
feature:
tag: 'preview'
regex: ^features?[/-]
source-branches: ['develop']
increment: Minor
pull-request:
tag: 'PR'
regex: ^(pull|pull\-requests|pr)[/-]
source-branches: ['develop', 'main', 'release', 'feature', 'hotfix']
tag-number-pattern: '[/-](?<number>\d+)'
hotfix:
tag: 'fix'
regex: ^hotfix(es)?[/-]
source-branches: ['main']
release:
tag: ''
regex: ^releases?[/-]
source-branches: ['develop']
is-release-branch: true
ignore:
sha: []Configuration Explained:
- assembly-versioning-scheme: How assembly versions are calculated
- next-version: Starting version for new repositories
- bump-message patterns: Regex to detect version increment from commits
- padding: Zero-padding for version components
- Branch strategies: Different versioning per branch type
Use Conventional Commits format:
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
Common Types:
feat: New feature (increments MINOR)fix: Bug fix (increments PATCH)docs: Documentation onlystyle: Code style/formattingrefactor: Code restructuringtest: Adding/updating testschore: Maintenance tasksperf: Performance improvementsci: CI/CD changesbuild: Build system changes
Breaking Changes:
Add BREAKING CHANGE: in footer or ! after type:
feat!: remove legacy API endpoint
BREAKING CHANGE: The /api/v1/legacy endpoint has been removed.
Use /api/v2/resource instead.
Override GitVersion's default behavior with +semver: hints:
feat: add new caching layer +semver: minor
fix: correct parameter validation +semver: patch
refactor: restructure authentication +semver: major
docs: update README +semver: none
SemVer Hint Options:
+semver: majoror+semver: breaking- Force major increment+semver: minoror+semver: feature- Force minor increment+semver: patchor+semver: fix- Force patch increment+semver: none- Skip version increment
Feature Addition:
feat(user-management): add password reset functionality
Implements password reset via email verification.
Includes new Send-PasswordResetEmail function.
Closes #123
Bug Fix:
fix(validation): correct email regex pattern
The previous regex allowed invalid email formats.
Updated to RFC 5322 compliant pattern.
Fixes #456
Breaking Change:
feat(api)!: change authentication to OAuth 2.0
BREAKING CHANGE: Basic authentication is no longer supported.
All clients must migrate to OAuth 2.0.
Migration guide: docs/oauth-migration.md
Documentation Update:
docs: update installation instructions +semver: none
Added troubleshooting section for common installation issues.
Purpose: Production-ready code
Versioning: Stable releases without pre-release tags
Example: 1.2.3
Workflow:
- All merges to main create release versions
- Tagged automatically in CI/CD
- Published to PowerShell Gallery
- Changelog updated with release date
Purpose: Integration branch for features
Versioning: Preview versions with -preview tag
Example: 1.3.0-preview.4
Workflow:
- Feature branches merge here
- CI/CD builds preview versions
- Can be published to PSGallery as prerelease
- Merged to main when stable
Purpose: New feature development
Versioning: Preview versions with branch name
Example: 1.3.0-feature-caching.12
Workflow:
- Branch from develop:
feature/caching - Commits increment preview counter
- Merge to develop when complete
- Branch deleted after merge
Purpose: Code review and validation
Versioning: PR-specific preview versions
Example: 1.2.1-PR123.5
Workflow:
- Created from feature or develop
- Version includes PR number
- CI/CD validates all checks
- Merged after approval
Purpose: Critical production fixes
Versioning: Patch increment with -fix tag
Example: 1.2.4-fix.1
Workflow:
- Branch from main:
hotfix/critical-bug - Immediate patch version increment
- Merged to both main and develop
- Tagged and released quickly
Purpose: Release preparation and stabilization
Versioning: Release candidate versions
Example: 2.0.0-rc.1
Workflow:
- Branch from develop:
release/2.0.0 - Only bug fixes and documentation
- No new features
- Merged to main when stable
- Tagged with final version
GitVersion supports monorepos via ignore.paths — commits touching only excluded paths are skipped in version calculations:
# Only consider commits affecting ProjectA or shared library
ignore:
paths:
- '^(?!\/ProjectA\/|\/SharedLib\/).*'Monorepo Guidelines:
- Each sub-project gets its own
GitVersion.ymlwith appropriateignore.paths - Use negative lookahead regex to include only relevant paths
- A commit is ignored only if all changed paths match the ignore patterns
- Paths are case-sensitive; use
(?i)prefix on Windows for case-insensitive matching - Consider separate Git tags per sub-project (e.g.,
projectA/v1.2.3)
All version numbers MUST be synchronized across:
- Module manifest (
.psd1) -ModuleVersionproperty - Changelog (
CHANGELOG.md) - Latest[Unreleased]or version header - Git tags - Annotated tag matching release version
- PowerShell Gallery metadata - Published version
Update ModuleVersion:
@{
ModuleVersion = '1.2.3'
PrivateData = @{
PSData = @{
# Prerelease string — results in 1.2.3-preview on PSGallery
Prerelease = 'preview'
}
}
}PowerShell Gallery Prerelease Requirements:
ModuleVersionMUST be 3-part (Major.Minor.Build) when using a Prerelease string- The
Prereleaseproperty lives inPrivateData.PSData, not at the manifest root - PowerShell Gallery supports SemVer v1.0.0 only — dots (
.) and plus (+) are not allowed in the Prerelease string (unlike SemVer v2.0.0) - Valid prerelease strings:
alpha,alpha1,beta,rc1,preview0045 - Invalid prerelease strings:
alpha.1(dot),beta+build(plus) - Consumers must pass
-AllowPrereleasetoFind-Module,Install-Module,Update-Module, andSave-Module - Side-by-side prerelease installation is not supported —
2.5.0-alphaand2.5.0-betashare the same2.5.0folder
Automated Update in CI/CD:
# PowerShell script to update manifest
$manifestPath = './MyModule/MyModule.psd1'
$version = $env:GitVersion_MajorMinorPatch
$prerelease = $env:GitVersion_PreReleaseTag
Update-ModuleManifest -Path $manifestPath -ModuleVersion $version
if ($prerelease) {
Update-ModuleManifest -Path $manifestPath -Prerelease $prerelease
}Specify version constraints in module manifests to declare dependencies:
@{
RequiredModules = @(
# Minimum version (inclusive)
@{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' }
# Exact version
@{ ModuleName = 'PSFramework'; RequiredVersion = '1.10.318' }
# Maximum version (PowerShell 5.1+ only)
@{ ModuleName = 'Az.Accounts'; ModuleVersion = '2.0.0'; MaximumVersion = '2.99.99' }
# GUID for disambiguation
@{ ModuleName = 'MyInternalLib'; GUID = 'abc12345-...'; ModuleVersion = '1.0.0' }
)
}Best Practices:
- Always set a minimum
ModuleVersionfor dependencies - Use
RequiredVersionsparingly — it prevents minor/patch upgrades - Pin major version with
MaximumVersion(e.g.,2.99.99) to allow non-breaking updates - Include the module GUID when multiple modules share a name
PowerShell has two version types — understand the difference:
| Type | Parts | Supports Pre-release | Use Case |
|---|---|---|---|
[System.Version] |
4 (Major.Minor.Build.Revision) |
No | $PSVersionTable.PSVersion, ModuleVersion manifest property |
[semver] (PS 6+) |
3 + labels (Major.Minor.Patch-Pre+Build) |
Yes | SemVer-aware comparison, manually parsed versions |
# System.Version — 4-part, no pre-release
[version]'1.2.3' # → 1.2.3 (Build = -1, Revision = -1)
[version]'1.2.3.0' # → 1.2.3.0
# SemVer — 3-part, pre-release aware (PowerShell 7+ only)
[semver]'1.2.3-alpha.1' # → Major=1, Minor=2, Patch=3, PreReleaseLabel=alpha.1
[semver]'1.2.3' -gt [semver]'1.2.3-alpha' # → True (release > pre-release)Gotcha: [System.Version] has 4 components and compares numerically; SemVer has 3 components with string-based pre-release ordering. Never mix them in the same comparison.
Version Header Format:
## [Unreleased]
### Added
- New feature descriptions
## [1.2.3] - 2024-01-15
### Fixed
- Bug fix descriptionsAutomated Update:
# Update changelog with version and date
$changelog = Get-Content -Path './CHANGELOG.md' -Raw
$version = $env:GitVersion_SemVer
$date = Get-Date -Format 'yyyy-MM-dd'
$updated = $changelog -replace '\[Unreleased\]', "[$version] - $date`n`n## [Unreleased]"
Set-Content -Path './CHANGELOG.md' -Value $updatedTagging Strategy:
# Annotated tag with message
git tag -a v1.2.3 -m "Release version 1.2.3"
# Push tag to remote
git push origin v1.2.3Automated Tagging in CI/CD:
# Azure Pipelines
- task: Bash@3
inputs:
targetType: 'inline'
script: |
git tag -a "v$(GitVersion.SemVer)" -m "Release $(GitVersion.SemVer)"
git push origin "v$(GitVersion.SemVer)"Pre-release Checklist:
- Module manifest version matches GitVersion calculation
- CHANGELOG.md has entry for new version with date
- Git tag exists for version
- All tests pass with new version
- Documentation references correct version
- Breaking changes documented if MAJOR increment
Automated Validation Script:
# Validate version synchronization
$manifestVersion = (Import-PowerShellDataFile './MyModule/MyModule.psd1').ModuleVersion
$changelogVersion = (Select-String -Path './CHANGELOG.md' -Pattern '## \[(\d+\.\d+\.\d+)\]').Matches[0].Groups[1].Value
$gitTag = git describe --tags --abbrev=0
if ($manifestVersion -ne $changelogVersion -or "v$manifestVersion" -ne $gitTag) {
Write-Error "Version mismatch detected!"
Write-Host "Manifest: $manifestVersion"
Write-Host "Changelog: $changelogVersion"
Write-Host "Git Tag: $gitTag"
exit 1
}Install and Run GitVersion:
trigger:
branches:
include:
- main
- develop
- feature/*
- hotfix/*
pool:
vmImage: 'windows-latest'
steps:
- task: gitversion/setup@0
displayName: 'Install GitVersion'
inputs:
versionSpec: '5.x'
- task: gitversion/execute@0
displayName: 'Calculate Version'
inputs:
useConfigFile: true
configFilePath: 'GitVersion.yml'
- task: PowerShell@2
displayName: 'Update Module Manifest'
inputs:
targetType: 'inline'
script: |
$version = "$(GitVersion.SemVer)"
Update-ModuleManifest -Path './MyModule/MyModule.psd1' -ModuleVersion $version
Write-Host "Updated manifest to version: $version"
- task: PowerShell@2
displayName: 'Display Version Info'
inputs:
targetType: 'inline'
script: |
Write-Host "SemVer: $(GitVersion.SemVer)"
Write-Host "Major: $(GitVersion.Major)"
Write-Host "Minor: $(GitVersion.Minor)"
Write-Host "Patch: $(GitVersion.Patch)"
Write-Host "PreReleaseTag: $(GitVersion.PreReleaseTag)"GitVersion Workflow:
name: Version and Build
on:
push:
branches:
- main
- develop
pull_request:
jobs:
version:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0 # Required for GitVersion
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v0
with:
versionSpec: '5.x'
- name: Determine Version
id: gitversion
uses: gittools/actions/gitversion/execute@v0
with:
useConfigFile: true
configFilePath: GitVersion.yml
- name: Display Version
run: |
echo "SemVer: ${{ steps.gitversion.outputs.semVer }}"
echo "Major: ${{ steps.gitversion.outputs.major }}"
echo "Minor: ${{ steps.gitversion.outputs.minor }}"
echo "Patch: ${{ steps.gitversion.outputs.patch }}"
- name: Update Module Manifest
shell: pwsh
run: |
$version = "${{ steps.gitversion.outputs.semVer }}"
Update-ModuleManifest -Path './MyModule/MyModule.psd1' -ModuleVersion $version
Write-Host "Updated to version: $version"Available GitVersion Variables:
| Variable | Description | Example |
|---|---|---|
GitVersion.SemVer |
Full semantic version | 1.2.3-preview.4 |
GitVersion.Major |
Major version number | 1 |
GitVersion.Minor |
Minor version number | 2 |
GitVersion.Patch |
Patch version number | 3 |
GitVersion.PreReleaseTag |
Pre-release identifier | preview.4 |
GitVersion.BuildMetaData |
Build metadata | 5.Branch.develop |
GitVersion.FullSemVer |
Complete version string | 1.2.3-preview.4+5 |
GitVersion.InformationalVersion |
Informational version | 1.2.3-preview.4+5.Branch.develop.Sha.abc123 |
Track version metadata within functions for debugging and compatibility:
function Get-UserData {
[CmdletBinding()]
[OutputType([PSCustomObject])]
param(
[Parameter(Mandatory)]
[string]$UserId
)
# Function metadata
$functionVersion = '1.2.0'
$moduleVersion = $MyInvocation.MyCommand.Module.Version
Write-Verbose "Function Version: $functionVersion"
Write-Verbose "Module Version: $moduleVersion"
# Function implementation
# ...
}Use PowerShell custom attributes for version documentation:
class VersionAttribute : System.Attribute {
[string]$Version
[string]$Since
[string]$Deprecated
VersionAttribute([string]$version) {
$this.Version = $version
}
}
[Version("1.2.0")]
function Get-UserData {
# Implementation
}Git Hook for Version Checks:
# .git/hooks/pre-commit
#!/usr/bin/env pwsh
$manifest = Import-PowerShellDataFile './MyModule/MyModule.psd1'
$changelog = Get-Content './CHANGELOG.md' -Raw
if ($changelog -notmatch "\[Unreleased\]") {
Write-Error "CHANGELOG.md must have [Unreleased] section"
exit 1
}
Write-Host "✓ Version validation passed"
exit 0Automated Version Validation:
- task: PowerShell@2
displayName: 'Validate Versioning'
inputs:
targetType: 'inline'
script: |
# Validate manifest exists
$manifestPath = './MyModule/MyModule.psd1'
if (-not (Test-Path $manifestPath)) {
Write-Error "Module manifest not found"
exit 1
}
# Validate changelog format
$changelog = Get-Content './CHANGELOG.md' -Raw
if ($changelog -notmatch '## \[\d+\.\d+\.\d+\]') {
Write-Error "CHANGELOG.md missing version entries"
exit 1
}
# Validate SemVer format
$version = (Import-PowerShellDataFile $manifestPath).ModuleVersion
if ($version -notmatch '^\d+\.\d+\.\d+$') {
Write-Error "Invalid semantic version: $version"
exit 1
}
Write-Host "✓ All version validations passed"Follow a structured process when removing or replacing functionality:
-
Announce Deprecation (MINOR release)
- Mark functions with
[Obsolete()]attribute orWrite-Warningon first use - Add
### Deprecatedentry in CHANGELOG.md - Document migration path and replacement API
- Mark functions with
-
Maintain Deprecated API (at least one MINOR release cycle)
- Keep full backward compatibility
- Emit warnings but do not break callers
- Update documentation with recommended alternatives
-
Remove Deprecated API (MAJOR release only)
- Remove the deprecated code
- Document the removal as a
BREAKING CHANGE - Increment MAJOR version
# Deprecation warning pattern
function Get-LegacyData {
[CmdletBinding()]
param()
Write-Warning 'Get-LegacyData is deprecated and will be removed in v3.0.0. Use Get-DataV2 instead.'
# ... existing implementation ...
}Define how long each major version receives support:
| Version | Status | Support Until | Notes |
|---|---|---|---|
| 3.x | Current | Active | Latest features and fixes |
| 2.x | Maintenance | 2025-06-30 | Security and critical bug fixes only |
| 1.x | End of Life | 2024-01-01 | No further updates |
DO:
- ✅ Use GitVersion for automated version calculation
- ✅ Follow semantic versioning strictly
- ✅ Synchronize versions across manifest, changelog, and tags
- ✅ Use conventional commit messages
- ✅ Validate versions in CI/CD pipelines
- ✅ Tag releases with annotated Git tags
- ✅ Document breaking changes clearly
- ✅ Use pre-release versions for non-production releases
- ✅ Start at
0.1.0for initial development; move to1.0.0when the API is stable - ✅ Use the
vprefix only on Git tags, never in manifestModuleVersion - ✅ Use
[semver]type (PS 7+) for version comparisons that involve pre-release labels - ✅ Define a deprecation period before any breaking change
- ✅ Set minimum version constraints in
RequiredModules - ✅ Prefer
labelover deprecatedtagproperty in GitVersion v6
DON'T:
- ❌ Manually edit version numbers without updating all artifacts
- ❌ Skip version increments for significant changes
- ❌ Use inconsistent versioning across branches
- ❌ Forget to update CHANGELOG.md with version details
- ❌ Tag commits without proper validation
- ❌ Release breaking changes as MINOR or PATCH versions
- ❌ Use generic commit messages that skip version metadata
- ❌ Use dots or plus signs in PSGallery
Prereleasestrings (SemVer v1 only) - ❌ Compare
[System.Version]and[semver]objects directly — they are incompatible types - ❌ Stay on
0.y.zindefinitely when the API is already stable and used in production - ❌ Place the
Prereleaseproperty at the manifest root — it belongs inPrivateData.PSData - ❌ Use
RequiredVersionin dependencies unless you truly need an exact pin
DO:
- ✅ Write clear, descriptive commit messages
- ✅ Use conventional commit format
- ✅ Include
+semver:hints when needed - ✅ Reference issue numbers
- ✅ Explain the "why" in commit body
- ✅ Mark breaking changes explicitly
DON'T:
- ❌ Write vague messages like "fix stuff" or "update"
- ❌ Commit without considering version impact
- ❌ Mix multiple unrelated changes in one commit
- ❌ Forget to add
+semver: nonefor docs-only changes
Preparation:
- Ensure all tests pass
- Update CHANGELOG.md with release notes
- Validate version synchronization
- Review breaking changes documentation
- Test in pre-release environment
Execution:
- GitVersion calculates version from Git history
- CI/CD updates module manifest automatically
- Automated tests run with new version
- Git tag created for release
- Publish to PowerShell Gallery
- Update CHANGELOG.md with release date
Post-release:
- Verify published version on PSGallery
- Update documentation links
- Create GitHub release with notes
- Announce release in appropriate channels
Versioning and changelog management are tightly coupled:
- Version increments must have corresponding CHANGELOG.md entries
- Breaking changes in commits must appear in CHANGELOG.md
- Release dates in CHANGELOG.md must match Git tag dates
- Version headers in CHANGELOG.md must match module manifest versions
See markdown.instructions.md for detailed changelog management practices.
Situation: Adding a new Export-Report function to the module.
Steps:
- Create feature branch:
git checkout -b feature/export-report - Implement function with tests
- Commit with conventional message:
feat(reports): add Export-Report function Implements PDF and Excel export capabilities for reports. Includes comprehensive parameter validation and error handling. Closes #234 - GitVersion calculates:
1.3.0-feature-export-report.1 - Merge to develop: Version becomes
1.3.0-preview.X - Update CHANGELOG.md:
## [Unreleased] ### Added - New `Export-Report` function for PDF and Excel export (#234)
- Merge to main: Version becomes
1.3.0 - Tag release:
git tag -a v1.3.0 -m "Release 1.3.0"
Result: MINOR version increment (new functionality, backwards-compatible)
Situation: Fixing null reference error in Get-UserData.
Steps:
- Create hotfix branch from main:
git checkout -b hotfix/null-reference - Fix bug and add regression test
- Commit with message:
fix(users): handle null values in Get-UserData Previously threw NullReferenceException when user had no email. Now returns empty string with proper warning. Fixes #456 - GitVersion calculates:
1.2.1-fix.1 - Merge to main: Version becomes
1.2.1 - Tag and release immediately
- Merge back to develop to keep branches in sync
Result: PATCH version increment (bug fix, backwards-compatible)
Situation: Renaming parameter from -UserName to -Identity for consistency.
Steps:
- Create feature branch:
git checkout -b feature/rename-username-param - Update function signature and all references
- Update documentation and examples
- Commit with breaking change marker:
feat(users)!: rename UserName parameter to Identity BREAKING CHANGE: The -UserName parameter has been renamed to -Identity for consistency across all user-related functions. Migration: Replace all instances of -UserName with -Identity in scripts. - Update CHANGELOG.md with migration guide:
## [Unreleased] ### BREAKING CHANGES - **User Functions**: Renamed `-UserName` parameter to `-Identity` across all functions - **Migration**: Update all scripts using `-UserName` to use `-Identity` - **Reason**: Consistency with Active Directory and other PowerShell modules
- Merge to develop, then to main
- GitVersion calculates:
2.0.0(MAJOR increment)
Result: MAJOR version increment (breaking API change)
Situation: Release includes features, fixes, and documentation.
Commits:
feat(auth): add OAuth 2.0 support
fix(logging): correct timestamp format in logs
docs: update authentication examples +semver: none
test: add integration tests for OAuth +semver: none
CHANGELOG.md:
## [Unreleased]
### Added
- OAuth 2.0 authentication support (#567)
### Fixed
- Timestamp format in log output now uses ISO 8601 (#589)
### Documentation
- Updated authentication examples with OAuth patternsResult: MINOR version increment (highest impact is new feature)
Before releasing or reviewing version-related changes, verify:
- Version follows SemVer
MAJOR.MINOR.PATCHformat - MAJOR incremented for any breaking / incompatible change
- MINOR incremented for new backward-compatible features
- PATCH incremented for backward-compatible bug fixes
- Pre-release label is SemVer v1 compliant for PSGallery (no dots or plus)
-
Prereleasestring is inPrivateData.PSData, not manifest root - Module manifest
ModuleVersionmatches calculated version - CHANGELOG.md has an entry for the new version with a date
- Git tag exists with
vprefix and matches the release version - Conventional commit messages used with appropriate
+semver:hints - Breaking changes documented in CHANGELOG.md and commit footer
- Deprecated APIs have migration guides and at least one MINOR release grace period
-
RequiredModulesversion constraints are set and tested - CI/CD pipeline validates version synchronization across all artifacts
- GitVersion workflow and mode are appropriate for the branching strategy
-
0.y.zprojects promoted to1.0.0when API is stable and in production - Build metadata (if used) does not affect version precedence
- All tests pass with the new version
- Documentation references correct version numbers
- Tag prefix convention (
v) is consistent across all releases
- Semantic Versioning: https://semver.org/
- Calendar Versioning: https://calver.org/
- GitVersion: https://gitversion.net/
- Conventional Commits: https://www.conventionalcommits.org/
- Keep a Changelog: https://keepachangelog.com/
- Prerelease Module Versions: Microsoft Docs
- Module Manifest (psd1): about_Module_Manifests
- Update-ModuleManifest: Command Reference
- [semver] Type Accelerator: Available in PowerShell 6.1+ for SemVer-aware comparisons
- DSC Community Guidelines: https://dsccommunity.org/
- Sample GitVersion Configurations: DSC Community Repos
- ComputerManagementDsc: Reference implementation example
- SqlServerDsc: Advanced versioning patterns
- Azure Pipelines GitVersion Task: GitTools Extension
- GitHub Actions GitVersion: GitTools Actions
- GitVersion Configuration Reference: Configuration
- GitVersion Versioning Modes: Modes
- GitVersion CLI: Command-line version calculation tool
- PSFramework: PowerShell framework with versioning utilities
- Pester: Testing framework for version validation
- PSScriptAnalyzer: Static analysis for PowerShell code quality