PSModuleUtils.psm1
function Build-PSModule { [CmdletBinding()] param ( [String]$Name = 'PSModule', [String]$Version = '0.0.1', [String]$Description = 'A PowerShell module.', [String[]]$Tags = @('PSEdition_Desktop', 'PSEdition_Core', 'Windows'), [String]$SourceDirectory = "$PWD/src", [String]$OutputDirectory = "$PWD/out", [Switch]$FixScriptAnalyzer ) Write-Host -Object 'Building with the following parameters:' Write-Host -Object ( [PSCustomObject]@{ Name = $Name Version = $Version Description = $Description Tags = $Tags SourceDirectory = $SourceDirectory OutputDirectory = $OutputDirectory FixScriptAnalyzer = $FixScriptAnalyzer } | Format-List | Out-String ) Remove-Item -Path $OutputDirectory -Recurse -Force -ErrorAction SilentlyContinue $ModuleOutputDirectory = "$OutputDirectory/$Name/$Version" $null = New-Item -Path "$ModuleOutputDirectory/$name.psm1" -ItemType File -Force $functionNames = @() $moduleContent = @() Get-ChildItem -Path "$SourceDirectory/public" -Filter '*.ps1' -Exclude '*.Tests.ps1' -File -Recurse | ForEach-Object -Process { $functionName = $_.BaseName Write-Host -Object "Building function $functionName..." $functionNames += $functionName $functionContent = Get-Content -Path $_.FullName $originalFunctionContent = $functionContent # Remove any init blocks outside of the function $startIndex = ( $functionContent.IndexOf('<#'), $functionContent.IndexOf($functionContent -match "function $functionName")[0] ) | Where-Object -FilterScript { $_ -ge 0 } | Sort-Object | Select-Object -First 1 $functionContent = $functionContent[$startIndex..($functionContent.Length - 1)] # Format the private function dot sources for the expected folder structure $functionContent = $functionContent -replace '(?<=\. \$PSScriptRoot/)\.\./\.\./private', 'private' Write-Host -Object ( Compare-Object -ReferenceObject $functionContent -DifferenceObject $originalFunctionContent | Format-Table | Out-String ) $moduleContent += '' $moduleContent += $functionContent } $moduleContent | Set-Content -Path "$ModuleOutputDirectory/$name.psm1" -Force $null = New-Item -Path "$ModuleOutputDirectory/private" -ItemType Directory -Force Get-ChildItem -Path "$SourceDirectory/private" -Exclude '*.Tests.ps1' | Copy-Item -Destination "$ModuleOutputDirectory/private" -Recurse -Force $manifestPath = "$ModuleOutputDirectory/$Name.psd1" $repoUrl = ( & git config --get remote.origin.url ).Replace('.git', '') $companyName = if ($repoUrl -match 'github') { $repoUrl.Split('/')[3] } else { $env:USERDOMAIN } $existingGuid = Find-Module -Name $Name -Repository PSGallery -ErrorAction SilentlyContinue | Select-Object -ExpandProperty AdditionalMetadata | Select-Object -ExpandProperty GUID $guid = if ($existingGuid) { $existingGuid } else { ( New-Guid ).Guid } $requiredModulesStatement = Get-Content -Path "$SourceDirectory\$Name.psm1" | Where-Object -FilterScript { $_ -match '#Requires' } $requiredModules = (($requiredModulesStatement -split '-Modules ')[1] -split ',').Trim() $moduleVersion, $modulePrerelease = $Version -split '-', 2 $newModuleManifest = @{ Path = $manifestPath Author = ( & git log --format='%aN' | Sort-Object -Unique ) CompanyName = $companyName Copyright = "(c) $( Get-Date -Format yyyy ) $companyName. All rights reserved." RootModule = "$Name.psm1" ModuleVersion = $moduleVersion Guid = $guid Description = $Description PowerShellVersion = 5.1 FunctionsToExport = $functionNames CompatiblePSEditions = ('Desktop', 'Core') Tags = $Tags ProjectUri = $repoUrl LicenseUri = 'https://opensource.org/licenses/MIT' ReleaseNotes = ( git log -1 --pretty=%B )[0] } if ($requiredModules) { $newModuleManifest['RequiredModules'] = $requiredModules } if ($modulePrerelease) { $newModuleManifest['Prerelease'] = $modulePrerelease } New-ModuleManifest @newModuleManifest Get-Item -Path $manifestPath Get-Module -Name $Name -All | Remove-Module -Force -ErrorAction SilentlyContinue Import-Module -Name $manifestPath -Force -PassThru } function Invoke-PSModuleAnalyzer { [CmdletBinding()] param ( [String]$SourceDirectory = "$PWD/src", [Switch]$Fix ) Invoke-ScriptAnalyzer ` -Path $SourceDirectory ` -Recurse ` -Severity Information ` -Fix:$Fix ` -EnableExit:(!$Fix) ` -ReportSummary } function Publish-PSModule { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param ( [String]$Name = 'PSModule', [String]$OutputDirectory = "$PWD/out", [String]$Repository = 'PSGallery', [String]$ApiKey = $env:PSGALLERYAPIKEY ) Get-Module -Name $Name -All | Remove-Module -Force -Confirm:$false -ErrorAction SilentlyContinue $versionedFolder = Get-ChildItem -Path "$OutputDirectory/$Name" | Select-Object -Last 1 if ($versionedFolder) { Import-Module -Name "$($versionedFolder.FullName)/$Name.psd1" -Force -PassThru Publish-PSResource ` -Path $versionedFolder.FullName ` -ApiKey $ApiKey ` -Repository $Repository Find-PSResource ` -Name $Name ` -Version $versionedFolder.BaseName ` -Prerelease ` -Repository $Repository } else { Write-Warning -Message "No module named $Name found to publish." } } function Test-PSModule { [CmdletBinding()] param ( [String]$Name = 'PSModule', [String]$SourceDirectory = "$PWD/src", [String[]]$Tag ) Install-Module -Name Pester -Force -AllowClobber -SkipPublisherCheck Get-Module -Name $Name -All | Remove-Module -Force -ErrorAction SilentlyContinue $config = New-PesterConfiguration @{ Run = @{ Path = $SourceDirectory } CodeCoverage = @{ Enabled = $true OutputPath = 'tests/coverage.xml' } TestResult = @{ Enabled = $true OutputPath = 'tests/testResults.xml' } Output = @{ Verbosity = 'Detailed' } } if ($Tag) { $config.Filter.Tag = 'Unit' } # TODO: Remove after implementing test result publishing $config.Run.Exit = $true $config.Run.Throw = $true Invoke-Pester -Configuration $config } |