functions/package.ps1
$TYPES = @( 'PackageManifest', 'PackageMetadata', 'PackageDependency', 'PackageFile', 'LocalFile', 'RemoteFile', 'PackageInstaller' ) <# .SYNOPSIS Returns a ChocolateyISOPackage object using the given packages .DESCRIPTION Returns a ChocolateyISOPackage object using the given packages. Each package is validated before it is created. .PARAMETER PackagePath The full file path to where the ISO package files are located .PARAMETER PackageConfig The package configuration data to use when creating the ChocolateyISOPackage .PARAMETER MetaPackage The meta package which ties the ISO and all sub-packages together .PARAMETER Packages A list of packages which are subordinate and require use of the ISO file contents .EXAMPLE $package = New-ChocolateyISOPackage ` -PackagePath 'C:\my\package' ` -PackageConfig $myConfig ` -MetaPackage $MetaPackae ` -Packages @($Package1, $Package2, $Package3) .OUTPUTS A new instance of the ChocolateyISOPackage object #> Function New-ChocolateyISOPackage { param( [string] $PackagePath, [hashtable] $PackageConfig, [ChocolateyPackage] $MetaPackage, [ChocolateyPackage[]] $Packages ) # Clone config to prevent modifying passed in hash table $config = $PackageConfig.Clone() # Add package path, meta package, and packages $config.Add('path', $PackagePath) $config.Add('metapackage', $MetaPackage) $config.Add('packages', $Packages) # Convert configuration to object $packageSchema = Get-Schema 'ChocolateyISOPackage' Invoke-Schema $packageSchema $config 'ChocolateyISOPackage' $TYPES } <# .SYNOPSIS Returns a ChocolateyPackage object using the given configuration .DESCRIPTION Validates the given package configuration data and then creates a new ChocolateyPackage object from the configuration data. .PARAMETER PackagePath The full file path to where the package files are located .PARAMETER PackageConfig The package configuration data to use when creating the ChocolateyPackage object .EXAMPLE $package = New-ChocolateyPackage ` -PackagePath 'C:\my\package' ` -PackageConfig $myConfig .OUTPUTS A new instance of the ChocolateyPackage object #> Function New-ChocolateyPackage { param( [string] $PackagePath, [hashtable] $PackageConfig ) # Clone config to prevent modifying passed in hash table $config = $PackageConfig.Clone() # Add package path and fully qualify local file paths $config.Add('path', $PackagePath) foreach ($localFile in $config.localFiles) { $localFile.localPath = Join-Path $config.Path $localFile.localPath } if ($config.processScript) { $config.processScript = Join-Path $config.Path $config.processScript } # Convert configuration to object $packageSchema = Get-Schema 'ChocolateyPackage' Invoke-Schema $packageSchema $config 'ChocolateyPackage' $TYPES } <# .SYNOPSIS Creates an example package configuration structure at the given path .DESCRIPTION Creates an example package configuration file along with an example process and Chocolatey install file at the given path. This is the easiest way to get started with making a new package. .PARAMETER OutPath The full file path to where the package files will be created .EXAMPLE New-ChocolateyPackageConfig C:\my\package .OUTPUTS None #> Function New-ChocolateyPackageConfig { param( [string] $OutPath ) if (!(Test-Path $OutPath)) { Write-Verbose ('Creating {0}...' -f $OutPath) New-Item -ItemType Directory $OutPath | Out-Null } $packageFile = Join-Path $PSScriptRoot '..\static\package.psd1' Write-Verbose ('Copying package file from {0} to {1}...' -f $packageFile, $OutPath) Copy-Item $packageFile $OutPath | Out-Null } <# .SYNOPSIS Loads the schema file for the given custom object type .DESCRIPTION Searches in the module schema directory ($MODULEROOT\schema) for a schema file for the given custom object type. It assumes a .PSD1 file with the name of the object type will exist in the schema directory and will automatically import the contents and return them. .PARAMETER Name The name of the custom object to load the schema for .EXAMPLE $schema = Get-Schema ChocolateyPackage .OUTPUTS A hashtable containing the imported contents of the schema file #> Function Get-Schema { param( [string] $Name ) $schemaFile = Join-Path $PSScriptRoot ('..\schema\{0}.psd1' -f $Name) if (!(Test-Path $schemaFile)) { throw ('Could not find schema file for {0} at {1}' -f $Name, $schemaFile) } Import-PowerShellDataFile $schemaFile } <# .SYNOPSIS Applies the given schema against the given input object .DESCRIPTION The given input object is first validated against the schema and then any custom types are converted from their hashtable values to their respective object type. Optional properties that are not present in the input object have the default value contained in the schema applied to them. Only the custom types contained in the $CustomTypes parameter are automaticaly converted, the remaining types are left as is after validation. This function is recursive and will operate down the tree of an object performing validation and conversion on all applicable properties. .PARAMETER Name The schema to apply to the input object .PARAMETER InputObject The object to validate and transform .PARAMETER InputObjectType The type of the input object (used for conversion) .PARAMETER CustomTypes A list of types to validate and transform. Each type in the list must have a schema file located in the schema directory. .EXAMPLE $transformedObject = Invoke-Schema $mySchema $object 'MyObjectType' @('CustomObjectType') .OUTPUTS The validated and transformed input object #> Function Invoke-Schema { param( [hashtable] $Schema, [hashtable] $InputObject, [string] $InputObjectType, [string[]] $CustomTypes ) $InputObject = $InputObject.Clone() foreach ($property in $InputObject.GetEnumerator()) { if (!($property.Name -in $Schema.Keys)) { throw ('Error validating configuration: {0} does not have a {1} property' -f $InputObjectType, $property.Name) } } foreach ($property in $Schema.GetEnumerator()) { if (($property.Value.required) -and (!($property.Name -in $InputObject.Keys))) { throw ('Error validating configuration: {0} must have a {1} property' -f $InputObjectType, $property.Name) } if (!($property.Value.Required) -and (!($property.Name -in $InputObject.Keys))) { $InputObject.Add($property.Name, $property.Value.default) } if ($property.Value.type -match '\[\]') { $inputType = $InputObject[$property.Name].GetType() if (!(($inputType.Name -eq 'ArrayList') -or ($inputType.BaseType.Name -eq 'Array'))) { throw ('Error validating configuration: {0} property of {1} must be an array' -f $property.Name, $InputObjectType) } $type = $property.Value.type -replace '\[\]', '' if ($type -in $CustomTypes) { foreach ($subObject in $InputObject[$property.Name]) { $InputObject[$property.Name] = $InputObject[$property.Name].Clone() $index = $InputObject[$property.Name].IndexOf($subObject) $propertySchema = Get-Schema $type $InputObject[$property.Name][$index] = Invoke-Schema $propertySchema $subObject $type $CustomTypes } } } else { if ($property.Value.type -in $CustomTypes) { if (($property.Value.required) -and ($InputObject[$property.Name].Count -eq 0)) { throw ('Error validating configuration: {0} property of {1} cannot be empty' -f $property.Name, $InputObjectType) } if ($InputObject[$property.Name].Count -gt 0) { $propertySchema = Get-Schema $property.Value.type $InputObject[$property.Name] = Invoke-Schema $propertySchema $InputObject[$property.Name] $property.Value.type $CustomTypes } } } } if ($InputObject.GetType() -ne $InputObjectType) { New-Object $InputObjectType -Property $InputObject } else { $InputObject } } |