< Summary - Kestrun — Combined Coverage

Information
Class: Public.Service.New-KrServicePackage
Assembly: Kestrun.PowerShell.Public
File(s): /home/runner/work/Kestrun/Kestrun/src/PowerShell/Kestrun/Public/Service/New-KrServicePackage.ps1
Tag: Kestrun/Kestrun@09cad9a8fdafda7aca15f5f5e888b4bbcc8f0674
Line coverage
89%
Covered lines: 79
Uncovered lines: 9
Coverable lines: 88
Total lines: 272
Line coverage: 89.7%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 03/26/2026 - 03:54:59 Line coverage: 89.7% (79/88) Total lines: 272 Tag: Kestrun/Kestrun@844b5179fb0492dc6b1182bae3ff65fa7365521d

Metrics

File(s)

/home/runner/work/Kestrun/Kestrun/src/PowerShell/Kestrun/Public/Service/New-KrServicePackage.ps1

#LineLine coverage
 1<#
 2.SYNOPSIS
 3    Creates a Kestrun service package (.krpack).
 4.DESCRIPTION
 5    Creates a .krpack archive from either:
 6    - A source folder that already contains Service.psd1 (validated before packaging), or
 7    - A script file plus Name/Version metadata (a Service.psd1 descriptor is generated automatically).
 8
 9    For generated descriptors, FormatVersion is set to '1.0' and EntryPoint is set to the script file name.
 10.PARAMETER SourceFolder
 11    Folder to package. Must contain a valid Service.psd1 descriptor.
 12.PARAMETER ScriptPath
 13    Script file to package. A Service.psd1 descriptor is generated automatically.
 14.PARAMETER Name
 15    Service name used when generating Service.psd1 from ScriptPath.
 16    If omitted, defaults to the script filename without extension.
 17.PARAMETER Version
 18    Service version used when generating Service.psd1 from ScriptPath.
 19.PARAMETER Description
 20    Optional description used when generating Service.psd1 from ScriptPath.
 21    Defaults to Name.
 22.PARAMETER ServiceLogPath
 23    Optional ServiceLogPath written to generated Service.psd1.
 24.PARAMETER PreservePaths
 25    Optional relative file/folder paths to preserve during service update.
 26.PARAMETER OutputPath
 27    Output .krpack path.
 28    Defaults:
 29    - SourceFolder mode: <SourceFolderName>.krpack in current directory
 30    - ScriptPath mode: <Name>-<Version>.krpack in current directory
 31.PARAMETER Force
 32    Overwrite an existing output file.
 33.PARAMETER WhatIf
 34    Shows what would happen if the cmdlet runs. The cmdlet is not executed.
 35.PARAMETER Confirm
 36    Prompts for confirmation before running the cmdlet.
 37.EXAMPLE
 38    New-KrServicePackage -SourceFolder .\my-service -OutputPath .\my-service.krpack
 39.EXAMPLE
 40    New-KrServicePackage -ScriptPath .\server.ps1 -Name demo -Version 1.2.0 -OutputPath .\demo.krpack
 41.EXAMPLE
 42    New-KrServicePackage -ScriptPath .\server.ps1 -Version 1.2.0
 43#>
 44function New-KrServicePackage {
 45    [KestrunRuntimeApi('Everywhere')]
 46    [CmdletBinding(DefaultParameterSetName = 'FromFolder', SupportsShouldProcess)]
 47    [OutputType([pscustomobject])]
 48    param(
 49        [Parameter(Mandatory, ParameterSetName = 'FromFolder')]
 50        [ValidateNotNullOrEmpty()]
 51        [string]$SourceFolder,
 52
 53        [Parameter(Mandatory, ParameterSetName = 'FromScript')]
 54        [ValidateNotNullOrEmpty()]
 55        [string]$ScriptPath,
 56
 57        [Parameter(ParameterSetName = 'FromScript')]
 58        [ValidateNotNullOrEmpty()]
 59        [string]$Name,
 60
 61        [Parameter(Mandatory, ParameterSetName = 'FromScript')]
 62        [ValidateNotNullOrEmpty()]
 63        [version]$Version,
 64
 65        [Parameter(ParameterSetName = 'FromScript')]
 66        [string]$Description,
 67
 68        [Parameter(ParameterSetName = 'FromScript')]
 69        [string]$ServiceLogPath,
 70
 71        [Parameter(ParameterSetName = 'FromScript')]
 72        [string[]]$PreservePaths,
 73
 74        [Parameter()]
 75        [ValidateNotNullOrEmpty()]
 76        [string]$OutputPath,
 77
 78        [Parameter()]
 79        [switch]$Force
 80    )
 81
 82    <#
 83    .SYNOPSIS
 84        Resolves the output path for the .krpack file.
 85    .DESCRIPTION
 86        Determines the full path for the output .krpack file based on the provided path or a default base name.
 87    .PARAMETER ProvidedOutputPath
 88        The user-provided output path.
 89    .PARAMETER DefaultBaseName
 90        The default base name to use if no output path is provided.
 91    .EXAMPLE
 92        Get-KrResolvedOutputPath -ProvidedOutputPath .\output.krpack -DefaultBaseName demo
 93    .EXAMPLE
 94        Get-KrResolvedOutputPath -ProvidedOutputPath '' -DefaultBaseName demo
 95    .EXAMPLE
 96        Get-KrResolvedOutputPath -ProvidedOutputPath $null -DefaultBaseName demo
 97    .OUTPUTS
 98        [string] The resolved full path for the .krpack file.
 99    #>
 100    function Get-KrResolvedOutputPath {
 101        param(
 102            [string]$ProvidedOutputPath,
 103            [string]$DefaultBaseName
 104        )
 105
 1106        $resolved = if ([string]::IsNullOrWhiteSpace($ProvidedOutputPath)) {
 2107            [System.IO.Path]::Combine((Get-Location).Path, "$DefaultBaseName.krpack")
 108        } else {
 1109            [System.IO.Path]::GetFullPath($ProvidedOutputPath)
 110        }
 111
 1112        if (-not $resolved.EndsWith('.krpack', [System.StringComparison]::OrdinalIgnoreCase)) {
 0113            $resolved = "$resolved.krpack"
 114        }
 115
 1116        return $resolved
 117    }
 118
 119    <#
 120    .SYNOPSIS
 121        Creates a zip archive from a directory.
 122    .DESCRIPTION
 123        Compresses the contents of a directory into a .krpack file.
 124    .PARAMETER DirectoryPath
 125        The path of the directory to compress.
 126    .PARAMETER DestinationPath
 127        The path of the resulting .krpack file.
 128    .EXAMPLE
 129        Invoke-KrZipFromDirectory -DirectoryPath .\my-service -DestinationPath .\my-service.krpack
 130    .OUTPUTS
 131        None. Creates a .krpack file at the specified destination.
 132    #>
 133    function Invoke-KrZipFromDirectory {
 134        param(
 135            [string]$DirectoryPath,
 136            [string]$DestinationPath
 137        )
 138
 1139        Add-Type -AssemblyName System.IO.Compression
 1140        Add-Type -AssemblyName System.IO.Compression.FileSystem
 141
 1142        $destinationDirectory = [System.IO.Path]::GetDirectoryName($DestinationPath)
 2143        if (-not [string]::IsNullOrWhiteSpace($destinationDirectory) -and -not (Test-Path -LiteralPath $destinationDirec
 0144            $null = New-Item -ItemType Directory -Path $destinationDirectory -Force
 145        }
 146
 1147        if (Test-Path -LiteralPath $DestinationPath -PathType Leaf) {
 0148            Remove-Item -LiteralPath $DestinationPath -Force
 149        }
 150
 1151        $zip = [System.IO.Compression.ZipFile]::Open($DestinationPath, [System.IO.Compression.ZipArchiveMode]::Create)
 152        try {
 2153            foreach ($file in (Get-ChildItem -LiteralPath $DirectoryPath -File -Recurse -Force)) {
 1154                $relativePath = [System.IO.Path]::GetRelativePath($DirectoryPath, $file.FullName)
 1155                $normalizedRelativePath = $relativePath -replace '\\', '/'
 2156                [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip, $file.FullName, $normalizedRelative
 157            }
 158        } finally {
 1159            $zip.Dispose()
 160        }
 161    }
 162
 1163    $stagingRoot = $null
 1164    $packageRoot = $null
 1165    $descriptorInfo = $null
 166
 167    try {
 1168        if ($PSCmdlet.ParameterSetName -eq 'FromFolder') {
 1169            $packageRoot = [System.IO.Path]::GetFullPath($SourceFolder)
 2170            if (-not (Test-Path -LiteralPath $packageRoot -PathType Container)) {
 0171                throw "Source folder not found: $packageRoot"
 172            }
 173
 1174            $descriptorPath = [System.IO.Path]::Combine($packageRoot, 'Service.psd1')
 2175            if (-not (Test-Path -LiteralPath $descriptorPath -PathType Leaf)) {
 1176                throw "Service descriptor not found: $descriptorPath"
 177            }
 178
 1179            $descriptorData = Import-PowerShellDataFile -LiteralPath $descriptorPath
 1180            $descriptorInfo = Test-KrServiceDescriptorData -Descriptor $descriptorData -DescriptorPath $descriptorPath -
 181
 1182            $defaultBaseName = [System.IO.Path]::GetFileName($packageRoot)
 1183            $resolvedOutputPath = Get-KrResolvedOutputPath -ProvidedOutputPath $OutputPath -DefaultBaseName $defaultBase
 184        } else {
 1185            $resolvedScriptPath = [System.IO.Path]::GetFullPath($ScriptPath)
 2186            if (-not (Test-Path -LiteralPath $resolvedScriptPath -PathType Leaf)) {
 0187                throw "Script file not found: $resolvedScriptPath"
 188            }
 189
 1190            if (-not $resolvedScriptPath.EndsWith('.ps1', [System.StringComparison]::OrdinalIgnoreCase)) {
 0191                throw 'ScriptPath must point to a .ps1 file.'
 192            }
 193
 1194            $scriptFileName = [System.IO.Path]::GetFileName($resolvedScriptPath)
 3195            $effectiveName = if ([string]::IsNullOrWhiteSpace($Name)) { [System.IO.Path]::GetFileNameWithoutExtension($r
 2196            $effectiveDescription = if ([string]::IsNullOrWhiteSpace($Description)) { $effectiveName } else { $Descripti
 197
 2198            $stagingRoot = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "kestrun-krpack-$([Guid]::NewGuid(
 1199            $null = New-Item -ItemType Directory -Path $stagingRoot -Force
 1200            $packageRoot = $stagingRoot
 201
 2202            Copy-Item -LiteralPath $resolvedScriptPath -Destination ([System.IO.Path]::Combine($packageRoot, $scriptFile
 203
 1204            $escapedName = $effectiveName.Replace("'", "''")
 1205            $escapedDescription = $effectiveDescription.Replace("'", "''")
 1206            $escapedVersion = $Version.ToString().Replace("'", "''")
 1207            $escapedEntryPoint = $scriptFileName.Replace("'", "''")
 208
 1209            $descriptorLines = [System.Collections.Generic.List[string]]::new()
 1210            $descriptorLines.Add('@{')
 1211            $descriptorLines.Add("    FormatVersion = '1.0'")
 1212            $descriptorLines.Add("    Name = '$escapedName'")
 1213            $descriptorLines.Add("    Description = '$escapedDescription'")
 1214            $descriptorLines.Add("    Version = '$escapedVersion'")
 1215            $descriptorLines.Add("    EntryPoint = '$escapedEntryPoint'")
 216
 1217            if (-not [string]::IsNullOrWhiteSpace($ServiceLogPath)) {
 0218                $escapedServiceLogPath = $ServiceLogPath.Replace("'", "''")
 0219                $descriptorLines.Add("    ServiceLogPath = '$escapedServiceLogPath'")
 220            }
 221
 1222            if ($null -ne $PreservePaths -and $PreservePaths.Count -gt 0) {
 1223                $descriptorLines.Add('    PreservePaths = @(')
 1224                foreach ($preservePath in $PreservePaths) {
 1225                    if ([string]::IsNullOrWhiteSpace($preservePath)) {
 226                        continue
 227                    }
 228
 1229                    $escapedPreservePath = $preservePath.Replace("'", "''")
 1230                    $descriptorLines.Add("        '$escapedPreservePath'")
 231                }
 232
 1233                $descriptorLines.Add('    )')
 234            }
 235
 1236            $descriptorLines.Add('}')
 237
 1238            $descriptorPath = [System.IO.Path]::Combine($packageRoot, 'Service.psd1')
 2239            Set-Content -LiteralPath $descriptorPath -Value ($descriptorLines -join [Environment]::NewLine) -Encoding ut
 240
 1241            $descriptorData = Import-PowerShellDataFile -LiteralPath $descriptorPath
 1242            $descriptorInfo = Test-KrServiceDescriptorData -Descriptor $descriptorData -DescriptorPath $descriptorPath -
 243
 2244            $defaultBaseName = "$effectiveName-$($Version.ToString())"
 1245            $resolvedOutputPath = Get-KrResolvedOutputPath -ProvidedOutputPath $OutputPath -DefaultBaseName $defaultBase
 246        }
 247
 2248        if ((Test-Path -LiteralPath $resolvedOutputPath -PathType Leaf) -and -not $Force) {
 0249            throw "Output package already exists: $resolvedOutputPath. Use -Force to overwrite."
 250        }
 251
 1252        if ($PSCmdlet.ShouldProcess($resolvedOutputPath, 'Create .krpack package')) {
 1253            Invoke-KrZipFromDirectory -DirectoryPath $packageRoot -DestinationPath $resolvedOutputPath
 254        }
 255
 2256        [pscustomobject]([ordered]@{
 1257                PackagePath = $resolvedOutputPath
 1258                Name = $descriptorInfo.Name
 1259                FormatVersion = $descriptorInfo.FormatVersion
 1260                EntryPoint = $descriptorInfo.EntryPoint
 1261                Description = $descriptorInfo.Description
 1262                Version = $descriptorInfo.Version
 1263                ServiceLogPath = $descriptorInfo.ServiceLogPath
 2264                PreservePaths = @($descriptorInfo.PreservePaths)
 265            }
 266        )
 267    } finally {
 2268        if (-not [string]::IsNullOrWhiteSpace($stagingRoot) -and (Test-Path -LiteralPath $stagingRoot -PathType Containe
 1269            Remove-Item -LiteralPath $stagingRoot -Recurse -Force -ErrorAction SilentlyContinue
 270        }
 271    }
 272}