├── .gitignore
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── CHANGELOG.md
├── LICENSE
├── Modules
└── ScriptConfig
│ ├── Functions
│ └── Get-ScriptConfig.ps1
│ ├── Helpers
│ ├── ConvertFrom-ScriptConfigIni.ps1
│ ├── ConvertFrom-ScriptConfigJson.ps1
│ └── ConvertFrom-ScriptConfigXml.ps1
│ ├── Resources
│ ├── ScriptConfig.Formats.ps1xml
│ └── ScriptConfig.Types.ps1xml
│ ├── ScriptConfig.psd1
│ ├── ScriptConfig.psm1
│ ├── Tests
│ ├── Integration
│ │ ├── IniConfigDemo.ps1
│ │ ├── IniConfigDemo.ps1.config
│ │ ├── JsonConfigDemo.ps1
│ │ ├── JsonConfigDemo.ps1.config
│ │ ├── XmlConfigDemo.ps1
│ │ └── XmlConfigDemo.ps1.config
│ └── Unit
│ │ ├── ConvertFrom-ScriptConfigIni.Tests.ps1
│ │ ├── ConvertFrom-ScriptConfigJson.Tests.ps1
│ │ ├── ConvertFrom-ScriptConfigXml.Tests.ps1
│ │ ├── Get-ScriptConfig.Tests.ps1
│ │ └── TestData
│ │ ├── config.ini
│ │ ├── config.json
│ │ └── config.xml
│ └── en-US
│ └── about_ScriptConfig.help.txt
├── README.md
├── appveyor.yml
├── build.debug.ps1
├── build.psake.ps1
└── build.settings.ps1
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore project files
2 | /bin/
3 | /tst/
4 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "type": "PowerShell",
6 | "request": "launch",
7 | "name": "PowerShell Launch Debug File",
8 | "script": "${workspaceRoot}\\build.debug.ps1",
9 | "args": [],
10 | "cwd": "${workspaceRoot}",
11 | "createTemporaryIntegratedConsole": true
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | // Trim trailing whitespaces, but not for markdown
3 | "files.trimTrailingWhitespace": true,
4 | "[markdown]": {
5 | "files.trimTrailingWhitespace": false
6 | },
7 |
8 | // Exclude files form the explorer tree
9 | "files.exclude": {
10 | "bin/**": true,
11 | "tst/**": true,
12 | "Sources/*/.vs/**": true,
13 | "Sources/*/packages/**": true,
14 | "Sources/*/TestResults/**": true
15 | },
16 | "search.exclude": {
17 | "bin": true,
18 | "tst": true
19 | },
20 |
21 | // PowerShell console behaviour
22 | "powershell.debugging.createTemporaryIntegratedConsole": true,
23 | "powershell.integratedConsole.showOnStartup": true,
24 | "powershell.integratedConsole.focusConsoleOnExecute": false,
25 |
26 | // PowerShell code formatting
27 | "powershell.codeFormatting.openBraceOnSameLine": false,
28 | "powershell.codeFormatting.newLineAfterOpenBrace": true,
29 | "powershell.codeFormatting.newLineAfterCloseBrace": true,
30 | "powershell.codeFormatting.whitespaceBeforeOpenBrace": true,
31 | "powershell.codeFormatting.whitespaceBeforeOpenParen": true,
32 | "powershell.codeFormatting.whitespaceAroundOperator": true,
33 | "powershell.codeFormatting.whitespaceAfterSeparator": true,
34 | "powershell.codeFormatting.ignoreOneLineBlock": true,
35 | "powershell.codeFormatting.alignPropertyValuePairs": true
36 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 |
4 | "windows": {
5 | "type": "shell",
6 | "options": {
7 | "shell": {
8 | "executable": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
9 | "args": [ "-NoProfile", "-Command" ]
10 | }
11 | },
12 |
13 | "command": "\"& { Invoke-psake build.psake.ps1 -taskList $args -notr }\"",
14 |
15 | "presentation": {
16 | "echo": false,
17 | "reveal": "always",
18 | "focus": false,
19 | "panel": "new"
20 | }
21 | },
22 |
23 | "tasks": [
24 | {
25 | "label": "Test",
26 | "group": {
27 | "kind": "test",
28 | "isDefault": true
29 | }
30 | },
31 | {
32 | "label": "Build",
33 | "group": {
34 | "kind": "build",
35 | "isDefault": true
36 | }
37 | }
38 | ]
39 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is mainly based on [Keep a Changelog](http://keepachangelog.com/)
6 | and this project adheres to [Semantic Versioning](http://semver.org/).
7 |
8 | ## 3.1.0 - 2019-09-16
9 |
10 | * Added: Auto-detect the config file if -Path was not specified
11 |
12 | ## 3.0.0 - 2019-02-18
13 |
14 | * Changed: Get the default config path from the PS call stack
15 | * Changed: The default configuration file type is now JSON (BREAKING CHANGE)
16 |
17 | ## 2.0.0 - 2016-12-12
18 |
19 | * Changed: Convert module to new deployment model
20 | * Changed: Rework code against high quality module guidelines by Microsoft
21 | * Changed: Remove positional parameters (BREAKING CHANGE)
22 |
23 | ## 1.0.3 - 2016-02-09
24 |
25 | * Added: Formats and types resources
26 |
27 | ## 1.0.2 - 2016-02-03
28 |
29 | * Fixed: Default path issue for Get-ScriptConfig
30 |
31 | ## 1.0.1 - 2016-01-29
32 |
33 | * Updated: File encoding to UTF8 w/o BOM
34 | * Updated: Demo and help
35 | * Added: Enhance parameters for Get-ScriptConfig function
36 |
37 | ## 1.0.0 - 2016-01-21
38 |
39 | * Added: Initial public release
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2019 Claudio Spizzi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Functions/Get-ScriptConfig.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Load a script configuration from a config file.
4 |
5 | .DESCRIPTION
6 | Load a script configuration from a config file. By default, the config
7 | file next to the script is loaded. So for the script MyScript.ps1, the
8 | config file MyScript.ps1.config will be loaded. The config file format
9 | will be detected by it's extension (.xml, .ini, .json). If the file
10 | format can't be detected, the default format (JSON) will be used.
11 |
12 | .EXAMPLE
13 | PS C:\> $config = Get-ScriptConfig
14 | Loads the default JSON formatted configuration file.
15 |
16 | .EXAMPLE
17 | PS C:\> $config = Get-ScriptConfig -Path 'C:\MyApp\global.config'
18 | Loads a custom configuration file, with default JSON format.
19 |
20 | .EXAMPLE
21 | PS C:\> $config = Get-ScriptConfig -Format XML
22 | Loads the default configuration file but in XML format.
23 |
24 | .NOTES
25 | Author : Claudio Spizzi
26 | License : MIT License
27 |
28 | .LINK
29 | https://github.com/claudiospizzi/ScriptConfig
30 | #>
31 | function Get-ScriptConfig
32 | {
33 | [CmdletBinding()]
34 | param
35 | (
36 | # Specify the path to the configuration file. By default, the current
37 | # script file path will be used with an appended '.config' extension.
38 | [Parameter(Mandatory = $false)]
39 | [AllowEmptyString()]
40 | [System.String]
41 | $Path,
42 |
43 | # Override the format detection and default value.
44 | [Parameter(Mandatory = $false)]
45 | [ValidateSet('XML', 'JSON', 'INI')]
46 | [System.String]
47 | $Format
48 | )
49 |
50 | # If the Path parameter was not specified, add a default value. If possible,
51 | # use the last script called this function. Else throw an exception.
52 | if (-not $PSBoundParameters.ContainsKey('Path') -or [System.String]::IsNullOrEmpty($Path))
53 | {
54 | $lastScriptPath = Get-PSCallStack | Select-Object -Skip 1 -First 1 -ExpandProperty 'ScriptName'
55 |
56 | if (-not [System.String]::IsNullOrEmpty($lastScriptPath))
57 | {
58 | if (Test-Path -Path "$lastScriptPath.ini")
59 | {
60 | $Path = "$lastScriptPath.ini"
61 | }
62 | elseif (Test-Path -Path "$lastScriptPath.json")
63 | {
64 | $Path = "$lastScriptPath.json"
65 | }
66 | elseif (Test-Path -Path "$lastScriptPath.xml")
67 | {
68 | $Path = "$lastScriptPath.xml"
69 | }
70 | else
71 | {
72 | $Path = "$lastScriptPath.config"
73 | }
74 | }
75 | else
76 | {
77 | throw "Configuration file not specified"
78 | }
79 | }
80 |
81 | # Now check, if the configuration file exists
82 | if (-not (Test-Path -Path $Path))
83 | {
84 | throw "Configuration file not found: $Path"
85 | }
86 |
87 | # If the Format parameter was not specified, try to detect by the file
88 | # extension or use the default format JSON.
89 | if (-not $PSBoundParameters.ContainsKey('Format'))
90 | {
91 | switch -Wildcard ($Path)
92 | {
93 | '*.ini' { $Format = 'INI' }
94 | '*.json' { $Format = 'JSON' }
95 | '*.xml' { $Format = 'XML' }
96 | default { $Format = 'JSON' }
97 | }
98 | }
99 |
100 | Write-Verbose "Load script configuration from file $Path with format $Format"
101 |
102 | # Load raw content, parse it later
103 | $content = Get-Content -Path $Path -ErrorAction Stop
104 |
105 | # Use custom functions to parse the files and return the config object
106 | switch ($Format)
107 | {
108 | 'XML' { ConvertFrom-ScriptConfigXml -Content $content }
109 | 'JSON' { ConvertFrom-ScriptConfigJson -Content $content }
110 | 'INI' { ConvertFrom-ScriptConfigIni -Content $content }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Helpers/ConvertFrom-ScriptConfigIni.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Convert the INI file content to a hashtable containing the
4 | configuration.
5 |
6 | .EXAMPLE
7 | PS C:\> Get-Content -Path 'config.ini' | ConvertFrom-ScriptConfigIni
8 | Use the pipeline input to parse the INI file content.
9 |
10 | .NOTES
11 | Author : Claudio Spizzi
12 | License : MIT License
13 |
14 | .LINK
15 | https://github.com/claudiospizzi/ScriptConfig
16 | #>
17 | function ConvertFrom-ScriptConfigIni
18 | {
19 | [CmdletBinding()]
20 | param
21 | (
22 | # An array of strings with the INI file content.
23 | [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
24 | [AllowEmptyString()]
25 | [System.String[]]
26 | $Content
27 | )
28 |
29 | $config = @{
30 | PSTypeName = 'ScriptConfig.Configuration'
31 | }
32 |
33 | try
34 | {
35 | # Iterating each line and parse the setting
36 | foreach ($line in $Content)
37 | {
38 | switch -Wildcard ($line)
39 | {
40 | # Comment
41 | ';*' {
42 | break
43 | }
44 |
45 | # Section
46 | '`[*`]*' {
47 | break
48 | }
49 |
50 | # Array
51 | '*`[`]=*'{
52 | $key = $line.Split('[]=', 4)[0]
53 | $value = $line.Split('[]=', 4)[3]
54 |
55 | if ($null -eq $config[$key])
56 | {
57 | $config[$key] = @()
58 | }
59 |
60 | $config[$key] += $value
61 |
62 | break
63 | }
64 |
65 | # Hashtable
66 | '*`[*`]=*' {
67 | $key = $line.Split('[]=', 4)[0]
68 | $hash = $line.Split('[]=', 4)[1]
69 | $value = $line.Split('[]=', 4)[3]
70 |
71 | if ($null -eq $config[$key])
72 | {
73 | $config[$key] = @{}
74 | }
75 |
76 | $config[$key][$hash] = $value
77 |
78 | break
79 | }
80 |
81 | # String, Integer or Boolean
82 | '*=*' {
83 | $key = $line.Split('=', 2)[0]
84 | $value = $line.Split('=', 2)[1]
85 |
86 | [Int32] $valueInt = $null
87 | if (([Int32]::TryParse($value, [ref] $valueInt)))
88 | {
89 | $config[$key] = $valueInt
90 | }
91 | else
92 | {
93 | if ('True'.Equals($value)) { $value = $true }
94 | if ('False'.Equals($value)) { $value = $false }
95 |
96 | $config[$key] = $value
97 | }
98 |
99 | break
100 | }
101 | }
102 | }
103 |
104 | [PSCustomObject] $config
105 | }
106 | catch
107 | {
108 | throw "The INI configuration file content was in an invalid format: $_"
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Helpers/ConvertFrom-ScriptConfigJson.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Convert the JSON file content to a hashtable containing the
4 | configuration.
5 |
6 | .EXAMPLE
7 | PS C:\> Get-Content -Path 'config.json' | ConvertFrom-ScriptConfigJson
8 | Use the pipeline input to parse the JSON file content.
9 |
10 | .NOTES
11 | Author : Claudio Spizzi
12 | License : MIT License
13 |
14 | .LINK
15 | https://github.com/claudiospizzi/ScriptConfig
16 | #>
17 | function ConvertFrom-ScriptConfigJson
18 | {
19 | [CmdletBinding()]
20 | param
21 | (
22 | # An array of strings with the JSON file content.
23 | [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
24 | [AllowEmptyString()]
25 | [System.String[]]
26 | $Content
27 | )
28 |
29 | $config = @{}
30 |
31 | try
32 | {
33 | # Join all lines into one string and parse the JSON content
34 | $jsonContent = ($Content -join '') | ConvertFrom-Json
35 |
36 | # Extract all propeties from the json content
37 | $jsonNodes = $jsonContent | Get-Member -MemberType NoteProperty
38 |
39 | foreach ($jsonNode in $jsonNodes)
40 | {
41 | $Key = $jsonNode.Name
42 | $value = $jsonContent.$Key
43 |
44 | if ($value -is [System.Management.Automation.PSCustomObject])
45 | {
46 | $config[$Key] = @{}
47 |
48 | foreach ($property in $value.PSObject.Properties)
49 | {
50 | $config[$Key][$property.Name] = $property.Value
51 | }
52 | }
53 | else
54 | {
55 | $config[$Key] = $value
56 | }
57 | }
58 |
59 | Write-Output $config
60 | }
61 | catch
62 | {
63 | throw "The JSON configuration file content was in an invalid format: $_"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Helpers/ConvertFrom-ScriptConfigXml.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Convert the XML file content to a hashtable containing the
4 | configuration.
5 |
6 | .EXAMPLE
7 | PS C:\> Get-Content -Path 'config.xml' | ConvertFrom-ScriptConfigXml
8 | Use the pipeline input to parse the XML file content.
9 |
10 | .NOTES
11 | Author : Claudio Spizzi
12 | License : MIT License
13 |
14 | .LINK
15 | https://github.com/claudiospizzi/ScriptConfig
16 | #>
17 | function ConvertFrom-ScriptConfigXml
18 | {
19 | [CmdletBinding()]
20 | param
21 | (
22 | # An array of strings with the XML file content.
23 | [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
24 | [AllowEmptyString()]
25 | [System.String[]]
26 | $Content
27 | )
28 |
29 | $config = @{}
30 |
31 | try
32 | {
33 | # Try to cast the content into an XmlDocument
34 | $xmlContent = [Xml] $Content
35 |
36 | # Extract all setting objects
37 | $settings = $xmlContent.Configuration.Settings.Setting
38 |
39 | foreach ($setting in $settings)
40 | {
41 | switch ($setting.Type)
42 | {
43 | # String
44 | 'string' {
45 | $config[$setting.Key] = $setting.Value
46 | }
47 |
48 | # Integer
49 | 'integer' {
50 | $config[$setting.Key] = [Int32]::Parse($setting.Value)
51 | }
52 |
53 | # Boolean
54 | 'boolean' {
55 | $config[$setting.Key] = 'True' -eq $setting.Value
56 | }
57 |
58 | # Array
59 | 'array' {
60 | $config[$setting.Key] = @()
61 | foreach ($item in $setting.Item)
62 | {
63 | $config[$setting.Key] += $item.Value
64 | }
65 | }
66 |
67 | # Hashtable
68 | 'hashtable' {
69 | $config[$setting.Key] = @{}
70 | foreach ($item in $setting.Item)
71 | {
72 | $config[$setting.Key][$item.Key] = $item.Value
73 | }
74 | }
75 | }
76 | }
77 |
78 | Write-Output $config
79 | }
80 | catch
81 | {
82 | throw "The XML configuration file content was in an invalid format: $_"
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Resources/ScriptConfig.Formats.ps1xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Resources/ScriptConfig.Types.ps1xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/ScriptConfig.psd1:
--------------------------------------------------------------------------------
1 | @{
2 | # Script module or binary module file associated with this manifest.
3 | RootModule = 'ScriptConfig.psm1'
4 |
5 | # Version number of this module.
6 | ModuleVersion = '3.1.0'
7 |
8 | # Supported PSEditions
9 | # CompatiblePSEditions = @()
10 |
11 | # ID used to uniquely identify this module
12 | GUID = '0464897C-2F37-489F-93B2-7F6B9B582761'
13 |
14 | # Author of this module
15 | Author = 'Claudio Spizzi'
16 |
17 | # Company or vendor of this module
18 | # CompanyName = ''
19 |
20 | # Copyright statement for this module
21 | Copyright = 'Copyright (c) 2019 by Claudio Spizzi. Licensed under MIT license.'
22 |
23 | # Description of the functionality provided by this module
24 | Description = 'PowerShell Module to handle configuration files for PowerShell controller scripts.'
25 |
26 | # Minimum version of the Windows PowerShell engine required by this module
27 | PowerShellVersion = '3.0'
28 |
29 | # Name of the Windows PowerShell host required by this module
30 | # PowerShellHostName = ''
31 |
32 | # Minimum version of the Windows PowerShell host required by this module
33 | # PowerShellHostVersion = ''
34 |
35 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
36 | # DotNetFrameworkVersion = ''
37 |
38 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
39 | # CLRVersion = ''
40 |
41 | # Processor architecture (None, X86, Amd64) required by this module
42 | # ProcessorArchitecture = ''
43 |
44 | # Modules that must be imported into the global environment prior to importing this module
45 | # RequiredModules = @()
46 |
47 | # Assemblies that must be loaded prior to importing this module
48 | # RequiredAssemblies = @()
49 |
50 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
51 | # ScriptsToProcess = @()
52 |
53 | # Type files (.ps1xml) to be loaded when importing this module
54 | TypesToProcess = @(
55 | 'Resources\ScriptConfig.Types.ps1xml'
56 | )
57 |
58 | # Format files (.ps1xml) to be loaded when importing this module
59 | FormatsToProcess = @(
60 | 'Resources\ScriptConfig.Formats.ps1xml'
61 | )
62 |
63 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
64 | # NestedModules = @()
65 |
66 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
67 | FunctionsToExport = @(
68 | 'Get-ScriptConfig'
69 | )
70 |
71 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
72 | # CmdletsToExport = @()
73 |
74 | # Variables to export from this module
75 | # VariablesToExport = @()
76 |
77 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
78 | # AliasesToExport = @()
79 |
80 | # DSC resources to export from this module
81 | # DscResourcesToExport = @()
82 |
83 | # List of all modules packaged with this module
84 | # ModuleList = @()
85 |
86 | # List of all files packaged with this module
87 | # FileList = @()
88 |
89 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
90 | PrivateData = @{
91 |
92 | PSData = @{
93 |
94 | # Tags applied to this module. These help with module discovery in online galleries.
95 | Tags = @('PSModule', 'Config', 'Configuration', 'Script', 'Controller')
96 |
97 | # A URL to the license for this module.
98 | LicenseUri = 'https://raw.githubusercontent.com/claudiospizzi/ScriptConfig/master/LICENSE'
99 |
100 | # A URL to the main website for this project.
101 | ProjectUri = 'https://github.com/claudiospizzi/ScriptConfig'
102 |
103 | # A URL to an icon representing this module.
104 | # IconUri = ''
105 |
106 | # ReleaseNotes of this module
107 | # ReleaseNotes = ''
108 |
109 | } # End of PSData hashtable
110 |
111 | } # End of PrivateData hashtable
112 |
113 | # HelpInfo URI of this module
114 | # HelpInfoURI = ''
115 |
116 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
117 | # DefaultCommandPrefix = ''
118 | }
119 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/ScriptConfig.psm1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Root module file.
4 |
5 | .DESCRIPTION
6 | The root module file loads all classes, helpers and functions into the
7 | module context.
8 | #>
9 |
10 |
11 | ## Module loader
12 |
13 | # Get and dot source all classes (internal)
14 | Split-Path -Path $PSCommandPath |
15 | Get-ChildItem -Filter 'Classes' -Directory |
16 | Get-ChildItem -Include '*.ps1' -File -Recurse |
17 | ForEach-Object { . $_.FullName }
18 |
19 | # Get and dot source all helper functions (internal)
20 | Split-Path -Path $PSCommandPath |
21 | Get-ChildItem -Filter 'Helpers' -Directory |
22 | Get-ChildItem -Include '*.ps1' -File -Recurse |
23 | ForEach-Object { . $_.FullName }
24 |
25 | # Get and dot source all external functions (public)
26 | Split-Path -Path $PSCommandPath |
27 | Get-ChildItem -Filter 'Functions' -Directory |
28 | Get-ChildItem -Include '*.ps1' -File -Recurse |
29 | ForEach-Object { . $_.FullName }
30 |
31 |
32 | ## Module configuration
33 |
34 | # Module path
35 | New-Variable -Name 'ModulePath' -Value $PSScriptRoot
36 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Tests/Integration/IniConfigDemo.ps1:
--------------------------------------------------------------------------------
1 |
2 | # Load the default configuration file in INI format
3 | $Config = Get-ScriptConfig -Format 'INI'
4 |
5 | # Access the configuration settings
6 | Write-Verbose "String :" $Config.MyString
7 | Write-Verbose "Integer Positive :" $Config.MyIntegerPositive
8 | Write-Verbose "Integer Negative :" $Config.MyIntegerNegative
9 | Write-Verbose "Boolean True :" $Config.MyBooleanTrue
10 | Write-Verbose "Boolean False :" $Config.MyBooleanFalse
11 | Write-Verbose "Array :" $Config.MyArray
12 | Write-Verbose "Array Item :" $Config.MyArray[0]
13 | Write-Verbose "Hashtable :" $Config.MyHashtable
14 | Write-Verbose "Hashtable Item :" $Config.MyHashtable['Hello']
15 | Write-Verbose "Hashtable Keys :" $Config.MyHashtable.Keys
16 | Write-Verbose "Hashtable Values :" $Config.MyHashtable.Values
17 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Tests/Integration/IniConfigDemo.ps1.config:
--------------------------------------------------------------------------------
1 | MyString=This is a test INI config file!
2 | MyIntegerPositive=42
3 | MyIntegerNegative=-153
4 | MyBooleanTrue=True
5 | MyBooleanFalse=False
6 | MyArray[]=Lorem
7 | MyArray[]=Ipsum
8 | MyHashtable[Foo]=Bar
9 | MyHashtable[Hello]=World
10 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Tests/Integration/JsonConfigDemo.ps1:
--------------------------------------------------------------------------------
1 |
2 | # Load the default configuration file in JSON format
3 | $Config = Get-ScriptConfig -Format 'JSON'
4 |
5 | # Access the configuration settings
6 | Write-Verbose "String :" $Config.MyString
7 | Write-Verbose "Integer Positive :" $Config.MyIntegerPositive
8 | Write-Verbose "Integer Negative :" $Config.MyIntegerNegative
9 | Write-Verbose "Boolean True :" $Config.MyBooleanTrue
10 | Write-Verbose "Boolean False :" $Config.MyBooleanFalse
11 | Write-Verbose "Array :" $Config.MyArray
12 | Write-Verbose "Array Item :" $Config.MyArray[0]
13 | Write-Verbose "Hashtable :" $Config.MyHashtable
14 | Write-Verbose "Hashtable Item :" $Config.MyHashtable['Hello']
15 | Write-Verbose "Hashtable Keys :" $Config.MyHashtable.Keys
16 | Write-Verbose "Hashtable Values :" $Config.MyHashtable.Values
17 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Tests/Integration/JsonConfigDemo.ps1.config:
--------------------------------------------------------------------------------
1 | {
2 | "MyString": "This is a test JSON config file!",
3 | "MyIntegerPositive": 42,
4 | "MyIntegerNegative": -153,
5 | "MyBooleanTrue": true,
6 | "MyBooleanFalse": false,
7 | "MyArray": [
8 | "Lorem",
9 | "Ipsum"
10 | ],
11 | "MyHashtable": {
12 | "Foo": "Bar",
13 | "Hello": "World"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Tests/Integration/XmlConfigDemo.ps1:
--------------------------------------------------------------------------------
1 |
2 | # Load the default configuration file in XML format
3 | $Config = Get-ScriptConfig -Format 'XML'
4 |
5 | # Access the configuration settings
6 | Write-Verbose "String :" $Config.MyString
7 | Write-Verbose "Integer Positive :" $Config.MyIntegerPositive
8 | Write-Verbose "Integer Negative :" $Config.MyIntegerNegative
9 | Write-Verbose "Boolean True :" $Config.MyBooleanTrue
10 | Write-Verbose "Boolean False :" $Config.MyBooleanFalse
11 | Write-Verbose "Array :" $Config.MyArray
12 | Write-Verbose "Array Item :" $Config.MyArray[0]
13 | Write-Verbose "Hashtable :" $Config.MyHashtable
14 | Write-Verbose "Hashtable Item :" $Config.MyHashtable['Hello']
15 | Write-Verbose "Hashtable Keys :" $Config.MyHashtable.Keys
16 | Write-Verbose "Hashtable Values :" $Config.MyHashtable.Values
17 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Tests/Integration/XmlConfigDemo.ps1.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Tests/Unit/ConvertFrom-ScriptConfigIni.Tests.ps1:
--------------------------------------------------------------------------------
1 |
2 | $modulePath = Resolve-Path -Path "$PSScriptRoot\..\..\.." | Select-Object -ExpandProperty Path
3 | $moduleName = Resolve-Path -Path "$PSScriptRoot\..\.." | Get-Item | Select-Object -ExpandProperty BaseName
4 |
5 | Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue
6 | Import-Module -Name "$modulePath\$moduleName" -Force
7 |
8 | InModuleScope ScriptConfig {
9 |
10 | Describe 'ConvertFrom-ScriptConfigIni' {
11 |
12 | It 'should be able to convert the example config file' {
13 |
14 | # Arrange
15 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.ini"
16 |
17 | # Act
18 | $config = ConvertFrom-ScriptConfigIni -Content $content
19 |
20 | # Assert
21 | $config | Should -Not -BeNullOrEmpty
22 | }
23 |
24 | It 'shloud be able to parse a string' {
25 |
26 | # Arrange
27 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.ini"
28 |
29 | # Act
30 | $config = ConvertFrom-ScriptConfigIni -Content $content
31 |
32 | # Assert
33 | $config.MyString | Should -Be 'This is a test INI config file!'
34 | $config.MyString.GetType() | Should -Be ([System.String])
35 | }
36 |
37 | It 'shloud be able to parse an integer' {
38 |
39 | # Arrange
40 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.ini"
41 |
42 | # Act
43 | $config = ConvertFrom-ScriptConfigIni -Content $content
44 |
45 | # Assert
46 | $config.MyIntegerPositive | Should -Be 42
47 | $config.MyIntegerPositive.GetType() | Should -Be ([System.Int32])
48 | $config.MyIntegerNegative | Should -Be -153
49 | $config.MyIntegerNegative.GetType() | Should -Be ([System.Int32])
50 | }
51 |
52 | It 'shloud be able to parse an boolean' {
53 |
54 | # Arrange
55 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.ini"
56 |
57 | # Act
58 | $config = ConvertFrom-ScriptConfigIni -Content $content
59 |
60 | # Assert
61 | $config.MyBooleanTrue | Should -BeTrue
62 | $config.MyBooleanTrue.GetType() | Should -Be ([System.Boolean])
63 | $config.MyBooleanFalse | Should -BeFalse
64 | $config.MyBooleanFalse.GetType() | Should -Be ([System.Boolean])
65 | }
66 |
67 | It 'shloud be able to parse an array' {
68 |
69 | # Arrange
70 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.ini"
71 | $expectedArray = @( 'Lorem', 'Ipsum' )
72 |
73 | # Act
74 | $config = ConvertFrom-ScriptConfigIni -Content $content
75 |
76 | # Assert
77 | $config.MyArray | Should -Not -BeNullOrEmpty
78 | $config.MyArray | Should -Be $expectedArray
79 | $config.MyArray.GetType() | Should -Be ([System.Object[]])
80 | }
81 |
82 | It 'shloud be able to parse an hashtable' {
83 |
84 | # Arrange
85 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.ini"
86 | $expectedHashtable = @{ Foo = 'Bar'; Hello = 'World' }
87 |
88 | # Act
89 | $config = ConvertFrom-ScriptConfigIni -Content $content
90 |
91 | # Assert
92 | $config.MyHashtable | Should -Not -BeNullOrEmpty
93 | $config.MyHashtable.Keys -as [string[]] | Should -Be ($expectedHashtable.Keys -as [string[]])
94 | $config.MyHashtable.Values -as [string[]] | Should -Be ($expectedHashtable.Values -as [string[]])
95 | $config.MyHashtable.GetType() | Should -Be ([System.Collections.Hashtable])
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Tests/Unit/ConvertFrom-ScriptConfigJson.Tests.ps1:
--------------------------------------------------------------------------------
1 |
2 | $modulePath = Resolve-Path -Path "$PSScriptRoot\..\..\.." | Select-Object -ExpandProperty Path
3 | $moduleName = Resolve-Path -Path "$PSScriptRoot\..\.." | Get-Item | Select-Object -ExpandProperty BaseName
4 |
5 | Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue
6 | Import-Module -Name "$modulePath\$moduleName" -Force
7 |
8 | InModuleScope ScriptConfig {
9 |
10 | Describe 'ConvertFrom-ScriptConfigJson' {
11 |
12 | It 'should be able to convert the example config file' {
13 |
14 | # Arrange
15 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.json"
16 |
17 | # Act
18 | $config = ConvertFrom-ScriptConfigJson -Content $content
19 |
20 | # Assert
21 | $config | Should -Not -BeNullOrEmpty
22 | }
23 |
24 | It 'shloud be able to parse a string' {
25 |
26 | # Arrange
27 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.json"
28 |
29 | # Act
30 | $config = ConvertFrom-ScriptConfigJson -Content $content
31 |
32 | # Assert
33 | $config.MyString | Should -Be 'This is a test JSON config file!'
34 | $config.MyString.GetType() | Should -Be ([System.String])
35 | }
36 |
37 | It 'shloud be able to parse an integer' {
38 |
39 | # Arrange
40 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.json"
41 |
42 | # Act
43 | $config = ConvertFrom-ScriptConfigJson -Content $content
44 |
45 | # Assert
46 | $config.MyIntegerPositive | Should -Be 42
47 | $config.MyIntegerPositive.GetType() | Should -Be ([System.Int32])
48 | $config.MyIntegerNegative | Should -Be -153
49 | $config.MyIntegerNegative.GetType() | Should -Be ([System.Int32])
50 | }
51 |
52 | It 'shloud be able to parse an boolean' {
53 |
54 | # Arrange
55 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.json"
56 |
57 | # Act
58 | $config = ConvertFrom-ScriptConfigJson -Content $content
59 |
60 | # Assert
61 | $config.MyBooleanTrue | Should -BeTrue
62 | $config.MyBooleanTrue.GetType() | Should -Be ([System.Boolean])
63 | $config.MyBooleanFalse | Should -BeFalse
64 | $config.MyBooleanFalse.GetType() | Should -Be ([System.Boolean])
65 | }
66 |
67 | It 'shloud be able to parse an array' {
68 |
69 | # Arrange
70 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.json"
71 | $expectedArray = @( 'Lorem', 'Ipsum' )
72 |
73 | # Act
74 | $config = ConvertFrom-ScriptConfigJson -Content $content
75 |
76 | # Assert
77 | $config.MyArray | Should -Not -BeNullOrEmpty
78 | $config.MyArray | Should -Be $expectedArray
79 | $config.MyArray.GetType() | Should -Be ([System.Object[]])
80 | }
81 |
82 | It 'shloud be able to parse an hashtable' {
83 |
84 | # Arrange
85 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.json"
86 | $expectedHashtable = @{ Foo = 'Bar'; Hello = 'World' }
87 |
88 | # Act
89 | $config = ConvertFrom-ScriptConfigJson -Content $content
90 |
91 | # Assert
92 | $config.MyHashtable | Should -Not -BeNullOrEmpty
93 | $config.MyHashtable.Keys -as [string[]] | Should -Be ($expectedHashtable.Keys -as [string[]])
94 | $config.MyHashtable.Values -as [string[]] | Should -Be ($expectedHashtable.Values -as [string[]])
95 | $config.MyHashtable.GetType() | Should -Be ([System.Collections.Hashtable])
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Tests/Unit/ConvertFrom-ScriptConfigXml.Tests.ps1:
--------------------------------------------------------------------------------
1 |
2 | $modulePath = Resolve-Path -Path "$PSScriptRoot\..\..\.." | Select-Object -ExpandProperty Path
3 | $moduleName = Resolve-Path -Path "$PSScriptRoot\..\.." | Get-Item | Select-Object -ExpandProperty BaseName
4 |
5 | Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue
6 | Import-Module -Name "$modulePath\$moduleName" -Force
7 |
8 | InModuleScope ScriptConfig {
9 |
10 | Describe 'ConvertFrom-ScriptConfigXml' {
11 |
12 | It 'should be able to convert the example config file' {
13 |
14 | # Arrange
15 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.xml"
16 |
17 | # Act
18 | $config = ConvertFrom-ScriptConfigXml -Content $content
19 |
20 | # Assert
21 | $config | Should -Not -BeNullOrEmpty
22 | }
23 |
24 | It 'shloud be able to parse a string' {
25 |
26 | # Arrange
27 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.xml"
28 |
29 | # Act
30 | $config = ConvertFrom-ScriptConfigXml -Content $content
31 |
32 | # Assert
33 | $config.MyString | Should -Be 'This is a test XML config file!'
34 | $config.MyString.GetType() | Should -Be ([System.String])
35 | }
36 |
37 | It 'shloud be able to parse an integer' {
38 |
39 | # Arrange
40 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.xml"
41 |
42 | # Act
43 | $config = ConvertFrom-ScriptConfigXml -Content $content
44 |
45 | # Assert
46 | $config.MyIntegerPositive | Should -Be 42
47 | $config.MyIntegerPositive.GetType() | Should -Be ([System.Int32])
48 | $config.MyIntegerNegative | Should -Be -153
49 | $config.MyIntegerNegative.GetType() | Should -Be ([System.Int32])
50 | }
51 |
52 | It 'shloud be able to parse an boolean' {
53 |
54 | # Arrange
55 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.xml"
56 |
57 | # Act
58 | $config = ConvertFrom-ScriptConfigXml -Content $content
59 |
60 | # Assert
61 | $config.MyBooleanTrue | Should -BeTrue
62 | $config.MyBooleanTrue.GetType() | Should -Be ([System.Boolean])
63 | $config.MyBooleanFalse | Should -BeFalse
64 | $config.MyBooleanFalse.GetType() | Should -Be ([System.Boolean])
65 | }
66 |
67 | It 'shloud be able to parse an array' {
68 |
69 | # Arrange
70 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.xml"
71 | $expectedArray = @( 'Lorem', 'Ipsum' )
72 |
73 | # Act
74 | $config = ConvertFrom-ScriptConfigXml -Content $content
75 |
76 | # Assert
77 | $config.MyArray | Should -Not -BeNullOrEmpty
78 | $config.MyArray | Should -Be $expectedArray
79 | $config.MyArray.GetType() | Should -Be ([System.Object[]])
80 | }
81 |
82 | It 'shloud be able to parse an hashtable' {
83 |
84 | # Arrange
85 | $content = Get-Content -Path "$PSScriptRoot\TestData\config.xml"
86 | $expectedHashtable = @{ Foo = 'Bar'; Hello = 'World' }
87 |
88 | # Act
89 | $config = ConvertFrom-ScriptConfigXml -Content $content
90 |
91 | # Assert
92 | $config.MyHashtable | Should -Not -BeNullOrEmpty
93 | $config.MyHashtable.Keys -as [string[]] | Should -Be ($expectedHashtable.Keys -as [string[]])
94 | $config.MyHashtable.Values -as [string[]] | Should -Be ($expectedHashtable.Values -as [string[]])
95 | $config.MyHashtable.GetType() | Should -Be ([System.Collections.Hashtable])
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Tests/Unit/Get-ScriptConfig.Tests.ps1:
--------------------------------------------------------------------------------
1 |
2 | $modulePath = Resolve-Path -Path "$PSScriptRoot\..\..\.." | Select-Object -ExpandProperty Path
3 | $moduleName = Resolve-Path -Path "$PSScriptRoot\..\.." | Get-Item | Select-Object -ExpandProperty BaseName
4 |
5 | Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue
6 | Import-Module -Name "$modulePath\$moduleName" -Force
7 |
8 | Describe 'Get-ScriptConfig' {
9 |
10 | Context 'Test Data' {
11 |
12 | It 'shloud be able to load a valid INI configuration file' {
13 |
14 | # Arrange
15 | $expectedArray = @( 'Lorem', 'Ipsum' )
16 | $expectedHashtable = @{ Foo = 'Bar'; Hello = 'World' }
17 |
18 | # Act
19 | $config = Get-ScriptConfig -Path "$PSScriptRoot\TestData\config.ini" -Format 'INI'
20 |
21 | # Assert
22 | $config | Should -Not -BeNullOrEmpty
23 | $config.MyString | Should -Be 'This is a test INI config file!'
24 | $config.MyIntegerPositive | Should -Be 42
25 | $config.MyIntegerNegative | Should -Be -153
26 | $config.MyBooleanTrue | Should -BeTrue
27 | $config.MyBooleanFalse | Should -BeFalse
28 | $config.MyArray | Should -Be $expectedArray
29 | $config.MyHashtable.Keys -as [string[]] | Should -Be ($expectedHashtable.Keys -as [string[]])
30 | $config.MyHashtable.Values -as [string[]] | Should -Be ($expectedHashtable.Values -as [string[]])
31 | }
32 |
33 | It 'shloud be able to load a valid JSON configuration file' {
34 |
35 | # Arrange
36 | $expectedArray = @( 'Lorem', 'Ipsum' )
37 | $expectedHashtable = @{ Foo = 'Bar'; Hello = 'World' }
38 |
39 | # Act
40 | $config = Get-ScriptConfig -Path "$PSScriptRoot\TestData\config.json" -Format 'JSON'
41 |
42 | # Assert
43 | $config | Should -Not -BeNullOrEmpty
44 | $config.MyString | Should -Be 'This is a test JSON config file!'
45 | $config.MyIntegerPositive | Should -Be 42
46 | $config.MyIntegerNegative | Should -Be -153
47 | $config.MyBooleanTrue | Should -BeTrue
48 | $config.MyBooleanFalse | Should -BeFalse
49 | $config.MyArray | Should -Be $expectedArray
50 | $config.MyHashtable.Keys -as [string[]] | Should -Be ($expectedHashtable.Keys -as [string[]])
51 | $config.MyHashtable.Values -as [string[]] | Should -Be ($expectedHashtable.Values -as [string[]])
52 | }
53 |
54 | It 'shloud be able to load a valid XML configuration file' {
55 |
56 | # Arrange
57 | $expectedArray = @( 'Lorem', 'Ipsum' )
58 | $expectedHashtable = @{ Foo = 'Bar'; Hello = 'World' }
59 |
60 | # Act
61 | $config = Get-ScriptConfig -Path "$PSScriptRoot\TestData\config.xml" -Format 'XML'
62 |
63 | # Assert
64 | $config | Should -Not -BeNullOrEmpty
65 | $config.MyString | Should -Be 'This is a test XML config file!'
66 | $config.MyIntegerPositive | Should -Be 42
67 | $config.MyIntegerNegative | Should -Be -153
68 | $config.MyBooleanTrue | Should -BeTrue
69 | $config.MyBooleanFalse | Should -BeFalse
70 | $config.MyArray | Should -Be $expectedArray
71 | $config.MyHashtable.Keys -as [string[]] | Should -Be ($expectedHashtable.Keys -as [string[]])
72 | $config.MyHashtable.Values -as [string[]] | Should -Be ($expectedHashtable.Values -as [string[]])
73 | }
74 | }
75 |
76 | Context 'Mocked Convert Script' {
77 |
78 | Mock ConvertFrom-ScriptConfigIni { return @{} } -ModuleName 'ScriptConfig' -Verifiable
79 | Mock ConvertFrom-ScriptConfigJson { return @{} } -ModuleName 'ScriptConfig' -Verifiable
80 | Mock ConvertFrom-ScriptConfigXml { return @{} } -ModuleName 'ScriptConfig' -Verifiable
81 |
82 | It 'should call the INI function if a .ini file is specified' {
83 |
84 | # Act
85 | Get-ScriptConfig -Path "$PSScriptRoot\TestData\config.ini" | Out-Null
86 |
87 | # Assert
88 | Assert-MockCalled 'ConvertFrom-ScriptConfigIni' -ModuleName 'ScriptConfig' -Scope 'It' -Times 1 -Exactly
89 | }
90 |
91 | It 'should call the JSON function if a .json file is specified' {
92 |
93 | # Act
94 | Get-ScriptConfig -Path "$PSScriptRoot\TestData\config.json" | Out-Null
95 |
96 | # Assert
97 | Assert-MockCalled 'ConvertFrom-ScriptConfigJson' -ModuleName 'ScriptConfig' -Scope 'It' -Times 1 -Exactly
98 | }
99 |
100 | It 'should call the XML function if a .xml file is specified' {
101 |
102 | # Act
103 | Get-ScriptConfig -Path "$PSScriptRoot\TestData\config.xml" | Out-Null
104 |
105 | # Assert
106 | Assert-MockCalled 'ConvertFrom-ScriptConfigXml' -ModuleName 'ScriptConfig' -Scope 'It' -Times 1 -Exactly
107 | }
108 |
109 | It 'should call the INI function if INI format is specified' {
110 |
111 | # Act
112 | Get-ScriptConfig -Path "$PSScriptRoot\TestData\config.ini" -Format 'INI' | Out-Null
113 |
114 | # Assert
115 | Assert-MockCalled 'ConvertFrom-ScriptConfigIni' -ModuleName 'ScriptConfig' -Scope 'It' -Times 1 -Exactly
116 | }
117 |
118 | It 'should call the JSON function if JSON format is specified' {
119 |
120 | # Act
121 | Get-ScriptConfig -Path "$PSScriptRoot\TestData\config.json" -Format 'JSON' | Out-Null
122 |
123 | # Assert
124 | Assert-MockCalled 'ConvertFrom-ScriptConfigJson' -ModuleName 'ScriptConfig' -Scope 'It' -Times 1 -Exactly
125 | }
126 |
127 | It 'should call the XML function if XML format is specified' {
128 |
129 | # Act
130 | Get-ScriptConfig -Path "$PSScriptRoot\TestData\config.xml" -Format 'XML' | Out-Null
131 |
132 | # Assert
133 | Assert-MockCalled 'ConvertFrom-ScriptConfigXml' -ModuleName 'ScriptConfig' -Scope 'It' -Times 1 -Exactly
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Tests/Unit/TestData/config.ini:
--------------------------------------------------------------------------------
1 | MyString=This is a test INI config file!
2 | MyIntegerPositive=42
3 | MyIntegerNegative=-153
4 | MyBooleanTrue=True
5 | MyBooleanFalse=False
6 | MyArray[]=Lorem
7 | MyArray[]=Ipsum
8 | MyHashtable[Foo]=Bar
9 | MyHashtable[Hello]=World
10 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Tests/Unit/TestData/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "MyString": "This is a test JSON config file!",
3 | "MyIntegerPositive": 42,
4 | "MyIntegerNegative": -153,
5 | "MyBooleanTrue": true,
6 | "MyBooleanFalse": false,
7 | "MyArray": [
8 | "Lorem",
9 | "Ipsum"
10 | ],
11 | "MyHashtable": {
12 | "Foo": "Bar",
13 | "Hello": "World"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/Tests/Unit/TestData/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Modules/ScriptConfig/en-US/about_ScriptConfig.help.txt:
--------------------------------------------------------------------------------
1 |
2 | TOPIC
3 | about_ScriptConfig
4 |
5 | SHORT DESCRIPTION
6 | PowerShell Module to handle configuration files for PowerShell controller
7 | scripts.
8 |
9 | LONG DESCRIPTION
10 | To get more information about this module, check the open source GitHub
11 | repository readme or the cmdlet based help for the module functions:
12 |
13 | LINK
14 | https://github.com/claudiospizzi/ScriptConfig
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.powershellgallery.com/packages/ScriptConfig)
2 | [](https://github.com/claudiospizzi/ScriptConfig/releases)
3 | [](https://ci.appveyor.com/project/claudiospizzi/ScriptConfig/branch/master)
4 | [](https://ci.appveyor.com/project/claudiospizzi/ScriptConfig/branch/dev)
5 |
6 |
7 | # ScriptConfig PowerShell Module
8 |
9 | PowerShell Module to handle configuration files for PowerShell controller
10 | scripts.
11 |
12 |
13 | ## Introduction
14 |
15 | With the ScriptConfig module, configuration data can be loaded into a PowerShell
16 | controller script from a dedicated config file. Thanks to the module, it is no
17 | longer necessary to hard-code or paramter-pass the configuration data.
18 | Especially useful for scripts, which run unattended. The module support `XML`,
19 | `JSON` and `INI` formatted config files. Works great in cooperation with the
20 | [ScriptLogger] module to improve controller scripts.
21 |
22 |
23 | ## Features
24 |
25 | * **Get-ScriptConfig**
26 | Loads the configuration from a config file. The path and format can be
27 | specified with parameters.
28 |
29 |
30 | ### Example
31 |
32 | In this example, a `JSON` configuration file is loaded into the script. You will
33 | find an example configuration file in the after next chapter. As soon as you
34 | have imported the configuration data, you will be able to use the returned
35 | object to access the settings with the `.` notation. All available formats for
36 | the config files are listed in the next chapter.
37 |
38 | ```powershell
39 | # Load the configuration from a config file
40 | $config = Get-ScriptConfig -Path 'C:\Scripts\config.json' -Format JSON
41 |
42 | # Access the configuration data from the config variable
43 | Write-Host "Config Data:" $config.MyString
44 | ```
45 |
46 |
47 | ### Supported Types
48 |
49 | The cmdlet supports multiple types. Depending on the used format, the types have
50 | to be specified differently inside the config file.
51 |
52 | | Type | Description |
53 | | ------------- | ---------------------------------------------------------------------------------------- |
54 | | `String` | A settings is stored as a string by default. |
55 | | `Integer` | If the setting value is a valid integer, it will be casted into an integer. |
56 | | `Boolean` | By specifying the key words `True` or `False`, it will be casted to a boolean type. |
57 | | `Array` | An array of strings can be specified. |
58 | | `Hashtable` | A dictionary of key-value-pairs is supported too. The key and values will be a string. |
59 |
60 |
61 | ### XML Format
62 |
63 | Inside an `XML` formatted config file, it's mandatory to specify the type, the key and the value of each setting. Thanks to this, the config file is type-safe.
64 |
65 | ```xml
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | ```
85 |
86 |
87 | ### JSON Format
88 |
89 | With the `JSON` format, it's easy to specify the configuraiton data with less overhead. Because of the special notation, the `JSON` format is also type-safe.
90 |
91 | ```json
92 | {
93 | "MyString": "This is a test JSON config file!",
94 | "MyIntegerPositive": 42,
95 | "MyIntegerNegative": -153,
96 | "MyBooleanTrue": true,
97 | "MyBooleanFalse": false,
98 | "MyArray": [
99 | "Lorem",
100 | "Ipsum"
101 | ],
102 | "MyHashtable": {
103 | "Foo": "Bar",
104 | "Hello": "World"
105 | }
106 | }
107 | ```
108 |
109 |
110 | ### INI Format
111 |
112 | The last supported format is the `INI` file. To support all types, it is necessary to extend the basic `INI` file convention with `[` and `]`, to specify arrays and hash tables. Sections are not supported and will be ignored.
113 |
114 | ```ini
115 | MyString=This is a test INI config file!
116 | MyIntegerPositive=42
117 | MyIntegerNegative=-153
118 | MyBooleanTrue=True
119 | MyBooleanFalse=False
120 | MyArray[]=Lorem
121 | MyArray[]=Ipsum
122 | MyHashtable[Foo]=Bar
123 | MyHashtable[Hello]=World
124 | ```
125 |
126 |
127 | ## Versions
128 |
129 | Please find all versions in the [GitHub Releases] section and the release notes
130 | in the [CHANGELOG.md] file.
131 |
132 |
133 | ## Installation
134 |
135 | Use the following command to install the module from the [PowerShell Gallery],
136 | if the PackageManagement and PowerShellGet modules are available:
137 |
138 | ```powershell
139 | # Download and install the module
140 | Install-Module -Name 'ScriptConfig'
141 | ```
142 |
143 | Alternatively, download the latest release from GitHub and install the module
144 | manually on your local system:
145 |
146 | 1. Download the latest release from GitHub as a ZIP file: [GitHub Releases]
147 | 2. Extract the module and install it: [Installing a PowerShell Module]
148 |
149 |
150 | ## Requirements
151 |
152 | The following minimum requirements are necessary to use this module:
153 |
154 | * Windows PowerShell 3.0
155 | * Windows Server 2008 R2 / Windows 7
156 |
157 |
158 | ## Contribute
159 |
160 | Please feel free to contribute by opening new issues or providing pull requests.
161 | For the best development experience, open this project as a folder in Visual
162 | Studio Code and ensure that the PowerShell extension is installed.
163 |
164 | * [Visual Studio Code] with the [PowerShell Extension]
165 | * [Pester], [PSScriptAnalyzer] and [psake] PowerShell Modules
166 |
167 |
168 |
169 | [ScriptLogger]: https://github.com/claudiospizzi/ScriptLogger
170 |
171 | [PowerShell Gallery]: https://www.powershellgallery.com/packages/ScriptConfig
172 | [GitHub Releases]: https://github.com/claudiospizzi/ScriptConfig/releases
173 | [Installing a PowerShell Module]: https://msdn.microsoft.com/en-us/library/dd878350
174 |
175 | [CHANGELOG.md]: CHANGELOG.md
176 |
177 | [Visual Studio Code]: https://code.visualstudio.com/
178 | [PowerShell Extension]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.PowerShell
179 | [Pester]: https://www.powershellgallery.com/packages/Pester
180 | [PSScriptAnalyzer]: https://www.powershellgallery.com/packages/PSScriptAnalyzer
181 | [psake]: https://www.powershellgallery.com/packages/psake
182 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 |
2 | # PowerShell 5.0 build worker
3 | os: WMF 5
4 |
5 | # Install required Pester and PSScriptAnalyzer modules
6 | install:
7 | - ps: Install-PackageProvider NuGet -Force | Out-Null
8 | - ps: Install-Module posh-git -Force
9 | - ps: Install-Module SecurityFever -Force
10 | - ps: Install-Module psake -Force
11 | - ps: Install-Module Pester -Force
12 | - ps: Install-Module PSScriptAnalyzer -Force
13 |
14 | # Set version to build number
15 | version: '{build}'
16 |
17 | # Build configuration
18 | configuration: Release
19 | platform: Any CPU
20 |
21 | # Execute psake build task
22 | build_script:
23 | - ps: >-
24 | Invoke-psake build.psake.ps1 -taskList Build -notr
25 |
26 | # Execute psake test and analyze task
27 | test_script:
28 | - ps: >-
29 | Invoke-psake build.psake.ps1 -taskList Pester, ScriptAnalyzer -notr
30 |
--------------------------------------------------------------------------------
/build.debug.ps1:
--------------------------------------------------------------------------------
1 |
2 | # Append module path for auto-loading
3 | $Env:PSModulePath = "$PSScriptRoot\Modules;$Env:PSModulePath"
4 |
5 | # Import all modules for the debugging session
6 | Get-ChildItem -Path "$PSScriptRoot\Modules" -Directory | ForEach-Object { Remove-Module $_.BaseName -ErrorAction 'SilentlyContinue' }
7 | Get-ChildItem -Path "$PSScriptRoot\Modules" -Directory | ForEach-Object { Import-Module $_.BaseName }
8 |
9 | <# ------------------ PLACE DEBUG COMMANDS AFTER THIS LINE ------------------ #>
10 |
--------------------------------------------------------------------------------
/build.psake.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | PowerShell module build script based on psake.
4 |
5 | .DESCRIPTION
6 | This psake build script supports building PowerShell manifest modules
7 | which contain PowerShell script functions and optionally binary .NET C#
8 | libraries. The build script contains the following tasks.
9 |
10 | - Task Verify
11 | Before any build task runs, verify that the build scripts are current.
12 |
13 | - Task Init
14 | Create folders, which are used by the build system: /tst and /bin.
15 |
16 | - Task Clean
17 | Cleans the content of build paths to ensure no side effects of
18 | previous build with the current build.
19 |
20 | - Task Compile
21 | If required, compile the Visual Studio solutions. Ensure that the
22 | build system copies the result into the target module folder.
23 |
24 | - Task Stage
25 | Copy all module files to the build directory excluding the class,
26 | function, helper and test files. These files get merged in the .psm1
27 | file.
28 |
29 | - Task Merge
30 | Copy the content of all .ps1 files within the classes, functions and
31 | helpers folders to the .psm1 file. This ensures a faster loading time
32 | for the module, but still a nice development experience with one
33 | function per file. This is optional and can be controlled by the
34 | setting $ModuleMerge.
35 |
36 | - Task Pester
37 | Invoke all Pester tests within the module and ensure that all tests
38 | pass before the build script continues.
39 |
40 | - Task ScriptAnalyzer
41 | Invoke all Script Analyzer rules against the PowerShell script files
42 | and ensure, that they do not break any rule.
43 |
44 | - Task Gallery
45 | This task will publish the module to a PowerShell Gallery. The task is
46 | not part of the default tasks, it needs to be called manually if
47 | needed during a deployment.
48 |
49 | - Task GitHub
50 | This task will publish the module to the GitHub Releases. The task is
51 | not part of the default tasks, it needs to be called manually if
52 | needed during a deployment.
53 |
54 | The tasks are grouped to the following task groups. The deploy task is
55 | not part of the default tasks, this must be invoked manually.
56 |
57 | - Group Default
58 | Task to group the other groups Build and Test. This will be invoked by
59 | default, if Invoke-psake is invoked.
60 |
61 | - Group Build
62 | The build task group will invoke the tasks Init, Clean, Compile, Stage
63 | and Merge. The output is stored in /bin.
64 |
65 | - Group Test
66 | All tasks to verify the integrity of the module with the tasks Pester
67 | and ScriptAnalyzer.
68 |
69 | - Group Deploy
70 | Tasks to deploy the module to the PowerShell Gallery and/or GitHub.
71 |
72 | .NOTES
73 | Author : Claudio Spizzi
74 | License : MIT License
75 |
76 | .LINK
77 | https://github.com/claudiospizzi
78 | #>
79 |
80 |
81 | # Suppress some rules for this build file
82 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingCmdletAliases', '')]
83 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
84 | param ()
85 |
86 |
87 | ## Configuration and Default task
88 |
89 | # Load project configuration
90 | . "$PSScriptRoot\build.settings.ps1"
91 |
92 | # Default build configuration
93 | Properties {
94 |
95 | # Option to disbale the build script verification
96 | $VerifyBuildSystem = $true
97 |
98 | # Module configuration: Location and option to enable the merge
99 | $ModulePath = Join-Path -Path $PSScriptRoot -ChildPath 'Modules'
100 | $ModuleNames = ''
101 | $ModuleMerge = $false
102 |
103 | # Source configuration: Visual Studio projects to compile
104 | $SourcePath = Join-Path -Path $PSScriptRoot -ChildPath 'Sources'
105 | $SourceNames = ''
106 | $SourcePublish = ''
107 |
108 | # Path were the release files are stored
109 | $ReleasePath = Join-Path -Path $PSScriptRoot -ChildPath 'bin'
110 |
111 | # Configure the Pester test execution
112 | $PesterPath = Join-Path -Path $PSScriptRoot -ChildPath 'tst'
113 | $PesterFile = 'pester.xml'
114 |
115 | # Configure the Script Analyzer rules
116 | $ScriptAnalyzerPath = Join-Path -Path $PSScriptRoot -ChildPath 'tst'
117 | $ScriptAnalyzerFile = 'scriptanalyzer.json'
118 | $ScriptAnalyzerRules = Get-ScriptAnalyzerRule
119 |
120 | # Define if the module is published to the PowerShell Gallery
121 | $GalleryEnabled = $false
122 | $GalleryName = 'PSGallery'
123 | $GallerySource = 'https://www.powershellgallery.com/api/v2/'
124 | $GalleryPublish = 'https://www.powershellgallery.com/api/v2/package/'
125 | $GalleryKey = ''
126 |
127 | # Define if the module is published to the GitHub Releases section
128 | $GitHubEnabled = $false
129 | $GitHubRepoName = ''
130 | $GitHubToken = ''
131 | }
132 |
133 | # Default task
134 | Task Default -depends Build, Test
135 |
136 |
137 | ## Build tasks
138 |
139 | # Overall build task
140 | Task Build -depends Verify, Init, Clean, Compile, Stage, Merge
141 |
142 | # Verify the build system
143 | Task Verify -requiredVariables VerifyBuildSystem {
144 |
145 | if ($VerifyBuildSystem)
146 | {
147 | $files = 'build.psake.ps1'
148 |
149 | foreach ($file in $files)
150 | {
151 | # Download reference file
152 | Invoke-WebRequest -Uri "https://raw.githubusercontent.com/claudiospizzi/PSModuleTemplate/master/Template/$file" -OutFile "$Env:Temp\$file"
153 |
154 | # Get content (don't compare hashes, because of new line chars)
155 | $expected = Get-Content -Path "$Env:Temp\$file"
156 | $actual = Get-Content -Path "$PSScriptRoot\$file"
157 |
158 | # Compare objects
159 | Assert -conditionToCheck ($null -eq (Compare-Object -ReferenceObject $expected -DifferenceObject $actual)) -failureMessage "The file '$file' is not current. Please update the file and restart the build."
160 | }
161 | }
162 | else
163 | {
164 | Write-Warning 'Build system is not verified!'
165 | }
166 | }
167 |
168 | # Create release and test folders
169 | Task Init -requiredVariables ReleasePath, PesterPath, ScriptAnalyzerPath {
170 |
171 | if (!(Test-Path -Path $ReleasePath))
172 | {
173 | New-Item -Path $ReleasePath -ItemType Directory -Verbose:$VerbosePreference > $null
174 | }
175 |
176 | if (!(Test-Path -Path $PesterPath))
177 | {
178 | New-Item -Path $PesterPath -ItemType Directory -Verbose:$VerbosePreference > $null
179 | }
180 |
181 | if (!(Test-Path -Path $ScriptAnalyzerPath))
182 | {
183 | New-Item -Path $ScriptAnalyzerPath -ItemType Directory -Verbose:$VerbosePreference > $null
184 | }
185 | }
186 |
187 | # Remove any items in the release and test folders
188 | Task Clean -depends Init -requiredVariables ReleasePath, PesterPath, ScriptAnalyzerPath, SourcePath, SourceNames {
189 |
190 | Get-ChildItem -Path $ReleasePath | Remove-Item -Recurse -Force -Verbose:$VerbosePreference
191 |
192 | Get-ChildItem -Path $PesterPath | Remove-Item -Recurse -Force -Verbose:$VerbosePreference
193 |
194 | Get-ChildItem -Path $ScriptAnalyzerPath | Remove-Item -Recurse -Force -Verbose:$VerbosePreference
195 |
196 | if ($null -ne $SourceNames -and -not [string]::IsNullOrEmpty($SourceNames))
197 | {
198 | $msBuildPath = 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin'
199 |
200 | if ($Env:Path -notlike "*$msBuildPath*")
201 | {
202 | $Env:Path = "$msBuildPath;$Env:Path"
203 | }
204 |
205 | foreach ($sourceName in $SourceNames)
206 | {
207 | $msBuildLog = (MSBuild.exe "$SourcePath\$sourceName\$sourceName.sln" /target:Clean /p:Configuration=Release)
208 |
209 | $msBuildLog | ForEach-Object { Write-Verbose $_ }
210 | }
211 | }
212 | }
213 |
214 | # Compile C# solutions
215 | Task Compile -depends Clean -requiredVariables SourcePath, SourcePublish, SourceNames {
216 |
217 | if ($null -ne $SourceNames -and -not [string]::IsNullOrEmpty($SourceNames))
218 | {
219 | $msBuildPath = 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin'
220 |
221 | if ($Env:Path -notlike "*$msBuildPath*")
222 | {
223 | $Env:Path = "$msBuildPath;$Env:Path"
224 | }
225 |
226 | foreach ($sourceName in $SourceNames)
227 | {
228 | $nuGetLog = (nuget.exe restore "Sources\$sourceName")
229 |
230 | $nuGetLog | ForEach-Object { Write-Verbose $_ }
231 |
232 | if ([String]::IsNullOrEmpty($SourcePublish))
233 | {
234 | $msBuildLog = (MSBuild.exe "$SourcePath\$sourceName\$sourceName.sln" /target:Build /p:Configuration=Release /verbosity:m)
235 | }
236 | else
237 | {
238 | $msBuildLog = (MSBuild.exe "$SourcePath\$sourceName\$sourceName.sln" /target:Build /p:Configuration=Release /p:DeployOnBuild=true /p:PublishProfile=$SourcePublish /verbosity:m)
239 | }
240 |
241 | $msBuildLog | ForEach-Object { Write-Verbose $_ }
242 | }
243 | }
244 | }
245 |
246 | # Copy all required module files to the release folder
247 | Task Stage -depends Compile -requiredVariables ReleasePath, ModulePath, ModuleNames, ModuleMerge {
248 |
249 | if ($null -ne $ModuleNames -and -not [string]::IsNullOrEmpty($ModuleNames))
250 | {
251 | foreach ($moduleName in $ModuleNames)
252 | {
253 | New-Item -Path "$ReleasePath\$moduleName" -ItemType 'Directory' | Out-Null
254 |
255 | # If the module is merged, exclude the module definition file and
256 | # all classes, functions and helpers.
257 | $excludePath = @()
258 | if ($ModuleMerge)
259 | {
260 | $excludePath += "$moduleName.psd1", 'Classes', 'Functions', 'Helpers'
261 | }
262 |
263 | foreach ($item in (Get-ChildItem -Path "$ModulePath\$moduleName" -Exclude $excludePath))
264 | {
265 | Copy-Item -Path $item.FullName -Destination "$ReleasePath\$moduleName\$($item.Name)" -Recurse -Verbose:$VerbosePreference
266 | }
267 | }
268 | }
269 | }
270 |
271 | # Merge the module by copying all helper and cmdlet functions to the psm1 file
272 | Task Merge -depends Stage -requiredVariables ReleasePath, ModulePath, ModuleNames, ModuleMerge {
273 |
274 | if ($null -ne $ModuleNames -and -not [string]::IsNullOrEmpty($ModuleNames))
275 | {
276 | foreach ($moduleName in $ModuleNames)
277 | {
278 | try
279 | {
280 | if ($ModuleMerge)
281 | {
282 | $moduleContent = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
283 | $moduleDefinition = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
284 |
285 | # Load code of the module namespace loader
286 | if ((Get-Content -Path "$ModulePath\$moduleName\$moduleName.psm1" -Raw) -match '#region Namepsace Loader[\r\n](?[\S\s]*)[\r\n]#endregion Namepsace Loader')
287 | {
288 | $moduleContent.Add($matches['NamespaceLoader'])
289 | }
290 |
291 | # Load code for all class files
292 | foreach ($file in (Get-ChildItem -Path "$ModulePath\$moduleName\Classes" -Filter '*.ps1' -Recurse -File -ErrorAction 'SilentlyContinue'))
293 | {
294 | $moduleContent.Add((Get-Content -Path $file.FullName -Raw))
295 | }
296 |
297 | # Load code for all function files
298 | foreach ($file in (Get-ChildItem -Path "$ModulePath\$moduleName\Functions" -Filter '*.ps1' -Recurse -File -ErrorAction 'SilentlyContinue'))
299 | {
300 | $moduleContent.Add((Get-Content -Path $file.FullName -Raw))
301 | }
302 |
303 | # Load code for all helpers files
304 | foreach ($file in (Get-ChildItem -Path "$ModulePath\$moduleName\Helpers" -Filter '*.ps1' -Recurse -File -ErrorAction 'SilentlyContinue'))
305 | {
306 | $moduleContent.Add((Get-Content -Path $file.FullName -Raw))
307 | }
308 |
309 | # Load code of the module namespace loader
310 | if ((Get-Content -Path "$ModulePath\$moduleName\$moduleName.psm1" -Raw) -match '#region Module Configuration[\r\n](?[\S\s]*)#endregion Module Configuration')
311 | {
312 | $moduleContent.Add($matches['ModuleConfiguration'])
313 | }
314 |
315 | # Concatenate whole code into the module file
316 | $moduleContent | Set-Content -Path "$ReleasePath\$moduleName\$moduleName.psm1" -Encoding UTF8 -Verbose:$VerbosePreference
317 |
318 | # Load the current content of the mudile definition
319 | $moduleDefinitionProcess = $true
320 | foreach ($moduleDefinitionLine in (Get-Content -Path "$ModulePath\$moduleName\$moduleName.psd1"))
321 | {
322 | if ($moduleDefinitionLine -like '*ScriptsToProcess*')
323 | {
324 | $moduleDefinition.Add(' # ScriptsToProcess = @()')
325 | $moduleDefinitionProcess = $false
326 | }
327 |
328 | if ($moduleDefinitionProcess)
329 | {
330 | $moduleDefinition.Add($moduleDefinitionLine)
331 | }
332 |
333 | if ($moduleDefinitionLine -like '*)*')
334 | {
335 | $moduleDefinitionProcess = $true
336 | }
337 | }
338 |
339 | # Save the updated module definition
340 | $moduleDefinition | Set-Content -Path "$ReleasePath\$moduleName\$moduleName.psd1" -Encoding UTF8 -Verbose:$VerbosePreference
341 | }
342 |
343 | # Compress
344 | Compress-Archive -Path "$ReleasePath\$moduleName" -DestinationPath "$ReleasePath\$moduleName.zip" -Verbose:$VerbosePreference
345 |
346 | # Publish AppVeyor artifacts
347 | if ($env:APPVEYOR)
348 | {
349 | Push-AppveyorArtifact -Path "$ReleasePath\$moduleName.zip" -DeploymentName $moduleName -Verbose:$VerbosePreference
350 | }
351 | }
352 | catch
353 | {
354 | Assert -conditionToCheck $false -failureMessage "Build failed: $_"
355 | }
356 | }
357 | }
358 | }
359 |
360 |
361 | ## Test tasks
362 |
363 | # Overall test task
364 | Task Test -depends Build, Pester, ScriptAnalyzer
365 |
366 | # Invoke Pester tests and return result as NUnit XML file
367 | Task Pester -requiredVariables ReleasePath, ModuleNames, PesterPath, PesterFile {
368 |
369 | if (!(Get-Module -Name 'Pester' -ListAvailable))
370 | {
371 | Write-Warning "Pester module is not installed. Skipping $($psake.context.currentTaskName) task."
372 | return
373 | }
374 |
375 | Import-Module -Name 'Pester'
376 |
377 | foreach ($moduleName in $ModuleNames)
378 | {
379 | $modulePesterFile = Join-Path -Path $PesterPath -ChildPath "$moduleName-$PesterFile"
380 |
381 | powershell.exe -NoLogo -NoProfile -NonInteractive -Command "Set-Location -Path '$ReleasePath\$moduleName'; Invoke-Pester -OutputFile '$modulePesterFile' -OutputFormat 'NUnitXml'"
382 |
383 | $testResults = [Xml] (Get-Content -Path $modulePesterFile)
384 |
385 | Assert -conditionToCheck ($testResults.'test-results'.failures -eq 0) -failureMessage "One or more Pester tests failed, build cannot continue."
386 |
387 | # Publish AppVeyor test results
388 | if ($env:APPVEYOR)
389 | {
390 | $webClient = New-Object -TypeName 'System.Net.WebClient'
391 | $webClient.UploadFile("https://ci.appveyor.com/api/testresults/nunit/$env:APPVEYOR_JOB_ID", $modulePesterFile)
392 | }
393 | }
394 | }
395 |
396 | # Invoke Script Analyzer tests and stop if any test fails
397 | Task ScriptAnalyzer -requiredVariables ReleasePath, ModulePath, ModuleNames, ScriptAnalyzerPath, ScriptAnalyzerFile, ScriptAnalyzerRules {
398 |
399 | if (!(Get-Module -Name 'PSScriptAnalyzer' -ListAvailable))
400 | {
401 | Write-Warning "PSScriptAnalyzer module is not installed. Skipping $($psake.context.currentTaskName) task."
402 | return
403 | }
404 |
405 | Import-Module -Name 'PSScriptAnalyzer'
406 |
407 | foreach ($moduleName in $ModuleNames)
408 | {
409 | $moduleScriptAnalyzerFile = Join-Path -Path $ScriptAnalyzerPath -ChildPath "$moduleName-$ScriptAnalyzerFile"
410 |
411 | # Invoke script analyzer on the module but exclude all examples
412 | $analyzeResults = Invoke-ScriptAnalyzer -Path "$ReleasePath\$moduleName" -IncludeRule $ScriptAnalyzerRules -Recurse
413 | $analyzeResults = $analyzeResults | Where-Object { $_.ScriptPath -notlike "$releasePath\$moduleName\Examples\*" }
414 | $analyzeResults | ConvertTo-Json | Out-File -FilePath $moduleScriptAnalyzerFile -Encoding UTF8
415 |
416 | Show-ScriptAnalyzerResult -ModuleName $moduleName -Rule $ScriptAnalyzerRules -Result $analyzeResults
417 |
418 | Assert -conditionToCheck ($analyzeResults.Where({$_.Severity -ne 0}).Count -eq 0) -failureMessage "One or more Script Analyzer tests failed, build cannot continue."
419 | }
420 | }
421 |
422 |
423 | ## Deploy tasks
424 |
425 | # Overall deploy task
426 | Task Deploy -depends Test, GitHub, Gallery
427 |
428 | # Deploy a release to the GitHub repository
429 | Task GitHub -requiredVariables ReleasePath, ModuleNames, GitHubEnabled, GitHubRepoName, GitHubToken {
430 |
431 | if (!$GitHubEnabled)
432 | {
433 | return
434 | }
435 |
436 | if ([String]::IsNullOrEmpty($GitHubToken))
437 | {
438 | throw 'GitHub key is null or empty!'
439 | }
440 |
441 | Test-GitRepo @($ModuleNames)[0]
442 |
443 | $plainGitHubToken = $GitHubToken | Unprotect-SecureString
444 |
445 | foreach ($moduleName in $ModuleNames)
446 | {
447 | $moduleVersion = (Import-PowerShellDataFile -Path "$ReleasePath\$moduleName\$moduleName.psd1").ModuleVersion
448 | $releaseNotes = Get-ReleaseNote -Version $moduleVersion
449 |
450 | # Add TLS 1.2 for GitHub
451 | if (([Net.ServicePointManager]::SecurityProtocol -band [Net.SecurityProtocolType]::Tls12) -ne [Net.SecurityProtocolType]::Tls12)
452 | {
453 | [Net.ServicePointManager]::SecurityProtocol += [Net.SecurityProtocolType]::Tls12
454 | }
455 |
456 | # Create GitHub release
457 | $releaseParams = @{
458 | Method = 'Post'
459 | Uri = "https://api.github.com/repos/$GitHubRepoName/releases"
460 | Headers = @{
461 | 'Accept' = 'application/vnd.github.v3+json'
462 | 'Authorization' = "token $plainGitHubToken"
463 | }
464 | Body = @{
465 | tag_name = $moduleVersion
466 | target_commitish = 'master'
467 | name = "$moduleName v$moduleVersion"
468 | body = ($releaseNotes -join "`n")
469 | draft = $false
470 | prerelease = $false
471 | } | ConvertTo-Json
472 | }
473 | $release = Invoke-RestMethod @releaseParams -ErrorAction Stop
474 |
475 | # Upload artifact to GitHub
476 | $artifactParams = @{
477 | Method = 'Post'
478 | Uri = "https://uploads.github.com/repos/$GitHubRepoName/releases/$($release.id)/assets?name=$moduleName-$moduleVersion.zip"
479 | Headers = @{
480 | 'Accept' = 'application/vnd.github.v3+json'
481 | 'Authorization' = "token $plainGitHubToken"
482 | 'Content-Type' = 'application/zip'
483 | }
484 | InFile = "$ReleasePath\$ModuleName.zip"
485 | }
486 | $artifact = Invoke-RestMethod @artifactParams -ErrorAction Stop
487 | }
488 | }
489 |
490 | # Deploy to the public PowerShell Gallery
491 | Task Gallery -requiredVariables ReleasePath, ModuleNames, GalleryEnabled, GalleryName, GallerySource, GalleryPublish, GalleryKey {
492 |
493 | if (!$GalleryEnabled)
494 | {
495 | return
496 | }
497 |
498 | if ([String]::IsNullOrEmpty($GalleryKey))
499 | {
500 | throw 'PowerShell Gallery key is null or empty!'
501 | }
502 |
503 | Test-GitRepo @($ModuleNames)[0]
504 |
505 | # Register the target PowerShell Gallery, if it does not exist
506 | if ($null -eq (Get-PSRepository -Name $GalleryName -ErrorAction SilentlyContinue))
507 | {
508 | Register-PSRepository -Name $GalleryName -SourceLocation $GallerySource -PublishLocation $GalleryPublish
509 | }
510 |
511 | foreach ($moduleName in $ModuleNames)
512 | {
513 | $moduleVersion = (Import-PowerShellDataFile -Path "$ReleasePath\$moduleName\$moduleName.psd1").ModuleVersion
514 | $releaseNotes = Get-ReleaseNote -Version $moduleVersion
515 |
516 | $plainGalleryKey = $GalleryKey | Unprotect-SecureString
517 |
518 | Publish-Module -Path "$ReleasePath\$moduleName" -Repository $GalleryName -NuGetApiKey $plainGalleryKey -ReleaseNotes $releaseNotes
519 | }
520 | }
521 |
522 |
523 | ## Helper functions
524 |
525 | # Check if the git repo is ready for a deployment
526 | function Test-GitRepo($ModuleName)
527 | {
528 | $gitStatus = Get-GitStatus
529 | if ($gitStatus.Branch -ne 'master')
530 | {
531 | throw "Git Exception: $($gitStatus.Branch) is checked out, switch to master branch! (git checkout master)"
532 | }
533 |
534 | if ($gitStatus.AheadBy -ne 0)
535 | {
536 | throw "Git Exception: master branch is ahead by $($gitStatus.AheadBy)! (git push)"
537 | }
538 |
539 | $version = (Import-PowerShellDataFile -Path "$ReleasePath\$ModuleName\$ModuleName.psd1").ModuleVersion
540 |
541 | $localTag = (git describe --tags)
542 | if ($version -ne $localTag)
543 | {
544 | throw "Git Exception: Tag $localTag not matches module version $version! (git tag $version)"
545 | }
546 |
547 | $remoteTag = (git ls-remote origin "refs/tags/$version")
548 | if ($remoteTag -notlike "*refs/tags/$version")
549 | {
550 | throw "Git Exception: Local tag $localTag not found on origin remote! (git push --tag)"
551 | }
552 | }
553 |
554 | # Check if a source branch is merged to the target branch
555 | function Get-GitMergeStatus($Branch)
556 | {
557 | $mergedBranches = (git.exe branch --merged "$Branch")
558 |
559 | foreach ($mergedBranch in $mergedBranches)
560 | {
561 | $mergedBranch = $mergedBranch.Trim('* ')
562 |
563 | Write-Output $mergedBranch
564 | }
565 | }
566 |
567 | # Show the Script Analyzer results on the host
568 | function Show-ScriptAnalyzerResult($ModuleName, $Rule, $Result)
569 | {
570 | $colorMap = @{
571 | ParseError = 'DarkRed'
572 | Error = 'Red'
573 | Warning = 'Yellow'
574 | Information = 'Cyan'
575 | }
576 |
577 | Write-Host "`nModule $ModuleName" -ForegroundColor Green
578 |
579 | foreach ($currentRule in $Rule)
580 | {
581 | Write-Host "`n Rule $($currentRule.RuleName)" -ForegroundColor Green
582 |
583 | $records = $Result.Where({$_.RuleName -eq $currentRule.RuleName})
584 |
585 | if ($records.Count -eq 0)
586 | {
587 | Write-Host " [+] No rule violation found" -ForegroundColor DarkGreen
588 | }
589 | else
590 | {
591 | foreach ($record in $records)
592 | {
593 | Write-Host " [-] $($record.Severity): $($record.Message)" -ForegroundColor $colorMap[[String]$record.Severity]
594 | Write-Host " at $($record.ScriptPath): line $($record.Line)" -ForegroundColor $colorMap[[String]$record.Severity]
595 | }
596 | }
597 | }
598 |
599 | Write-Host "`nScript Analyzer completed"
600 | Write-Host "Rules: $($Rule.Count) Findings: $($analyzeResults.Count)"
601 | }
602 |
603 | # Extract the Release Notes from the CHANGELOG.md file
604 | function Get-ReleaseNote($Version)
605 | {
606 | $changelogFile = Join-Path -Path $PSScriptRoot -ChildPath 'CHANGELOG.md'
607 |
608 | $releaseNotes = @('Release Notes:')
609 |
610 | $isCurrentVersion = $false
611 |
612 | foreach ($line in (Get-Content -Path $changelogFile))
613 | {
614 | if ($line -like "## $Version - ????-??-??")
615 | {
616 | $isCurrentVersion = $true
617 | }
618 | elseif ($line -like '## *')
619 | {
620 | $isCurrentVersion = $false
621 | }
622 |
623 | if ($isCurrentVersion -and ($line.StartsWith('* ') -or $line.StartsWith('- ')))
624 | {
625 | $releaseNotes += $line
626 | }
627 | }
628 |
629 | Write-Output $releaseNotes
630 | }
631 |
--------------------------------------------------------------------------------
/build.settings.ps1:
--------------------------------------------------------------------------------
1 |
2 | Properties {
3 |
4 | $ModuleNames = 'ScriptConfig'
5 |
6 | $GalleryEnabled = $true
7 | $GalleryKey = Use-VaultSecureString -TargetName 'PowerShell Gallery Key (claudiospizzi)'
8 |
9 | $GitHubEnabled = $true
10 | $GitHubRepoName = 'claudiospizzi/ScriptConfig'
11 | $GitHubToken = Use-VaultSecureString -TargetName 'GitHub Token (claudiospizzi)'
12 | }
13 |
--------------------------------------------------------------------------------