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
1 change: 1 addition & 0 deletions GitVersion.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mode: ContinuousDeployment
next-version: 1.0.0
assembly-versioning-scheme: 'MajorMinorPatchTag'
assembly-informational-format: '{Major}.{Minor}.{Patch}{PreReleaseTagWithDash}+Sha.{Sha}.Date.{CommitDate}'
commit-message-incrementing: MergeMessageOnly
Expand Down
9 changes: 5 additions & 4 deletions Source/ModuleBuilder.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
PrivateData = @{
# PrivateData.PSData is the PowerShell Gallery data
PSData = @{
# Prerelease string of this module
Prerelease = '-beta01'
# Prerelease string should be here, so we can set it
Prerelease = 'beta'

# ReleaseNotes of this module
# Release Notes have to be here, so we can update them
ReleaseNotes = '
1.0.0-beta01: Pre-release version of Build-Module
First release to the PowerShell gallery ...
'

# Tags applied to this module. These help with module discovery in online galleries.
Expand Down Expand Up @@ -50,3 +50,4 @@
PowerShellVersion = '5.1'
CompatiblePSEditions = @('Core','Desktop')
}

10 changes: 7 additions & 3 deletions Source/Private/InitializeBuild.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ function InitializeBuild {
[CmdletBinding()]
param(
# The root folder where the module source is (including the Build.psd1 and the module Manifest.psd1)
[string]$SourcePath
[string]$SourcePath,

# Pass the invocation from the parent in, so InitializeBuild can read parameter values
[Parameter(DontShow)]
$Invocation = $(Get-Variable MyInvocation -Scope 1 -ValueOnly)
)
# Read the caller's parameter values
# NOTE: This reads the parameter values from Build-Module!
$ParameterValues = @{}
foreach($parameter in (Get-Variable MyInvocation -Scope 1 -ValueOnly).MyCommand.Parameters.GetEnumerator()) {
foreach($parameter in $Invocation.MyCommand.Parameters.GetEnumerator()) {
$key = $parameter.Key
if($null -ne ($value = Get-Variable -Name $key -ValueOnly -ErrorAction Ignore )) {
if($value -ne ($null -as $parameter.Value.ParameterType)) {
Expand Down
80 changes: 76 additions & 4 deletions Source/Public/Build-Module.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,18 @@ function Build-Module {
Build-Module -Prefix "using namespace System.Management.Automation"

This example shows how to build a simple module from it's manifest, adding a using statement at the top as a prefix

.Example
$gitVersion = gitversion | ConvertFrom-Json | Select -Expand InformationalVersion
Build-Module -SemVer $gitVersion

This example shows how to use a semantic version from gitversion to version your build.
Note, this is how we version ModuleBuilder, so if you want to see it in action, check out our azure-pipelines.yml
https://github.com/PoshCode/ModuleBuilder/blob/master/azure-pipelines.yml
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "", Justification="Build is approved now")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseCmdletCorrectly", "")]
[CmdletBinding(DefaultParameterSetName="SemanticVersion")]
param(
# The path to the module folder, manifest or build.psd1
[Parameter(Position = 0, ValueFromPipelineByPropertyName)]
Expand All @@ -50,8 +59,26 @@ function Build-Module {
[Alias("Destination")]
[string]$OutputDirectory,

# Semantic version, like 1.0.3-beta01+sha.22c35ffff166f34addc49a3b80e622b543199cc5
# If the SemVer has metadata (after a +), then the full Semver will be added to the ReleaseNotes
[Parameter(ParameterSetName="SemanticVersion")]
[string]$SemVer,

# The module version (must be a valid System.Version such as PowerShell supports for modules)
[Alias("ModuleVersion")]
[version]$Version,
[Parameter(ParameterSetName="ModuleVersion", Mandatory)]
[version]$Version = $(if($V = $SemVer.Split("+")[0].Split("-")[0]){$V}),

# Setting pre-release forces the release to be a pre-release.
# Must be valid pre-release tag like PowerShellGet supports
[Parameter(ParameterSetName="ModuleVersion")]
[string]$Prerelease = $($SemVer.Split("+")[0].Split("-")[1]),

# Build metadata (like the commit sha or the date).
# If a value is provided here, then the full Semantic version will be inserted to the release notes:
# Like: ModuleName v(Version(-Prerelease?)+BuildMetadata)
[Parameter(ParameterSetName="ModuleVersion")]
[string]$BuildMetadata = $($SemVer.Split("+")[1]),

# Folders which should be copied intact to the module output
# Can be relative to the module folder
Expand Down Expand Up @@ -101,6 +128,18 @@ function Build-Module {
}
process {
try {
# BEFORE we InitializeBuild we need to "fix" the version
if($PSCmdlet.ParameterSetName -ne "SemanticVersion") {
Write-Verbose "Calculate the Semantic Version from the $Version - $Prerelease + $BuildMetadata"
$SemVer = $Version
if($Prerelease) {
$SemVer = $Version + '-' + $Prerelease
}
if($BuildMetadata) {
$SemVer = $SemVer + '+' + $BuildMetadata
}
}

# Push into the module source (it may be a subfolder)
$ModuleInfo = InitializeBuild $SourcePath
# Output file names
Expand Down Expand Up @@ -159,10 +198,43 @@ function Build-Module {
}
}

Write-Verbose "Update Manifest to $OutputManifest"
try {
if ($Version) {
Write-Verbose "Update Manifest at $OutputManifest with version: $Version"
Update-Metadata -Path $OutputManifest -PropertyName ModuleVersion -Value $Version
}
} catch {
Write-Warning "Failed to update version to $Version. $_"
}

if ($Prerelease) {
Write-Verbose "Update Manifest at $OutputManifest with Prerelease: $Prerelease"
Update-Metadata -Path $OutputManifest -PropertyName PrivateData.PSData.Prerelease -Value $Prerelease
} else {
Update-Metadata -Path $OutputManifest -PropertyName PrivateData.PSData.Prerelease -Value ""
}

if ($Version) {
Update-Metadata -Path $OutputManifest -PropertyName ModuleVersion -Value $Version
if ($BuildMetadata) {
Write-Verbose "Update Manifest at $OutputManifest with metadata: $BuildMetadata from $SemVer"
$RelNote = Get-Metadata -Path $OutputManifest -PropertyName PrivateData.PSData.ReleaseNotes -ErrorAction SilentlyContinue
if ($null -ne $RelNote) {
$Line = "$($ModuleInfo.Name) v$($SemVer)"
if ([string]::IsNullOrWhiteSpace($RelNote)) {
Write-Verbose "New ReleaseNotes:`n$Line"
Update-Metadata -Path $OutputManifest -PropertyName PrivateData.PSData.ReleaseNotes -Value $Line
} elseif ($RelNote -match "^\s*\n") {
# Leading whitespace includes newlines
Write-Verbose "Existing ReleaseNotes:$RelNote"
$RelNote = $RelNote -replace "^(?s)(\s*)\S.*$|^$","`${1}$($Line)`$_"
Write-Verbose "New ReleaseNotes:$RelNote"
Update-Metadata -Path $OutputManifest -PropertyName PrivateData.PSData.ReleaseNotes -Value $RelNote
} else {
Write-Verbose "Existing ReleaseNotes:`n$RelNote"
$RelNote = $RelNote -replace "^(?s)(\s*)\S.*$|^$","`${1}$($Line)`n`$_"
Write-Verbose "New ReleaseNotes:`n$RelNote"
Update-Metadata -Path $OutputManifest -PropertyName PrivateData.PSData.ReleaseNotes -Value $RelNote
}
}
}

# This is mostly for testing ...
Expand Down
111 changes: 110 additions & 1 deletion Tests/Public/Build-Module.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Describe "Build-Module" {
It "has an optional parameter for setting the Version"{
$parameters.ContainsKey("Version") | Should -Be $true
$parameters["Version"].ParameterType | Should -Be ([version])
$parameters["Version"].Attributes.Where{$_ -is [Parameter]}.Mandatory | Should -Be $false
$parameters["Version"].ParameterSets.Keys | Should -Not -Be "__AllParameterSets"
}

It "has an optional parameter for setting the Encoding"{
Expand Down Expand Up @@ -185,4 +185,113 @@ Describe "Build-Module" {
Assert-MockCalled SetModuleContent -ModuleName ModuleBuilder -Times 0
}
}

Context "Setting the version to a SemVer string" {
$SemVer = "1.0.0-beta03+sha.22c35ffff166f34addc49a3b80e622b543199cc5.Date.2018-10-11"
$global:ExpectedVersion = "1.0.0"
Push-Location TestDrive:\ -StackName BuildModuleTest
New-Item -ItemType Directory -Path TestDrive:\MyModule\ -Force
New-Item -ItemType Directory -Path "TestDrive:\$ExpectedVersion\" -Force

Mock SetModuleContent -ModuleName ModuleBuilder {}
Mock Update-Metadata -ModuleName ModuleBuilder {}
Mock InitializeBuild -ModuleName ModuleBuilder {
# These are actually all the values that we need
@{
OutputDirectory = "TestDrive:\$Version"
Name = "MyModule"
ModuleBase = "TestDrive:\MyModule\"
CopyDirectories = @()
Encoding = "UTF8"
PublicFilter = "Public\*.ps1"
}
}

Mock Test-Path {$True} -Parameter {$Path -eq "TestDrive:\$ExpectedVersion"} -ModuleName ModuleBuilder
Mock Remove-Item {} -Parameter {$Path -eq "TestDrive:\$ExpectedVersion"} -ModuleName ModuleBuilder
Mock Set-Location {} -ModuleName ModuleBuilder
Mock Copy-Item {} -ModuleName ModuleBuilder
# Release notes
Mock Get-Metadata { "First Release" } -ModuleName ModuleBuilder
Mock Join-Path {
[IO.Path]::Combine($Path, $ChildPath)
} -ModuleName ModuleBuilder

Mock Get-ChildItem {
[IO.FileInfo]$(Join-Path $(Convert-Path "TestDrive:\") "MyModule\Public\Get-MyInfo.ps1")
} -ModuleName ModuleBuilder

Mock New-Item {} -Parameter {
$Path -eq "TestDrive:\$ExpectedVersion" -and
$ItemType -eq "Directory" -and
$Force -eq $true
} -ModuleName ModuleBuilder

try {
Build-Module -SemVer $SemVer
} catch {
Pop-Location -StackName BuildModuleTest
throw
}

It "Should build to an output folder with the simple version." {
Assert-MockCalled Remove-Item -ModuleName ModuleBuilder
Assert-MockCalled New-Item -ModuleName ModuleBuilder
}

It "Should update the module version to the simple version." {
Assert-MockCalled Update-Metadata -ModuleName ModuleBuilder -ParameterFilter {
$PropertyName -eq "ModuleVersion" -and $Value -eq $ExpectedVersion
}
}
It "Should update the module pre-release version" {
Assert-MockCalled Update-Metadata -ModuleName ModuleBuilder -ParameterFilter {
$PropertyName -eq "PrivateData.PSData.Prerelease" -and $Value -eq "beta03"
}
}
It "When there are simple release notes, it should insert a line with the module name and full semver" {
Assert-MockCalled Update-Metadata -ModuleName ModuleBuilder -ParameterFilter {
$PropertyName -eq "PrivateData.PSData.ReleaseNotes" -and $Value -eq "MyModule v$($SemVer)`nFirst Release"
}
}

It "When there's no release notes, it should insert the module name and full semver" {
# If there's no release notes, but it was left uncommented
Mock Get-Metadata { "" } -ModuleName ModuleBuilder

try {
Build-Module -SemVer $SemVer
} catch {
Pop-Location -StackName BuildModuleTest
throw
}

Assert-MockCalled Update-Metadata -ModuleName ModuleBuilder -ParameterFilter {
$PropertyName -eq "PrivateData.PSData.ReleaseNotes" -and $Value -eq "MyModule v$SemVer"
}
}

It "When there's a prefix empty line, it should insert the module name and full semver the same way" {
# If there's no release notes, but it was left uncommented
Mock Get-Metadata { "
Multi-line Release Notes
With a prefix carriage return" } -ModuleName ModuleBuilder

try {
Build-Module -SemVer $SemVer
} catch {
Pop-Location -StackName BuildModuleTest
throw
}

Assert-MockCalled Update-Metadata -ModuleName ModuleBuilder -ParameterFilter {
$PropertyName -eq "PrivateData.PSData.ReleaseNotes" -and $Value -eq "
MyModule v$SemVer
Multi-line Release Notes
With a prefix carriage return"
}
}

Pop-Location -StackName BuildModuleTest
}
}
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ steps:
- powershell: .\bootstrap.ps1
displayName: 'Restore pre-requisites'

- powershell: .\build.ps1 -OutputDirectory $(Build.ArtifactStagingDirectory)\$(Build.DefinitionName) -Version $(GitVersion.AssemblySemVer) -Verbose
- powershell: .\build.ps1 -OutputDirectory $(Build.ArtifactStagingDirectory)\$(Build.DefinitionName) -SemVer $(GitVersion.InformationalVersion) -Verbose
displayName: 'Run build script'

- task: richardfennellBM.BM-VSTS-PesterRunner-Task.Pester-Task.Pester@8
Expand Down
3 changes: 1 addition & 2 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ param(

# The version of the output module
[Alias("ModuleVersion")]
[version]$Version
[string]$SemVer
)

# Sanitize parameters to pass to Build-Module
Expand All @@ -19,7 +19,6 @@ $null = $PSBoundParameters.Remove('Test')
$ErrorActionPreference = "Stop"
Push-Location $PSScriptRoot -StackName BuildBuildModule
try {

# Build ModuleBuilder with ModuleBuilder:
Write-Verbose "Compiling ModuleBuilderBootstrap module"
$OFS = "`n`n"
Expand Down