KMT.ModuleBuilder.psm1
using namespace System.Collections.Generic using namespace System.ComponentModel.Composition using namespace System.IO using namespace System.Management.Automation $Script:PSModuleRoot = $PSScriptRoot # Importing from [/home/vsts/work/1/s/KMT.ModuleBuilder\Private] # ./KMT.ModuleBuilder/Private/Get-KmtBuildVariable.ps1 function Get-KmtBuildVariable { <# .Synopsis Retreives initialization variables .Example $buildVar = Get-KmtBuildVariable .Notes #> [cmdletbinding()] param() end { $script:buildInit } } # ./KMT.ModuleBuilder/Private/Get-KmtSpecification.ps1 function Get-KmtSpecification { <# .Synopsis Gets the module.kmt.json file for the module .Example Get-KmtSpecification -Path $Path .Notes Searches specified folder first Then sub folders Then parent folders #> [cmdletbinding()] param( [Parameter( Position = 0, ValueFromPipeline )] [string] $Path = (Get-Location) ) process { try { $filter = @{filter = 'module.kmt.json'} Write-Debug "Load from local folder [$path]" $specFile = Get-ChildItem $Path @filter if(-not $specFile) { Write-Debug 'Check all child folders' $specFile = Get-ChildItem $Path @filter -Recurse | Select -First 1 } if(-not $specFile) { while($Path = Split-Path $Path) { Write-Debug "Walk parent folders [$Path]" $specFile = Get-ChildItem $Path @filter } } if(-not $specFile) { Write-Debug 'Specification was not found' throw [System.IO.FileNotFoundException]::new( 'Unable to find [module.kmt.json] specification', 'module.kmt.json' ) } Write-Verbose "Loading KMT Specification [$($specFile.FullName)]" $specification = Get-Content -Path $specFile -Raw | ConvertFrom-Json # attach spec path so we can find the root of the project $member = @{ MemberType = 'NoteProperty' Name = 'specificationPath' Value = $specFile.FullName } $specification | Add-Member @member $specification } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } } # ./KMT.ModuleBuilder/Private/Get-ModulePublicInterfaceMap.ps1 function Get-ModulePublicInterfaceMap { <# .Notes Imports a module and build a map of the public interface that is easy to compareS #> param($Path) $module = Import-KmtModule -Path $Path -PassThru -Verbose:$false $exportedCommands = @( $module.ExportedFunctions.values $module.ExportedCmdlets.values $module.ExportedAliases.values ) foreach($command in $exportedCommands) { foreach ($parameter in $command.Parameters.Keys) { if($false -eq $command.Parameters[$parameter].IsDynamic) { '{0}:{1}' -f $command.Name, $command.Parameters[$parameter].Name foreach ($alias in $command.Parameters[$parameter].Aliases) { '{0}:{1}' -f $command.Name, $alias } } } } } # ./KMT.ModuleBuilder/Private/Initialize-KmtModuleProject.ps1 function Initialize-KmtModuleProject { <# .Synopsis Initializes several project values used by other functions .Example Initialize-KmtModuleProject -Path $Path .Notes #> [Export()] [cmdletbinding()] param( [string] # Root folder of the project $Path = (Get-Location) ) end { try { $spec = Get-KmtSpecification -Path $Path $moduleName = $spec.moduleName $buildRoot = Split-Path -Path $spec.specificationPath $folders = $spec.folders Write-Verbose "Initializing build variables" $output = Join-Path -Path $buildRoot -ChildPath 'Output' $destination = Join-Path -Path $Output -ChildPath $moduleName $script:buildInit = @{ ModuleName = $moduleName BuildRoot = $buildRoot DocsPath = Join-Path -Path $buildRoot -ChildPath 'Docs' Output = $output Source = Join-Path -Path $buildRoot -ChildPath $moduleName Destination = $destination ManifestPath = Join-Path -Path $destination -ChildPath "$moduleName.psd1" ModulePath = Join-Path -Path $destination -ChildPath "$moduleName.psm1" Folders = $folders TestFile = Join-Path -Path $output -ChildPath "TestResults_PS${PSVersion}_$TimeStamp.xml" PSRepository = 'PSGallery' } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } } # ./KMT.ModuleBuilder/Private/Move-Statement.ps1 function Move-Statement { <# .SYNOPSIS Moves statements containing a specified token to the specified index in a file. .DESCRIPTION Move-Statement moves statements containing a specified token, to the specified index in a file. This can be used when building a module to move any using directives and #Requires statements to the top of a file. .PARAMETER Path Specifies the path to an item to get its contents. .PARAMETER Type Specifies the type of tokens to examine. Accepted values include "Comment" and "Keyword". .PARAMETER Token Specifies the contents to filter on when examining a supplied token. .PARAMETER Index Specifies the line to move a statement to. Each line in an item has a corresponding index, starting from 0. .EXAMPLE Move-Statement -Path $Path -Type 'Comment', 'Keyword' -Token '#Requires', 'using' -Index 0 Moves any using directives or #Requires statements to the top of a file. .NOTES Copy/Paste from LDModuleBuilder #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-Path -Path $PSItem })] [string] $Path, [Parameter(Position = 1, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [ValidateSet('Comment', 'Keyword')] [string[]] $Type = ('Comment', 'Keyword'), [Parameter(Position = 2, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string[]] $Token = ('#Requires', 'using'), [Parameter(Position = 3, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [int] $Index = 0 ) process { try { $statements = [SortedSet[String]]::new( [StringComparer]::InvariantCultureIgnoreCase ) Write-Verbose -Message "Reading content from $Path..." $content = [List[String]] (Get-Content $Path) Write-Verbose -Message "Tokenizing content from $Path..." $tokens = [PSParser]::Tokenize($content, [ref] $null) $match = $Token -join '|' Write-Verbose -Message 'Matching tokens...' Write-Verbose -Message "Type = [$Type]; Token = [$Token]" $keywords = $tokens.Where({ $PSItem.Type -in $Type -and $PSItem.Content -imatch "^(?:$match)" }) if (-not $keywords) { Write-Verbose -Message 'No matching tokens found! Returning...' return } $offset = 1 foreach ($keyword in $keywords) { $line = $keyword.StartLine - $offset Write-Verbose -Message "Moving [$($content[$line])] to Index [$Index]..." $null = $statements.Add($content[$line]), $content.RemoveAt($line) $offset++ } [string[]] $comments, [string[]] $statements = $statements.Where({ $PSItem -match '^#' }, 'Split') foreach ($item in ($statements, $comments)) { $content.Insert($Index, '') $content.InsertRange($Index, $item) } if ($PSCmdlet.ShouldProcess($Path, $MyInvocation.MyCommand.Name)) { Write-Verbose -Message "Writing content to $Path..." Set-Content -Path $Path -Value $content -Encoding 'UTF8' } } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtAnalyzeTask.ps1 function Invoke-KmtAnalyzeTask { <# .Synopsis Invokes script analzer .Example Invoke-KmtAnalyzeTask -Path $Path .Notes #> [cmdletbinding()] param() try { $BuildVariables = Get-KmtBuildVariable $scripts = Get-ChildItem -Path (Join-Path $PSModuleRoot 'Pester') | Foreach-Object {@{ Path = $_.FullName Parameters = @{BuildVariables = $BuildVariables} }} $pester = @{ Script = $scripts PassThru = $true Show = 'Failed', 'Fails', 'Summary' } $results = Invoke-Pester @pester if ($results.FailedCount -gt 0) { Write-Error -Message "Failed [$($results.FailedCount)] Ktm Pester tests." -ErrorAction Stop } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtBuildManifestTask.ps1 function Invoke-KmtBuildManifestTask { <# .Synopsis Copies the manifest and updates the functions to export .Example Invoke-KmtBuildManifestTask -Path $Path .Notes #> [cmdletbinding()] param() end { try { $ManifestPath = (Get-KmtBuildVariable).ManifestPath $ModuleName = (Get-KmtBuildVariable).ModuleName $Source = (Get-KmtBuildVariable).Source Write-Verbose "Updating [$ManifestPath]..." Copy-Item -Path "$Source\$ModuleName.psd1" -Destination $ManifestPath $functions = Get-ChildItem -Path "$ModuleName\Public" -Recurse -Filter *.ps1 -ErrorAction 'Ignore' | Where-Object 'Name' -notmatch 'Tests' if ($functions) { Write-Verbose 'Setting FunctionsToExport...' Set-ModuleFunction -Name $ManifestPath -FunctionsToExport $functions.BaseName } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtBuildModuleTask.ps1 function Invoke-KmtBuildModuleTask { <# .Synopsis Executes the build module task .Example Invoke-KmtBuildModuleTask .Notes #> [cmdletbinding()] param() try { $source = (Get-KmtBuildVariable).Source $modulePath = (Get-KmtBuildVariable).ModulePath $buildRoot = (Get-KmtBuildVariable).BuildRoot $folders = (Get-KmtBuildVariable).Folders $sb = [Text.StringBuilder]::new() $null = $sb.AppendLine('$Script:PSModuleRoot = $PSScriptRoot') # Class importer $root = Join-Path -Path $source -ChildPath 'Classes' Write-Verbose "Load classes from [$root]" $classFiles = Get-ChildItem -Path $root -Filter '*.ps1' -Recurse | Where-Object Name -notlike '*.Tests.ps1' $classes = @{ } foreach ($file in $classFiles) { $name = $file.BaseName $classes[$name] = @{ Name = $name Path = $file.FullName } $data = Get-Content $file.fullname foreach ($line in $data) { if ($line -match "\s+($Name)\s*(:|requires)\s*(?<baseclass>\w*)|\[(?<baseclass>\w+)\]") { $classes[$name].Base += @($Matches.baseclass) } } } $importOrder = $classes.GetEnumerator() | Resolve-DependencyOrder -Key { $_.Name } -DependsOn { $_.Value.Base } foreach ($class in $importOrder) { $classPath = $class.Value.Path Write-Verbose "Importing [$classPath]..." $null = $sb.AppendLine("# .$classPath") $null = $sb.AppendLine([IO.File]::ReadAllText($classPath)) } foreach ($folder in ($Folders -ne 'Classes')) { if (Test-Path -Path "$Source\$folder") { $null = $sb.AppendLine("# Importing from [$Source\$folder]") $files = Get-ChildItem -Path "$Source\$folder" -Recurse -Filter *.ps1 | Where-Object 'Name' -notlike '*.Tests.ps1' foreach ($file in $files) { $name = $file.Fullname.Replace($buildroot, '') Write-Verbose "Importing [$($file.FullName)]..." $null = $sb.AppendLine("# .$name") $null = $sb.AppendLine([IO.File]::ReadAllText($file.FullName)) } } } Write-Verbose "Creating Module [$ModulePath]..." $null = New-Item -Path (Split-path $ModulePath) -ItemType Directory -ErrorAction SilentlyContinue -Force Set-Content -Path $ModulePath -Value $sb.ToString() -Encoding 'UTF8' Write-Verbose 'Moving "#Requires" statements and "using" directives...' Move-Statement -Path $ModulePath -Type 'Comment', 'Keyword' -Token '#Requires', 'using' -Index 0 } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtCleanTask.ps1 function Invoke-KmtCleanTask { <# .Synopsis Cleans the project .Example Invoke-KmtCleanTask .Notes #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [cmdletbinding(SupportsShouldProcess)] param() try { Initialize-KmtModuleProject -Path "$testdrive\SampleModule" $Output = (Get-KmtBuildVariable).Output if (Test-Path $Output) { Write-Verbose "Cleaning Output files in [$Output]..." $null = Get-ChildItem -Path $Output -File -Recurse | Remove-Item -Force -ErrorAction 'Ignore' Write-Verbose "Cleaning Output directories in [$Output]..." $null = Get-ChildItem -Path $Output -Directory -Recurse | Remove-Item -Recurse -Force -ErrorAction 'Ignore' } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtCopyTask.ps1 function Invoke-KmtCopyTask { <# .Synopsis Copy module files and structure into build output .Example Invoke-KmtCopyTask .Notes #> [cmdletbinding()] param( ) begin { } end { try { $source = (Get-KmtBuildVariable).Source $destination = (Get-KmtBuildVariable).Destination $moduleName = (Get-KmtBuildVariable).ModuleName $buildRoot = (Get-KmtBuildVariable).BuildRoot $folders = (Get-KmtBuildVariable).Folders "Creating Directory [$Destination]..." $null = New-Item -ItemType 'Directory' -Path $Destination -ErrorAction 'Ignore' $files = Get-ChildItem -Path $Source -File | Where-Object 'Name' -notmatch "$ModuleName\.ps[dm]1" foreach ($file in $files) { 'Creating [.{0}]...' -f $file.FullName.Replace($buildroot, '') Copy-Item -Path $file.FullName -Destination $Destination -Force } $directories = Get-ChildItem -Path $Source -Directory | Where-Object 'Name' -notin $Folders foreach ($directory in $directories) { 'Creating [.{0}]...' -f $directory.FullName.Replace($buildroot, '') Copy-Item -Path $directory.FullName -Destination $Destination -Recurse -Force } $license = Join-Path -Path $buildroot -ChildPath 'LICENSE' if ( Test-Path -Path $license -PathType Leaf ) { Copy-Item -Path $license -Destination $Destination } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtDotNetCompileTask.ps1 function Invoke-KmtDotNetCompileTask { <# .Synopsis Compiles DotNet binary module .Example Invoke-KmtDotNetCompileTask -Path $Path .Notes #> [cmdletbinding()] param() end { try { $buildRoot = (Get-KmtBuildVariable).BuildRoot $destination = (Get-KmtBuildVariable).Destination $csproj = Get-ChildItem -Path $buildRoot -Include *.csproj -Recurse if ($csproj) { # This build command requires .Net Core # TODO add check for dotnet Write-Verbose "Building Binary Module" $csproj = Get-ChildItem -Path $BuildRoot -Include *.csproj -Recurse $folder = Split-Path $csproj dotnet build $folder -c Release -o $Destination\bin } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtGenerateHelpTask.ps1 function Invoke-KmtGenerateHelpTask { <# .Synopsis Generates help from markdown files .Example Invoke-KmtGenerateHelpTask .Notes #> [cmdletbinding()] param() try { $docsPath = (Get-KmtBuildVariable).DocsPath $destination = (Get-KmtBuildVariable).Destination $moduleName = (Get-KmtBuildVariable).ModuleName if (-not(Get-ChildItem -Path $DocsPath -Filter '*.md' -Recurse -ErrorAction 'Ignore')) { Write-Verbose "No Markdown help files to process. Skipping help file generation..." return } $locales = (Get-ChildItem -Path $DocsPath -Directory).Name foreach ($locale in $locales) { $params = @{ ErrorAction = 'SilentlyContinue' Force = $true OutputPath = "$Destination\en-US" Path = "$DocsPath\en-US" } # Generate the module's primary MAML help file. Write-Verbose "Creating new External help for [$ModuleName]..." $null = New-ExternalHelp @params } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtGenerateMarkdown.ps1 function Invoke-KmtGenerateMarkdown { <# .Synopsis Generates mardown files for all functions .Example Invoke-KmtGenerateMarkdown .Notes #> [cmdletbinding()] param( ) try { $manifestPath = (Get-KmtBuildVariable).ManifestPath $docsPath = (Get-KmtBuildVariable).DocsPath $moduleName = (Get-KmtBuildVariable).ModuleName $module = Import-KmtModule -Path $ManifestPath -PassThru try { if ($module.ExportedFunctions.Count -eq 0) { Write-Verbose 'No functions have been exported for this module. Skipping Markdown generation...' return } if (Get-ChildItem -Path $DocsPath -Filter '*.md' -Recurse) { $items = Get-ChildItem -Path $DocsPath -Directory -Recurse foreach ($item in $items) { Write-Verbose "Updating Markdown help in [$($item.BaseName)]..." $null = Update-MarkdownHelp -Path $item.FullName -AlphabeticParamsOrder } } $params = @{ AlphabeticParamsOrder = $true ErrorAction = 'SilentlyContinue' Locale = 'en-US' Module = $ModuleName OutputFolder = "$DocsPath\en-US" WithModulePage = $true } # ErrorAction is set to SilentlyContinue so this # command will not overwrite an existing Markdown file. Write-Verbose "Creating new Markdown help for [$ModuleName]..." $null = New-MarkdownHelp @params } finally { Remove-Module -Name $ModuleName -Force } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtImportBuiltModuleTask.ps1 function Invoke-KmtImportBuiltModuleTask { <# .Synopsis Imports the built module .Example Invoke-KmtImportBuiltModuleTask .Notes #> [cmdletbinding()] param() try { $path = (Get-KmtBuildVariable).ManifestPath Import-KmtModule -Path $path -Force } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtImportDevModuleTask.ps1 function Invoke-KmtImportDevModuleTask { <# .Synopsis Import the dev version of the module .Example Invoke-KmtImportDevModuleTask .Notes #> [cmdletbinding()] param() try { $source = (Get-KmtBuildVariable).Source $moduleName = (Get-KmtBuildVariable).ModuleName Import-KmtModule -Path "$Source\$ModuleName.psd1" -Force } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtInstallModule.ps1 function Invoke-KmtInstallModule { <# .Synopsis Installs this module to the system .Example Invoke-KmtInstallModule .Notes #> [cmdletbinding()] param( # Parameter help description [Parameter( Mandatory, Position = 0, ValueFromPipelineByPropertyName )] [ValidateNotNullOrEmpty()] [String[]] $Path ) try { $manifestPath = (Get-KmtBuildVariable).ManifestPath $moduleName = (Get-KmtBuildVariable).ModuleName $destination = (Get-KmtBuildVariable).Destination $version = [version] (Get-Metadata -Path $manifestPath -PropertyName 'ModuleVersion') $path = $env:PSModulePath.Split(';').Where( { $_ -like 'C:\Users\*' }, 'First', 1) if ($path -and (Test-Path -Path $path)) { Write-Verbose "Using [$path] as base path..." $path = Join-Path -Path $path -ChildPath $ModuleName $path = Join-Path -Path $path -ChildPath $version Write-Verbose "Creating directory at [$path]..." New-Item -Path $path -ItemType 'Directory' -Force -ErrorAction 'Ignore' Write-Verbose "Copying items from [$Destination] to [$path]..." Copy-Item -Path "$Destination\*" -Destination $path -Recurse -Force } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtPesterTask.ps1 function Invoke-KmtPesterTask { <# .Synopsis Invokes Pester .Example Invoke-KmtPesterTask -Path $Path .Notes #> [cmdletbinding()] param( ) try { $testFile = (Get-KmtBuildVariable).TestFile $requiredPercent = $Script:CodeCoveragePercent $params = @{ OutputFile = $testFile OutputFormat = 'NUnitXml' PassThru = $true Path = 'Tests' Show = 'Failed', 'Fails', 'Summary' Tag = 'Build' } if ($requiredPercent -gt 0.00) { $params['CodeCoverage'] = 'Output\*\*.psm1' $params['CodeCoverageOutputFile'] = 'Output\codecoverage.xml' } $results = Invoke-Pester @params if ($results.FailedCount -gt 0) { Write-Error -Message "Failed [$($results.FailedCount)] Pester tests." } if ($results.codecoverage.NumberOfCommandsAnalyzed -gt 0) { $codeCoverage = $results.codecoverage.NumberOfCommandsExecuted / $results.codecoverage.NumberOfCommandsAnalyzed if ($codeCoverage -lt $requiredPercent) { Write-Error ("Failed Code Coverage [{0:P}] below {1:P}" -f $codeCoverage, $requiredPercent) } } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtPublishModuleTask.ps1 function Invoke-KmtPublishModuleTask { <# .Synopsis Publishes to Repository .Example Invoke-KmtPublishModuleTask -Path $Path .Notes #> [cmdletbinding()] param( ) try { $Destination = (Get-KmtBuildVariable).Description $PSRepository = (Get-KmtBuildVariable).PSRepository if ( $ENV:BHBuildSystem -ne 'Unknown' -and $ENV:BHBranchName -eq "master" -and -not [string]::IsNullOrWhiteSpace($ENV:nugetapikey)) { $publishModuleSplat = @{ Path = $Destination NuGetApiKey = $ENV:nugetapikey Verbose = $true Force = $true Repository = $PSRepository ErrorAction = 'Stop' } Write-Verbose "Files in module output:" Get-ChildItem $Destination -Recurse -File | Select-Object -Expand FullName Write-Verbose "Publishing [$Destination] to [$PSRepository]" Publish-Module @publishModuleSplat } else { Write-Verbose ("Skipping deployment: To deploy, ensure that...`n" + "`t* You are in a known build system (Current: $ENV:BHBuildSystem)`n" + "`t* You are committing to the master branch (Current: $ENV:BHBranchName) `n" + "`t* The repository APIKey is defined in `$ENV:nugetapikey (Current: $(![string]::IsNullOrWhiteSpace($ENV:nugetapikey))) `n" + "`t* This is not a pull request") } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtPublishVersionTask.ps1 function Invoke-KmtPublishVersionTask { <# .Synopsis Publishes the version back to the build system .Example Invoke-KmtPublishVersionTask -Path $Path .Notes #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '')] [cmdletbinding()] param( ) try { $manifestPath = (Get-KmtBuildVariable).ManifestPath [version] $sourceVersion = (Get-Metadata -Path $manifestPath -PropertyName 'ModuleVersion') Write-Host "##vso[build.updatebuildnumber]$sourceVersion" # Do the same for appveyor # https://www.appveyor.com/docs/build-worker-api/#update-build-details } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtSemVerTask.ps1 function Invoke-KmtSemVerTask { <# .Synopsis Calculates the SemVersion and instersts it into the manifest file .Example Invoke-KmtSemVerTask .Notes #> [cmdletbinding(SupportsShouldProcess)] param( #$SourcePath, #[parameter(Mandatory)] #$ManifestPath, #$ModuleName, #$Repository = 'PSGallery' ) $ManifestPath = (Get-KmtBuildVariable).ManifestPath $ModuleName = (Get-KmtBuildVariable).ModuleName try { if ($PSCmdlet.ShouldProcess($ManifestPath)) { $output = (Get-KmtBuildVariable).Output $version = [version]"0.1.0" $publishedModule = $null $bumpVersionType = 'Patch' $versionStamp = (git rev-parse origin/master) + (git rev-parse head) Write-Verbose "Load current version from [$manifestPath]" [version] $sourceVersion = (Get-Metadata -Path $manifestPath -PropertyName 'ModuleVersion') Write-Verbose " Source version [$sourceVersion]" $downloadFolder = Join-Path -Path $output downloads $null = New-Item -ItemType Directory -Path $downloadFolder -Force -ErrorAction Ignore $versionFile = Join-Path $downloadFolder versionfile if (Test-Path $versionFile) { $versionFileData = Get-Content $versionFile -raw if ($versionFileData -eq $versionStamp) { return } } Write-Verbose "Checking for published version" $publishedModule = Find-Module -Name $ModuleName -ErrorAction 'Ignore' | Sort-Object -Property { [version]$_.Version } -Descending | Select -First 1 if ($null -ne $publishedModule) { [version] $publishedVersion = $publishedModule.Version Write-Verbose " Published version [$publishedVersion]" $version = $publishedVersion Write-Verbose "Downloading published module to check for breaking changes" $publishedModule | Save-Module -Path $downloadFolder [HashSet[string]] $publishedInterface = @(Get-ModulePublicInterfaceMap -Path (Join-Path $downloadFolder $ModuleName)) [HashSet[string]] $buildInterface = @(Get-ModulePublicInterfaceMap -Path $ManifestPath) if (-not $publishedInterface.IsSubsetOf($buildInterface)) { $bumpVersionType = 'Major' } elseif ($publishedInterface.count -ne $buildInterface.count) { $bumpVersionType = 'Minor' } } if ($version -lt ([version] '1.0.0')) { Write-Verbose "Module is still in beta; don't bump major version." if ($bumpVersionType -eq 'Major') { $bumpVersionType = 'Minor' } else { $bumpVersionType = 'Patch' } } Write-Verbose " Steping version [$bumpVersionType]" $version = [version] (Step-Version -Version $version -Type $bumpVersionType) Write-Verbose " Comparing to source version [$sourceVersion]" if ($sourceVersion -gt $version) { Write-Verbose " Using existing version" $version = $sourceVersion } if ( -not [string]::IsNullOrEmpty( $env:Build_BuildID ) ) { $build = $env:Build_BuildID $version = [version]::new($version.Major, $version.Minor, $version.Build, $build) } elseif ( -not [string]::IsNullOrEmpty( $env:APPVEYOR_BUILD_ID ) ) { $build = $env:APPVEYOR_BUILD_ID $version = [version]::new($version.Major, $version.Minor, $version.Build, $build) } Write-Verbose " Setting version [$version]" Update-Metadata -Path $ManifestPath -PropertyName 'ModuleVersion' -Value $version (Get-Content -Path $ManifestPath -Raw -Encoding UTF8) | ForEach-Object { $_.TrimEnd() } | Set-Content -Path $ManifestPath -Encoding UTF8 Set-Content -Path $versionFile -Value $versionStamp -NoNewline -Encoding UTF8 } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } # This cleans up files from previous implementation $BuildRoot = (Get-KmtBuildVariable).BuildRoot if (Test-Path $BuildRoot\fingerprint) { Remove-Item $BuildRoot\fingerprint } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KmtUpdateSourceTask.ps1 function Invoke-KmtUpdateSourceTask { <# .Synopsis Moves manifest changes back into source .Example Invoke-KmtUpdateSourceTask .Notes #> [cmdletbinding()] param( ) end { try { $source = (Get-KmtBuildVariable).Source $manifestPath = (Get-KmtBuildVariable).ManifestPath $moduleName = (Get-KmtBuildVariable).moduleName Copy-Item -Path $ManifestPath -Destination "$Source\$ModuleName.psd1" $content = Get-Content -Path "$Source\$ModuleName.psd1" -Raw -Encoding UTF8 $content.Trim() | Set-Content -Path "$Source\$ModuleName.psd1" -Encoding UTF8 } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } } # ./KMT.ModuleBuilder/Private/Tasks/Invoke-KtmUninstallModule.ps1 function Invoke-KtmUninstallModule { <# .Synopsis Uninstalls this module .Example Invoke-KtmUninstallModule -Path $Path .Notes #> [cmdletbinding()] param( # Parameter help description [Parameter( Mandatory, Position = 0, ValueFromPipelineByPropertyName )] [ValidateNotNullOrEmpty()] [String[]] $Path ) try { $moduleName = (Get-KmtBuildVariable).ModuleName Write-Verbose 'Unloading Modules...' Get-Module -Name $ModuleName -ErrorAction 'Ignore' | Remove-Module -Force Write-Verbose 'Uninstalling Module packages...' $modules = Get-Module $ModuleName -ErrorAction 'Ignore' -ListAvailable foreach ($module in $modules) { Uninstall-Module -Name $module.Name -RequiredVersion $module.Version -ErrorAction 'Ignore' } Write-Verbose 'Cleaning up manually installed Modules...' $path = $env:PSModulePath.Split(';').Where( { $_ -like 'C:\Users\*' }, 'First', 1) $path = Join-Path -Path $path -ChildPath $ModuleName if ($path -and (Test-Path -Path $path)) { Write-Verbose 'Removing files... (This may fail if any DLLs are in use.)' Get-ChildItem -Path $path -File -Recurse | Remove-Item -Force | ForEach-Object 'FullName' Write-Verbose 'Removing folders... (This may fail if any DLLs are in use.)' Remove-Item $path -Recurse -Force } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } # Importing from [/home/vsts/work/1/s/KMT.ModuleBuilder\Public] # ./KMT.ModuleBuilder/Public/Build-KmtModule.ps1 function Build-KmtModule { <# .Synopsis Executes all the build actions for a module .Example Build-KmtModule -Path $Path .Notes #> [cmdletbinding()] param( [Alias('FullName')] [Parameter( Position = 0, ValueFromPipelineByPropertyName )] [string] $Path = (Get-Location) ) try { #$Script:ModuleName = Get-ChildItem .\*\*.psm1 | Select-object -ExpandProperty BaseName #$Script:CodeCoveragePercent = 0.0 # 0 to 1 Initialize-KmtModuleProject -Path $Path $init = Get-KmtBuildVariable foreach($key in $init.Keys) { Write-Verbose " $key [$($init[$key])]" -Verbose } #Build Write-Verbose 'Copy' Invoke-KmtCopyTask Write-Verbose 'Compile' Invoke-KmtDotNetCompileTask Write-Verbose 'BuildModule' Invoke-KmtBuildModuleTask Write-Verbose 'BuildManifest' Invoke-KmtBuildManifestTask Write-Verbose 'SetVersion' Invoke-KmtSemVerTask Write-Verbose 'GenerateMarkdown' Invoke-KmtGenerateMarkdown Write-Verbose 'GenerateHelp' Invoke-KmtGenerateHelpTask Write-Verbose 'ImportModule' Invoke-KmtImportBuiltModuleTask Write-Verbose 'Analyze' Invoke-KmtAnalyzeTask Write-Verbose 'Pester' Invoke-KmtPesterTask Write-Verbose 'UpdateSource' Invoke-KmtUpdateSourceTask } catch { #Write-Error -ErrorRecord $PSItem -ErrorAction Stop $PSCmdlet.ThrowTerminatingError( $PSItem ) } } # ./KMT.ModuleBuilder/Public/Import-KmtModule.ps1 function Import-KmtModule { <# .SYNOPSIS Unloads existing module before importing from a path .Example Import-KmtModule -Path $Path #> param( [string]$path, [switch]$PassThru ) if (-not(Test-Path -Path $path)) { Write-Verbose "Cannot find [$path]." Write-Error -Message "Could not find module manifest [$path]" } else { $file = Get-Item $path $name = $file.BaseName $loaded = Get-Module -Name $name -All -ErrorAction Ignore if ($loaded) { Write-Verbose "Unloading Module [$name] from a previous import..." $loaded | Remove-Module -Force } Write-Verbose "Importing Module [$name] from [$($file.fullname)]..." $splat = @{ Name = $file.fullname Force = $true PassThru = $PassThru Scope = 'Global' Verbose = $false } Import-Module @splat } } # ./KMT.ModuleBuilder/Public/Reset-KtmModule.ps1 function Reset-KtmModule { <# .Synopsis Clears the build output for the module .Example Reset-KtmModule -Path $Path .Notes #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [Alias('Clean-KtmModule')] [cmdletbinding(SupportsShouldProcess)] param( [Alias('FullName')] [Parameter( Position = 0, ValueFromPipelineByPropertyName )] [ValidateNotNullOrEmpty()] [String[]] $Path = (Get-Location) ) process { try { foreach($folder in $Path) { Initialize-KmtModuleProject -Path $Folder Invoke-KmtCleanTask } } catch { $PSCmdlet.ThrowTerminatingError( $PSItem ) } } end { } } |