diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index ba73e9cdee..9574ed7a6f 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,11 +3,11 @@ "isRoot": true, "tools": { "gitversion.tool": { - "version": "5.12.0", + "version": "6.0.3", "commands": [ "dotnet-gitversion" ], "rollForward": false } } -} +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index a06e4ea9e8..78906e8269 100644 --- a/.editorconfig +++ b/.editorconfig @@ -23,9 +23,10 @@ indent_size = 2 # Code style defaults csharp_using_directive_placement = outside_namespace:error csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion -dotnet_sort_system_directives_first = true +dotnet_sort_system_directives_first = true:error dotnet_style_readonly_field = true:suggestion csharp_style_namespace_declarations = file_scoped:error +dotnet_diagnostic.IDE0005.severity = error # License header file_header_template = Copyright (c) Microsoft Corporation.\nLicensed under the MIT License. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f460982500..59e2c47c7b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,6 +16,12 @@ updates: - dependencies reviewers: - microsoft/psrule + groups: + # Group PowerShell packages that have inter-dependencies + dev-powershell: + patterns: + - System.Management.Automation + - Microsoft.PowerShell.SDK # Maintain dependencies for GitHub Actions - package-ecosystem: github-actions diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5763ad9be3..2388657551 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -47,6 +47,11 @@ jobs: timeout-minutes: 5 run: Invoke-Build -Configuration Release -AssertStyle GitHubActions + - name: Lint .NET + shell: pwsh + timeout-minutes: 5 + run: dotnet format --verify-no-changes + - name: Upload module uses: actions/upload-artifact@v4 with: diff --git a/PSRule.sln b/PSRule.sln index 22ad753e7b..29700881d3 100644 --- a/PSRule.sln +++ b/PSRule.sln @@ -3,14 +3,16 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32602.215 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.Types", "src\PSRule.Types\PSRule.Types.csproj", "{5FE4DB0B-63D1-4DDB-9762-9C0D29168BC9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.Badges", "src\PSRule.Badges\PSRule.Badges.csproj", "{309BED8B-4E60-4C42-A2B4-37A2E7EBEF3F}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule", "src\PSRule\PSRule.csproj", "{0130215D-58EB-4887-B6FA-31ED02500569}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.Benchmark", "src\PSRule.Benchmark\PSRule.Benchmark.csproj", "{3EC0912F-BFC7-4B53-A1A1-0BA993C6282E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.Tests", "tests\PSRule.Tests\PSRule.Tests.csproj", "{D3488CE2-779F-4474-B38A-F894A4B689F7}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.Badges", "src\PSRule.Badges\PSRule.Badges.csproj", "{309BED8B-4E60-4C42-A2B4-37A2E7EBEF3F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.BuildTool", "src\PSRule.BuildTool\PSRule.BuildTool.csproj", "{20DDCC65-8A9A-4BDC-91EC-C3BE6F32E52E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.SDK", "src\PSRule.SDK\PSRule.SDK.csproj", "{07A84E67-1CA3-4766-B9EA-1FDD9DF6516F}" @@ -22,8 +24,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution src\PSRule.Common.props = src\PSRule.Common.props EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.Types", "src\PSRule.Types\PSRule.Types.csproj", "{5FE4DB0B-63D1-4DDB-9762-9C0D29168BC9}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.MSBuild", "src\PSRule.MSBuild\PSRule.MSBuild.csproj", "{872D2648-2F00-475E-84B5-F08BE07385B7}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E0EA0CBA-96C5-4447-8B69-BC13EF0D7A4A}" @@ -32,7 +32,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.Tool.Tests", "tests\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.CommandLine", "src\PSRule.CommandLine\PSRule.CommandLine.csproj", "{9A556814-8E9D-4C76-8F6D-1AF2DA23A9E0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSRule.CommandLine.Tests", "tests\PSRule.CommandLine.Tests\PSRule.CommandLine.Tests.csproj", "{C25E2FC1-E306-4D99-925C-15E5DD51F6A2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.CommandLine.Tests", "tests\PSRule.CommandLine.Tests\PSRule.CommandLine.Tests.csproj", "{C25E2FC1-E306-4D99-925C-15E5DD51F6A2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.Types.Tests", "tests\PSRule.Types.Tests\PSRule.Types.Tests.csproj", "{34095F78-CDA3-4E72-B64C-6366EA4B3EAF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -88,6 +90,10 @@ Global {C25E2FC1-E306-4D99-925C-15E5DD51F6A2}.Debug|Any CPU.Build.0 = Debug|Any CPU {C25E2FC1-E306-4D99-925C-15E5DD51F6A2}.Release|Any CPU.ActiveCfg = Release|Any CPU {C25E2FC1-E306-4D99-925C-15E5DD51F6A2}.Release|Any CPU.Build.0 = Release|Any CPU + {34095F78-CDA3-4E72-B64C-6366EA4B3EAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {34095F78-CDA3-4E72-B64C-6366EA4B3EAF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34095F78-CDA3-4E72-B64C-6366EA4B3EAF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {34095F78-CDA3-4E72-B64C-6366EA4B3EAF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -96,6 +102,7 @@ Global {D3488CE2-779F-4474-B38A-F894A4B689F7} = {E0EA0CBA-96C5-4447-8B69-BC13EF0D7A4A} {DA46C891-08F1-4D01-9F98-1F8BB10CAFEC} = {E0EA0CBA-96C5-4447-8B69-BC13EF0D7A4A} {C25E2FC1-E306-4D99-925C-15E5DD51F6A2} = {E0EA0CBA-96C5-4447-8B69-BC13EF0D7A4A} + {34095F78-CDA3-4E72-B64C-6366EA4B3EAF} = {E0EA0CBA-96C5-4447-8B69-BC13EF0D7A4A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {533491EB-BAE9-472E-B57F-A675ECD335B5} diff --git a/docs/CHANGELOG-v3.md b/docs/CHANGELOG-v3.md index 2fd3f4fff1..bc86bf9451 100644 --- a/docs/CHANGELOG-v3.md +++ b/docs/CHANGELOG-v3.md @@ -27,6 +27,30 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers ## Unreleased +## v3.0.0-B0275 (pre-release) + +What's changed since pre-release v3.0.0-B0267: + +- New features: + - Allow CLI upgrade command to upgrade a single module by @BernieWhite. + [#2551](https://github.com/microsoft/PSRule/issues/2551) + - A single or specific modules can be upgraded by name when using `module upgrade`. + - By default, all modules are upgraded. + - Allow CLI to install pre-release modules by @BernieWhite. + [#2550](https://github.com/microsoft/PSRule/issues/2550) + - Add and upgrade pre-release modules with `--prerelease`. + - Pre-release modules will be restored from the lock file with `module restore`. +- General improvements: + - **Breaking change**: Empty version comparison only accepts stable versions by default by @BernieWhite. + [#2557](https://github.com/microsoft/PSRule/issues/2557) + - `version` and `apiVersion` assertions only accept stable versions by default for all cases. + - Pre-release versions can be accepted by setting `includePrerelease` to `true`. +- Bug fixes: + - Fixed CLI upgrade uses pre-release module by @BernieWhite. + [#2549](https://github.com/microsoft/PSRule/issues/2549) + +## v3.0.0-B0267 (pre-release) + What's changed since pre-release v3.0.0-B0203: - New features: @@ -37,6 +61,12 @@ What's changed since pre-release v3.0.0-B0203: - The `Execution.Break` option can be set to `Never`, `OnError`, `OnWarning`, or `OnInformation`. - If a rule fails with a severity level equal or higher than the configured level the pipeline will break. - General improvements: + - **Breaking change**: Improve scope handling for correctly handling cases with multiple module by @BernieWhite. + [#1215](https://github.com/microsoft/PSRule/issues/1215) + - As a result of this change: + - The `binding` property can no longer be used within baselines. + - Custom inline script blocks can no longer be used for custom binding. + - Use module configuration or workspace to configure binding options instead. - Added support for native logging within emitters by @BernieWhite. [#1837](https://github.com/microsoft/PSRule/issues/1837) - Engineering: @@ -59,6 +89,10 @@ What's changed since pre-release v3.0.0-B0203: - Bug fixes: - Fixed CLI exception the term Find-Module is not recognized by @BernieWhite. [#1860](https://github.com/microsoft/PSRule/issues/1860) + - Fixed aggregation of reasons with `$Assert.AnyOf()` by @BernieWhite. + [#1829](https://github.com/microsoft/PSRule/issues/1829) + - Added `Problem` to validate sets of `OutputOutcome` by @nightroman + [#2542](https://github.com/microsoft/PSRule/issues/2542) ## v3.0.0-B0203 (pre-release) diff --git a/docs/commands/PSRule/en-US/Assert-PSRule.md b/docs/commands/PSRule/en-US/Assert-PSRule.md index 0ed1653e34..75cee49aaf 100644 --- a/docs/commands/PSRule/en-US/Assert-PSRule.md +++ b/docs/commands/PSRule/en-US/Assert-PSRule.md @@ -106,7 +106,7 @@ Evaluate items and additionally save the results into a variable `resultRecords` ### -InputPath -Instead of processing objects from the pipeline, import objects file the specified file paths. +Instead of processing objects from the pipeline, import objects from the specified file paths. ```yaml Type: String[] diff --git a/docs/commands/PSRule/en-US/Get-PSRuleTarget.md b/docs/commands/PSRule/en-US/Get-PSRuleTarget.md index 940835d5dc..09e24711c6 100644 --- a/docs/commands/PSRule/en-US/Get-PSRuleTarget.md +++ b/docs/commands/PSRule/en-US/Get-PSRuleTarget.md @@ -45,7 +45,7 @@ Get target objects from `resources.json`. ### -InputPath -Instead of processing objects from the pipeline, import objects file the specified file paths. +Instead of processing objects from the pipeline, import objects from the specified file paths. ```yaml Type: String[] diff --git a/docs/commands/PSRule/en-US/Invoke-PSRule.md b/docs/commands/PSRule/en-US/Invoke-PSRule.md index cb84fe15ac..3a5eeb133d 100644 --- a/docs/commands/PSRule/en-US/Invoke-PSRule.md +++ b/docs/commands/PSRule/en-US/Invoke-PSRule.md @@ -409,7 +409,7 @@ Accept wildcard characters: False ### -InputPath -Instead of processing objects from the pipeline, import objects file the specified file paths. +Instead of processing objects from the pipeline, import objects from the specified file paths. ```yaml Type: String[] diff --git a/docs/commands/PSRule/en-US/New-PSRuleOption.md b/docs/commands/PSRule/en-US/New-PSRuleOption.md index d00cfc6ab0..821e623177 100644 --- a/docs/commands/PSRule/en-US/New-PSRuleOption.md +++ b/docs/commands/PSRule/en-US/New-PSRuleOption.md @@ -17,8 +17,8 @@ Create options to configure PSRule execution. ```text New-PSRuleOption [[-Path] ] [-Configuration ] - [-SuppressTargetName ] [-BindTargetName ] - [-BindTargetType ] [-BaselineGroup ] [-BindingIgnoreCase ] + [-SuppressTargetName ] + [-BaselineGroup ] [-BindingIgnoreCase ] [-BindingField ] [-BindingNameSeparator ] [-BindingPreferTargetInfo ] [-TargetName ] [-TargetType ] [-BindingUseQualifiedName ] [-Convention ] [-DuplicateResourceId ] @@ -44,8 +44,8 @@ New-PSRuleOption [[-Path] ] [-Configuration ] ```text New-PSRuleOption [-Option] [-Configuration ] - [-SuppressTargetName ] [-BindTargetName ] - [-BindTargetType ] [-BaselineGroup ] [-BindingIgnoreCase ] + [-SuppressTargetName ] + [-BaselineGroup ] [-BindingIgnoreCase ] [-BindingField ] [-BindingNameSeparator ] [-BindingPreferTargetInfo ] [-TargetName ] [-TargetType ] [-BindingUseQualifiedName ] [-Convention ] [-DuplicateResourceId ] @@ -71,7 +71,7 @@ New-PSRuleOption [-Option] [-Configuration ] ```text New-PSRuleOption [-Default] [-Configuration ] [-SuppressTargetName ] - [-BindTargetName ] [-BindTargetType ] [-BaselineGroup ] + [-BaselineGroup ] [-BindingIgnoreCase ] [-BindingField ] [-BindingNameSeparator ] [-BindingPreferTargetInfo ] [-TargetName ] [-TargetType ] [-BindingUseQualifiedName ] [-Convention ] @@ -118,28 +118,6 @@ Create an options object that suppresses `TestObject1` and `TestObject3` for a r ### Example 3 -```powershell -# Create a custom function that returns a TargetName string -$bindFn = { - param ($TargetObject) - - $otherName = $TargetObject.PSObject.Properties['OtherName']; - - if ($otherName -eq $Null) { - return $Null - } - - return $otherName.Value; -} - -# Specify the binding function script block code to execute -$option = New-PSRuleOption -BindTargetName $bindFn; -``` - -Creates an options object that uses a custom function to bind the _TargetName_ of an object. - -### Example 4 - ```powershell $option = New-PSRuleOption -Configuration @{ 'appServiceMinInstanceCount' = 2 }; ``` @@ -221,23 +199,6 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -BindTargetName - -Configures a custom function to use to bind TargetName of an object. -See about_PSRule_Options for more information. - -```yaml -Type: BindTargetName[] -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - ### -Configuration Configures a set of baseline configuration values that can be used in rule definitions instead of using hard coded values. @@ -274,23 +235,6 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -BindTargetType - -Configures a custom function to use to bind TargetType of an object. -See about_PSRule_Options for more information. - -```yaml -Type: BindTargetName[] -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - ### -BindingIgnoreCase Sets the option `Binding.IgnoreCase`. diff --git a/docs/commands/PSRule/en-US/Test-PSRuleTarget.md b/docs/commands/PSRule/en-US/Test-PSRuleTarget.md index 37b2c4f733..fa55eb2e4f 100644 --- a/docs/commands/PSRule/en-US/Test-PSRuleTarget.md +++ b/docs/commands/PSRule/en-US/Test-PSRuleTarget.md @@ -304,7 +304,7 @@ Accept wildcard characters: False ### -InputPath -Instead of processing objects from the pipeline, import objects file the specified file paths. +Instead of processing objects from the pipeline, import objects from the specified file paths. ```yaml Type: String[] diff --git a/docs/concepts/PSRule/en-US/about_PSRule_Assert.md b/docs/concepts/PSRule/en-US/about_PSRule_Assert.md index c154e412ee..7bbb71a989 100644 --- a/docs/concepts/PSRule/en-US/about_PSRule_Assert.md +++ b/docs/concepts/PSRule/en-US/about_PSRule_Assert.md @@ -141,11 +141,12 @@ Notable differences between object paths and JSONPath are: ### APIVersion -The `APIVersion` assertion method checks the field value is a valid date version. +The `APIVersion` assertion method checks the field value is a valid stable date version. A constraint can optionally be provided to require the date version to be within a range. +By default, only stable versions are accepted unless pre-releases are included. A date version uses the format `yyyy-MM-dd` (`2015-10-01`). -Additionally an optional string prerelease identifier can be used `yyyy-MM-dd-prerelease` (`2015-10-01-preview.1`). +Additionally an optional string pre-release identifier can be used `yyyy-MM-dd-prerelease` (`2015-10-01-preview.1`). The following parameters are accepted: @@ -153,7 +154,7 @@ The following parameters are accepted: - `field` - The name of the field to check. This is a case insensitive compare. - `constraint` (optional) - A version constraint, see below for details of version constrain format. -- `includePrerelease` (optional) - Determines if prerelease versions are included. +- `includePrerelease` (optional) - Determines if pre-release versions are included. Unless specified this defaults to `$False`. The following are supported constraints: @@ -183,14 +184,14 @@ By example: - Pass: `2014-01-01`, `2015-10-01`, `2019-06-30`, `2022-02-01`. - Fail: `2015-01-01`, `2022-09-01`. -Handling for prerelease versions: +Handling for pre-release versions: -- Constraints and versions containing prerelease identifiers are supported. +- Constraints and versions containing pre-release identifiers are supported. i.e. `>=2015-10-01-preview` or `2015-10-01-preview`. -- A version containing a prerelease identifer follows similar ordering to semantic versioning. +- A version containing a pre-release identifier follows similar ordering to semantic versioning. i.e. `2015-10-01-preview` < `2015-10-01-preview.1` < `2015-10-01` < `2022-03-01-preview` < `2022-03-01`. -- A constraint without a prerelease identifer will only match a stable version by default. - Set `includePrerelease` to `$True` to include prerelease versions. +- A constraint without a pre-release identifier will only match a stable version by default. + Set `includePrerelease` to `$True` to include pre-;release versions. Alternatively use the `@pre` or `@prerelease` flag in a constraint. Reasons include: @@ -204,10 +205,14 @@ Reasons include: Examples: ```powershell -Rule 'ValidAPIVersion' { +Rule 'ValidStableAPIVersion' { $Assert.APIVersion($TargetObject, 'apiVersion') } +Rule 'AnyValidAPIVersion' { + $Assert.APIVersion($TargetObject, 'apiVersion', '', $True) +} + Rule 'MinimumAPIVersion' { $Assert.APIVersion($TargetObject, 'apiVersion', '>=2015-10-01') } @@ -1584,17 +1589,18 @@ Rule 'Subset' { ### Version -The `Version` assertion method checks the field value is a valid semantic version. +The `Version` assertion method checks the field value is a valid stable semantic version. A constraint can optionally be provided to require the semantic version to be within a range. +By default, only stable versions are accepted unless pre-releases are included. The following parameters are accepted: - `inputObject` - The object being checked for the specified field. - `field` - The name of the field to check. -This is a case insensitive compare. + This is a case insensitive compare. - `constraint` (optional) - A version constraint, see below for details of version constrain format. -- `includePrerelease` (optional) - Determines if prerelease versions are included. -Unless specified this defaults to `$False`. +- `includePrerelease` (optional) - Determines if pre-release versions are included. + Unless specified this defaults to `$False`. The following are supported constraints: @@ -1627,17 +1633,17 @@ By example: - Pass: `1.2.3`, `3.4.5`, `3.5.0`, `4.9.9`. - Fail: `3.0.0`, `5.0.0`. -Handling for prerelease versions: +Handling for pre-release versions: -- Constraints and versions containing prerelease identifiers are supported. -i.e. `>=1.2.3-build.1` or `1.2.3-build.1`. -- A version containing a prerelease identifer follows semantic versioning rules. +- Constraints and versions containing pre-release identifiers are supported. + i.e. `>=1.2.3-build.1` or `1.2.3-build.1`. +- A version containing a pre-release identifier follows semantic versioning rules. i.e. `1.2.3-alpha` < `1.2.3-alpha.1` < `1.2.3-alpha.beta` < `1.2.3-beta` < `1.2.3-beta.2` < `1.2.3-beta.11` < `1.2.3-rc.1` < `1.2.3`. -- A constraint without a prerelease identifer will only match a stable version by default. -Set `includePrerelease` to `$True` to include prerelease versions. -- Constraints with a prerelease identifer will only match: - - Matching prerelease versions of the same major.minor.patch version by default. - Set `includePrerelease` to `$True` to include prerelease versions of all matching versions. +- A constraint without a pre-release identifier will only match a stable version by default. + Set `includePrerelease` to `$True` to include pre-release versions. +- Constraints with a pre-release identifier will only match: + - Matching pre-release versions of the same major.minor.patch version by default. + Set `includePrerelease` to `$True` to include pre-release versions of all matching versions. Alternatively use the `@pre` or `@prerelease` flag in a constraint. - Matching stable versions. @@ -1672,10 +1678,14 @@ Reasons include: Examples: ```powershell -Rule 'ValidVersion' { +Rule 'ValidStableVersion' { $Assert.Version($TargetObject, 'version') } +Rule 'AnyValidVersion' { + $Assert.Version($TargetObject, 'version', '', $True) +} + Rule 'MinimumVersion' { $Assert.Version($TargetObject, 'version', '>=1.2.3') } diff --git a/docs/concepts/PSRule/en-US/about_PSRule_Baseline.md b/docs/concepts/PSRule/en-US/about_PSRule_Baseline.md index ae4e97347f..266a6dc8d5 100644 --- a/docs/concepts/PSRule/en-US/about_PSRule_Baseline.md +++ b/docs/concepts/PSRule/en-US/about_PSRule_Baseline.md @@ -13,13 +13,6 @@ A baseline includes a set of rule and configuration options that are used for ev The following baseline options can be configured: -- [Binding.Field](about_PSRule_Options.md#bindingfield) -- [Binding.IgnoreCase](about_PSRule_Options.md#bindingignorecase) -- [Binding.NameSeparator](about_PSRule_Options.md#bindingnameseparator) -- [Binding.PreferTargetInfo](about_PSRule_Options.md#bindingprefertargetinfo) -- [Binding.TargetName](about_PSRule_Options.md#bindingtargetname) -- [Binding.TargetType](about_PSRule_Options.md#bindingtargettype) -- [Binding.UseQualifiedName](about_PSRule_Options.md#bindingusequalifiedname) - [Configuration](about_PSRule_Options.md#configuration) - [Rule.Include](about_PSRule_Options.md#ruleinclude) - [Rule.IncludeLocal](about_PSRule_Options.md#ruleincludelocal) @@ -55,7 +48,6 @@ metadata: annotations: { } spec: # One or more baseline options - binding: { } rule: { } configuration: { } ``` @@ -70,16 +62,6 @@ kind: Baseline metadata: name: Baseline1 spec: - binding: - field: - id: - - ResourceId - targetName: - - Name - - ResourceName - - ResourceGroupName - targetType: - - ResourceType rule: include: - Rule1 @@ -96,13 +78,6 @@ kind: Baseline metadata: name: Baseline2 spec: - binding: - targetName: - - Name - - ResourceName - - ResourceGroupName - targetType: - - ResourceType rule: include: - Rule1 @@ -125,7 +100,6 @@ To define a JSON baseline spec use the following structure: "annotations": {} }, "spec": { - "binding": {}, "rule": {}, "configuration": {} } @@ -145,21 +119,6 @@ For example: "name": "Baseline1" }, "spec": { - "binding": { - "field": { - "id": [ - "ResourceId" - ] - }, - "targetName": [ - "Name", - "ResourceName", - "ResourceGroupName" - ], - "targetType": [ - "ResourceType" - ] - }, "rule": { "include": [ "Rule1", @@ -182,16 +141,6 @@ For example: "name": "Baseline2" }, "spec": { - "binding": { - "targetName": [ - "Name", - "ResourceName", - "ResourceGroupName" - ], - "targetType": [ - "ResourceType" - ] - }, "rule": { "include": [ "Rule1", @@ -276,11 +225,6 @@ kind: Baseline metadata: name: TestBaseline1 spec: - binding: - targetName: - - AlternateName - targetType: - - kind rule: include: - 'WithBaseline' @@ -294,11 +238,6 @@ kind: Baseline metadata: name: TestBaseline2 spec: - binding: - targetName: - - AlternateName - targetType: - - kind rule: include: - 'WithBaseline' @@ -320,14 +259,6 @@ spec: "name": "TestBaseline1" }, "spec": { - "binding": { - "targetName": [ - "AlternateName" - ], - "targetType": [ - "kind" - ] - }, "rule": { "include": [ "WithBaseline" @@ -346,14 +277,6 @@ spec: "name": "TestBaseline2" }, "spec": { - "binding": { - "targetName": [ - "AlternateName" - ], - "targetType": [ - "kind" - ] - }, "rule": { "include": [ "WithBaseline" @@ -376,4 +299,3 @@ An online version of this document is available at https://microsoft.github.io/P - Options - PSRule - Baseline -- Binding diff --git a/docs/concepts/PSRule/en-US/about_PSRule_Conventions.md b/docs/concepts/PSRule/en-US/about_PSRule_Conventions.md index 9198f34828..bcf5fd1ef7 100644 --- a/docs/concepts/PSRule/en-US/about_PSRule_Conventions.md +++ b/docs/concepts/PSRule/en-US/about_PSRule_Conventions.md @@ -70,7 +70,7 @@ Convention block limitations: - `End` can not use automatic variables except `$PSRule`. Most methods and properties of `$PSRule` are not available in `End`. -By default, the `Process` block used. +By default, the `Process` block is used. For example: ```powershell diff --git a/docs/concepts/PSRule/en-US/about_PSRule_Options.md b/docs/concepts/PSRule/en-US/about_PSRule_Options.md index af6db68f5a..0aabf41c42 100644 --- a/docs/concepts/PSRule/en-US/about_PSRule_Options.md +++ b/docs/concepts/PSRule/en-US/about_PSRule_Options.md @@ -14,6 +14,13 @@ This topic describes what options are available, when to and how to use them. The following workspace options are available for use: - [Baseline.Group](#baselinegroup) +- [Binding.Field](#bindingfield) +- [Binding.IgnoreCase](#bindingignorecase) +- [Binding.NameSeparator](#bindingnameseparator) +- [Binding.PreferTargetInfo](#bindingprefertargetinfo) +- [Binding.TargetName](#bindingtargetname) +- [Binding.TargetType](#bindingtargettype) +- [Binding.UseQualifiedName](#bindingusequalifiedname) - [Convention.Include](#conventioninclude) - [Execution.AliasReference](#executionaliasreference) - [Execution.Break](#executionbreak) @@ -60,13 +67,6 @@ The following workspace options are available for use: Additionally the following baseline options can be included: -- [Binding.Field](#bindingfield) -- [Binding.IgnoreCase](#bindingignorecase) -- [Binding.NameSeparator](#bindingnameseparator) -- [Binding.PreferTargetInfo](#bindingprefertargetinfo) -- [Binding.TargetName](#bindingtargetname) -- [Binding.TargetType](#bindingtargettype) -- [Binding.UseQualifiedName](#bindingusequalifiedname) - [Configuration](#configuration) - [Rule.Baseline](#rulebaseline) - [Rule.Include](#ruleinclude) diff --git a/docs/concepts/cli/module.md b/docs/concepts/cli/module.md index 337a718436..b595c4e885 100644 --- a/docs/concepts/cli/module.md +++ b/docs/concepts/cli/module.md @@ -67,6 +67,9 @@ Optional parameters: - `--version` - Specifies a specific version of the module to add. By default, the latest stable version of the module is added. Any required version constraints set by the `Requires` option are taken into consideration. +- `--prerelease` - Accept pre-release versions in addition to stable module versions. + By default, pre-release versions are not included. + A pre-release version may also be accepted when `Requires` includes pre-releases. For example: @@ -80,6 +83,12 @@ For example, a specific version of the module is added: ps-rule module add PSRule.Rules.Azure --version 1.32.1 ``` +For example, include pre-release versions added: + +```bash title="PSRule CLI command-line" +ps-rule module add PSRule.Rules.Azure --prerelease +``` + ## `module remove` Remove one or more modules from the lock file. @@ -112,7 +121,13 @@ ps-rule module restore --force ## `module upgrade` -Upgrade to the latest versions any modules within the lock file. +Upgrade to the latest versions for all or a specific module within the lock file. + +Optional parameters: + +- `--prerelease` - Accept pre-release versions in addition to stable module versions. + By default, pre-release versions are not included. + A pre-release version may also be accepted when `Requires` includes pre-releases. For example: @@ -120,6 +135,12 @@ For example: ps-rule module upgrade ``` +For example, upgrade a specific module and include pre-release versions: + +```bash title="PSRule CLI command-line" +ps-rule module upgrade PSRule.Rules.Azure --prerelease +``` + ## Next steps For more information on the module lock file, see [Lock file](../lockfile.md). diff --git a/docs/deprecations.md b/docs/deprecations.md index a2169559dc..b983ee0f29 100644 --- a/docs/deprecations.md +++ b/docs/deprecations.md @@ -95,6 +95,24 @@ From _v3_ these properties will be removed. These changes do not affect normal usage of PSRule. Supporting scripts that directly use the old names may not work correctly until you update these names. +### Binding configuration in baselines + +Prior to v3, a baseline could configure a binding configuration to modify how objects are recognized by name, type, and scope. +This existed to support scenarios before a module configuration and language scopes where core to how PSRule operates. + +- Rules within a module will automatically use binding configuration from the module configuration. + If no binding configuration is set, the configuration of the workspace will be used. +- Rules within the workspace will automatically use the binding configuration from options (`ps-rule.yaml`). + +Configuring binding configuration on a baseline is removed from PSRule v3. + +### Binding hooks + +Prior to v3, a custom binding PowerShell script block could be used to perform custom binding inline. +This feature was hard to use and obsolete for most common use cases. + +Alternatively, configure `Binding.TargetName` and `Binding.TargetType` options to use the built-in binder. + ## Deprecations for v2 ### Default baseline by module manifest diff --git a/docs/quickstart/standalone-rule.md b/docs/quickstart/standalone-rule.md index b7ebde4f3c..afc69d0f78 100644 --- a/docs/quickstart/standalone-rule.md +++ b/docs/quickstart/standalone-rule.md @@ -64,7 +64,7 @@ Before an object can be tested with PSRule, one or more rules must be defined. Each rule is defined in a file named with the suffix `.Rule.yaml`, `.Rule.jsonc`, or `.Rule.ps1`. Multiple rules can be defined in a single file. -A rule that fail on files with `.jpg` or `.png` extensions is shown in YAML, JSON, and PowerShell formats. +A rule that fails on files with `.jpg` or `.png` extensions is shown in YAML, JSON, and PowerShell formats. You only need to choose one format, however you can choose to create all three to try out each format. === "YAML" diff --git a/docs/scenarios/baselines/Baseline.rule.yaml b/docs/scenarios/baselines/Baseline.rule.yaml index 68ee127df9..d4737801fa 100644 --- a/docs/scenarios/baselines/Baseline.rule.yaml +++ b/docs/scenarios/baselines/Baseline.rule.yaml @@ -6,11 +6,6 @@ kind: Baseline metadata: name: TestBaseline1 spec: - binding: - targetName: - - AlternateName - targetType: - - kind rule: include: - 'WithBaseline' @@ -24,11 +19,6 @@ kind: Baseline metadata: name: TestBaseline2 spec: - binding: - targetName: - - AlternateName - targetType: - - kind rule: include: - 'WithBaseline' diff --git a/docs/upgrade-notes.md b/docs/upgrade-notes.md index b9e152560b..2317812f1e 100644 --- a/docs/upgrade-notes.md +++ b/docs/upgrade-notes.md @@ -62,6 +62,43 @@ From _v3.0.0_, the `module restore` command installs modules based on: [5]: concepts/cli/module.md [6]: concepts/lockfile.md +### Version and APIVersion accept stable + +Prior to _v3.0.0_, some usage of `version` and `apiVersion` accepted pre-release versions by default. +For example: + +```yaml +--- +# Synopsis: Any version example +apiVersion: github.com/microsoft/PSRule/v1 +kind: Selector +metadata: + name: PreviousAnyVersionExample +spec: + if: + field: dateVersion + apiVersion: '' +``` + +When `apiVersion` is empty any version is accepted including pre-releases. + +From _v3.0.0_ pre-release versions are not accepted by default. +Set the `includePrerelease` property to `true`. + +```yaml +--- +# Synopsis: Test comparison with apiVersion. +apiVersion: github.com/microsoft/PSRule/v1 +kind: Selector +metadata: + name: AnyVersion +spec: + if: + field: dateVersion + apiVersion: '' + includePrerelease: true +``` + ## Upgrading to v2.0.0 ### Resources naming restrictions diff --git a/requirements-docs.txt b/requirements-docs.txt index 54bce8a4ac..5e407ca941 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,9 +1,9 @@ mkdocs==1.6.1 -mkdocs-material==9.5.33 -pymdown-extensions==10.9 +mkdocs-material==9.5.42 +pymdown-extensions==10.11.2 mike==2.1.3 mkdocs-simple-hooks==0.1.5 -mkdocs-git-revision-date-localized-plugin==1.2.7 -mkdocs-git-committers-plugin-2==2.3.0 +mkdocs-git-revision-date-localized-plugin==1.3.0 +mkdocs-git-committers-plugin-2==2.4.1 mdx-truly-sane-lists==1.3 mkdocs-redirects==1.2.1 diff --git a/schemas/PSRule-language.schema.json b/schemas/PSRule-language.schema.json index 314457d697..f839d33d5a 100644 --- a/schemas/PSRule-language.schema.json +++ b/schemas/PSRule-language.schema.json @@ -133,9 +133,6 @@ "description": "A specification for a baseline.", "markdownDescription": "A specification for a baseline. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Baseline/)", "properties": { - "binding": { - "$ref": "#/definitions/binding-option" - }, "configuration": { "$ref": "#/definitions/configuration" }, diff --git a/schemas/PSRule-lock.schema.json b/schemas/PSRule-lock.schema.json index 543b7f430a..edee473fb1 100644 --- a/schemas/PSRule-lock.schema.json +++ b/schemas/PSRule-lock.schema.json @@ -23,6 +23,11 @@ "type": "string", "title": "Module version", "description": "The version of the module to use." + }, + "includePrerelease": { + "type": "boolean", + "title": "Include prerelease", + "description": "Accept pre-release versions in addition to stable module versions." } }, "required": [ diff --git a/src/PSRule.Badges/Properties/AssemblyInfo.cs b/src/PSRule.Badges/Properties/AssemblyInfo.cs index eb7f94bc53..efee02b75d 100644 --- a/src/PSRule.Badges/Properties/AssemblyInfo.cs +++ b/src/PSRule.Badges/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Runtime.CompilerServices; diff --git a/src/PSRule.Benchmark/PSRule.Benchmark.csproj b/src/PSRule.Benchmark/PSRule.Benchmark.csproj index c2433db01f..a6c2363039 100644 --- a/src/PSRule.Benchmark/PSRule.Benchmark.csproj +++ b/src/PSRule.Benchmark/PSRule.Benchmark.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/PSRule.Benchmark/packages.lock.json b/src/PSRule.Benchmark/packages.lock.json index 6e184a9165..ccf1bbaa6b 100644 --- a/src/PSRule.Benchmark/packages.lock.json +++ b/src/PSRule.Benchmark/packages.lock.json @@ -31,25 +31,26 @@ }, "Microsoft.PowerShell.SDK": { "type": "Direct", - "requested": "[7.4.5, )", - "resolved": "7.4.5", - "contentHash": "POa9ARY/dyJy+rivTJ9skJ+qwbN5prjVgdxkdxiU7r77PZjBpY8fq2W/vsDuA7AqkL7q7Cb7IbdxWnxJgaFSPQ==", + "requested": "[7.4.6, )", + "resolved": "7.4.6", + "contentHash": "fQb9dKdx+fD5vbJ5wjFngeJ1qSkJ91M3jy6bomVsrs1tLNnB06/8pm42Cv+6xhUVQGKV0JKMFy44+QjHl5puqQ==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.ObjectPool": "8.0.8", - "Microsoft.Management.Infrastructure.CimCmdlets": "7.4.5", - "Microsoft.PowerShell.Commands.Diagnostics": "7.4.5", - "Microsoft.PowerShell.Commands.Management": "7.4.5", - "Microsoft.PowerShell.Commands.Utility": "7.4.5", - "Microsoft.PowerShell.ConsoleHost": "7.4.5", - "Microsoft.PowerShell.Security": "7.4.5", - "Microsoft.WSMan.Management": "7.4.5", - "Microsoft.Windows.Compatibility": "8.0.8", + "Microsoft.Extensions.ObjectPool": "8.0.10", + "Microsoft.Management.Infrastructure.CimCmdlets": "7.4.6", + "Microsoft.PowerShell.Commands.Diagnostics": "7.4.6", + "Microsoft.PowerShell.Commands.Management": "7.4.6", + "Microsoft.PowerShell.Commands.Utility": "7.4.6", + "Microsoft.PowerShell.ConsoleHost": "7.4.6", + "Microsoft.PowerShell.Security": "7.4.6", + "Microsoft.WSMan.Management": "7.4.6", + "Microsoft.Windows.Compatibility": "8.0.10", "System.Data.SqlClient": "4.8.6", - "System.IO.Packaging": "8.0.0", - "System.Management.Automation": "7.4.5", + "System.IO.Packaging": "8.0.1", + "System.Management.Automation": "7.4.6", "System.Net.Http.WinHttpHandler": "8.0.2", "System.Private.ServiceModel": "4.10.3", + "System.Runtime.Caching": "8.0.1", "System.ServiceModel.Duplex": "4.10.3", "System.ServiceModel.Http": "4.10.3", "System.ServiceModel.NetTcp": "4.10.3", @@ -93,40 +94,36 @@ "resolved": "2.3.0", "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + }, "Iced": { "type": "Transitive", "resolved": "1.17.0", "contentHash": "8x+HCVTl/HHTGpscH3vMBhV8sknN/muZFw9s3TsI8SA6+c43cOTCi2+jE4KsU8pNLbJ++iF2ZFcpcXHXtDglnw==" }, - "JetBrains.Annotations": { - "type": "Transitive", - "resolved": "2021.2.0", - "contentHash": "kKSyoVfndMriKHLfYGmr0uzQuI4jcc3TKGyww7buJFCYeHb/X0kodYBPL7n9454q7v6ASiRmDgpPGaDGerg/Hg==" - }, "Json.More.Net": { "type": "Transitive", - "resolved": "1.9.3", - "contentHash": "BKIsKHXR2Jq+LdLdxPo3L09Lv0ld9xs1fAMvSAe2cf2YOl3at9vw0RrMlhC2ookDi7VtrgHXzc2Et5mVBOAUdw==", - "dependencies": { - "System.Text.Json": "6.0.2" - } + "resolved": "2.0.1.2", + "contentHash": "uF3QeiaXEfH92emz0/BWUiNtMSfxIIvgynuB0Bf1vF4s8eWTcZitBx9l+g/FDaJk5XxqBv9buQXizXKQcXFG1w==" }, "JsonPointer.Net": { "type": "Transitive", - "resolved": "3.0.3", - "contentHash": "mCGQc15lHLp1R2CVhWiipnZurHXm93+LbPPAT/vXQm5PdHt6WQuYLhaEF8VZ+aXL9P2I6bGND6pDTEfqFs6gig==", + "resolved": "5.0.0", + "contentHash": "fm4T5w20AY6C+p5/pJr0vrXRNGgtSfHl34I1LxC9zdPwS9S3j0GiR1Mz/CVPWKDXXGDpCt1APHpCq7kn5adCfA==", "dependencies": { - "Json.More.Net": "1.8.0" + "Humanizer.Core": "2.14.1", + "Json.More.Net": "2.0.1.2" } }, "JsonSchema.Net": { "type": "Transitive", - "resolved": "5.2.7", - "contentHash": "8un7Xq2MoKiWNo0HQtf2sPr3764W9NjNELIx3l9d3fIKEjg3tYtrZmxN+CgXKtzku4g52CqYUZuI+o0ue226vw==", + "resolved": "7.0.4", + "contentHash": "R0Hk2Tr/np4Q1NO8CBjyQsoiD1iFJyEQP20Sw7JnZCNGJoaSBe+g4b+nZqnBXPQhiqY5LGZ8JZwZkRh/eKZhEQ==", "dependencies": { - "JetBrains.Annotations": "2021.2.0", - "Json.More.Net": "1.9.0", - "JsonPointer.Net": "3.0.3" + "JsonPointer.Net": "5.0.0" } }, "Manatee.Json": { @@ -170,21 +167,21 @@ }, "Microsoft.CodeAnalysis.Common": { "type": "Transitive", - "resolved": "4.8.0", - "contentHash": "/jR+e/9aT+BApoQJABlVCKnnggGQbvGh7BKq2/wI1LamxC+LbzhcLj4Vj7gXCofl1n4E521YfF9w0WcASGg/KA==", + "resolved": "4.9.2", + "contentHash": "M5PThug7b2AdxL7xKmQs50KzAQTl9jENw5jMT3iUt16k+DAFlw1S87juU3UuPs3gvBm8trMBSOEvSFDr31c9Vw==", "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "3.3.4", - "System.Collections.Immutable": "7.0.0", - "System.Reflection.Metadata": "7.0.0", + "System.Collections.Immutable": "8.0.0", + "System.Reflection.Metadata": "8.0.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "Microsoft.CodeAnalysis.CSharp": { "type": "Transitive", - "resolved": "4.8.0", - "contentHash": "+3+qfdb/aaGD8PZRCrsdobbzGs1m9u119SkkJt8e/mk3xLJz/udLtS2T6nY27OTXxBBw10HzAbC8Z9w08VyP/g==", + "resolved": "4.9.2", + "contentHash": "HGIo7E9Mf3exAJbUdYpDFfLoYkSVaHDJXPyusWTYUTBaOPCowGw+Gap5McE1w+K+ryIXre72oiqL88sQHmHBmg==", "dependencies": { - "Microsoft.CodeAnalysis.Common": "[4.8.0]" + "Microsoft.CodeAnalysis.Common": "[4.9.2]" } }, "Microsoft.Diagnostics.NETCore.Client": { @@ -300,16 +297,16 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" }, "Microsoft.Extensions.Diagnostics": { "type": "Transitive", @@ -478,8 +475,8 @@ }, "Microsoft.Extensions.ObjectPool": { "type": "Transitive", - "resolved": "8.0.8", - "contentHash": "wnjTFjEvvSbOs3iMfl6CeJcUgPHZMYUB9uAQbGQGxGwVRl4GydNpMSkVntTzoi7AqQeYumU9yDSNeVbpq+ebow==" + "resolved": "8.0.10", + "contentHash": "u7gAG7JgxF8VSJUGPSudAcPxOt+ymJKQCSxNRxiuKV+klCQbHljQR75SilpedCTfhPWDhtUwIJpnDVtspr9nMg==" }, "Microsoft.Extensions.Options": { "type": "Transitive", @@ -518,10 +515,10 @@ }, "Microsoft.Management.Infrastructure.CimCmdlets": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "EFil5FH6/AaNXjpKfh97BaY9/jp7HbrPwjEpKxD3/4NsvilJExAfSTeCXo0nl6jGA0KuU9iSZmzLw+NqnvzIbA==", + "resolved": "7.4.6", + "contentHash": "pWlnAxpOLZf5lTX0SPX68dukSFowbEiGd39jRcO5rhsXDG5TjWrF3x1KxlFtMbiMGqJewVn5MJ7FlOWZDD7E3g==", "dependencies": { - "System.Management.Automation": "7.4.5" + "System.Management.Automation": "7.4.6" } }, "Microsoft.Management.Infrastructure.Runtime.Unix": { @@ -546,51 +543,49 @@ }, "Microsoft.PowerShell.Commands.Diagnostics": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "VART8UZ6cUXlEHGG+Elb7hn0TNL9+ISIOt8F0IxilmBbSTa79Ry3RFur+joWgTe2jLUHSpdHD5Nx1vmgfIdFUw==", + "resolved": "7.4.6", + "contentHash": "beys8wUfsWNda8JVr/Oj/Erx4PeC/AQC3X/TaU/O/BKKjzO1evm+Moz7wOOHb7bK3mqfGIWjsbOC9U/aRZGpGA==", "dependencies": { - "System.Management.Automation": "7.4.5" + "System.Management.Automation": "7.4.6" } }, "Microsoft.PowerShell.Commands.Management": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "O1qelje0o0/IcsS3U7rK8QW4H7WLZFo2WtIH07Nx31eX0CM/1MZrzGTOKE2c5KU/805HUsmZNltA0XJu/ba1dQ==", + "resolved": "7.4.6", + "contentHash": "IqM/FMnJMWpausi+ONG/eD2w7phwHG6SgKtLOG7anmlCiFOefjnQX2Jf3ihm7TYQJIFbxg05LtoIviyICJ8Wrw==", "dependencies": { - "Microsoft.PowerShell.Security": "7.4.5", - "System.ServiceProcess.ServiceController": "8.0.0" + "Microsoft.PowerShell.Security": "7.4.6", + "System.ServiceProcess.ServiceController": "8.0.1" } }, "Microsoft.PowerShell.Commands.Utility": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "5VcmxTv8LKyI2CrBVzl++o1Lc0nWth3WqhdTCUOHwWe69JWtsKIyTJk6YXx/tA8d9cJ9GXbtnZLQ2LC+IL0t6Q==", + "resolved": "7.4.6", + "contentHash": "dqu5AGtk/MH73osSK6on14X3a1TfBolKDnPMXM5EC6vo1XtpDL70MFoomypwgwkxz5B29p2+gtVoGqiQz5cicQ==", "dependencies": { - "Json.More.Net": "1.9.3", - "JsonSchema.Net": "5.2.7", + "JsonSchema.Net": "7.0.4", "Markdig.Signed": "0.33.0", - "Microsoft.CodeAnalysis.CSharp": "4.8.0", + "Microsoft.CodeAnalysis.CSharp": "4.9.2", "Microsoft.PowerShell.MarkdownRender": "7.2.1", - "System.Drawing.Common": "8.0.8", - "System.Management.Automation": "7.4.5", - "System.Text.Json": "8.0.4", - "System.Threading.AccessControl": "8.0.0" + "System.Drawing.Common": "9.0.0-preview.6.24327.6", + "System.Management.Automation": "7.4.6", + "System.Threading.AccessControl": "9.0.0-preview.6.24327.7" } }, "Microsoft.PowerShell.ConsoleHost": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "VOfuT4zf2lecjOf4ppyWaBj7as9rk+T3n5enGv/wb8Wrc4xRRp/fO/CZNjBFqE1jsWqZgghTWpIsAdgr2oMYPA==", + "resolved": "7.4.6", + "contentHash": "LOMd4Zl4rsf3gHHwDf0L8iFdvLgHBACMQlgS3viz0L49N2o+0v5TAJSLOpjg77lcuH43rKLfjN+aoEen78iUBQ==", "dependencies": { - "System.Management.Automation": "7.4.5" + "System.Management.Automation": "7.4.6" } }, "Microsoft.PowerShell.CoreCLR.Eventing": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "yygq1fnGM1cYINM9XfsWUo4K2AAWuKFM/IkTGNlhAMIvxdmPgawgzb9Ygm4u1KNrjdFiJEXsqKqQsMxE9yOwhw==", + "resolved": "7.4.6", + "contentHash": "z8XYESoGwJYT/L60192dE+n1dS7HRvbh2VinadhD3/zA6fDC/lu0VhQc42c56K4JiIMsLfHUUtaPJEa1JPaNBg==", "dependencies": { - "System.Diagnostics.EventLog": "8.0.0" + "System.Diagnostics.EventLog": "8.0.1" } }, "Microsoft.PowerShell.MarkdownRender": { @@ -608,10 +603,10 @@ }, "Microsoft.PowerShell.Security": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "slQplgdH36PvPpv4YZy0srkj3eKvENBmk1Rt/UHu/D0Eio4TIBNRqVLb3Y7824YE/VGuC4DBmQl6f6/UyCbZ2g==", + "resolved": "7.4.6", + "contentHash": "62WzHhtzItjTMbEV88J5zL65/1HIZspEoWGwFAAi9ajoUzJOZUtdGyBQPa7ZsxszPk/Ktk+h3QtW0hwBVdrrPg==", "dependencies": { - "System.Management.Automation": "7.4.5" + "System.Management.Automation": "7.4.6" } }, "Microsoft.Security.Extensions": { @@ -650,37 +645,37 @@ }, "Microsoft.Win32.SystemEvents": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "9opKRyOKMCi2xJ7Bj7kxtZ1r9vbzosMvRrdEhVhDz8j8MoBGgB+WmC94yH839NPH+BclAjtQ/pyagvi/8gDLkw==" + "resolved": "9.0.0-preview.6.24327.7", + "contentHash": "iY1g4tiJLFzV+Ygp+g78w8sNMkDLiEY9nYnZVhVto+lAt6UVzw5ZqwRvLGDPzyrV9J1D/MKchmnziLkL8Ro6hA==" }, "Microsoft.Windows.Compatibility": { "type": "Transitive", - "resolved": "8.0.8", - "contentHash": "9FWk4D5mNFz99rT7NvsqrDdT/6bjkNtxo7CPwajyudBTMXi7kro8ixr5pUnFYrhUBfimR+OUGDYk/se4NxJ6sA==", + "resolved": "8.0.10", + "contentHash": "V92Ri/nR0VqFT6vAVGj20sl0GI6tEFlZiB1IENyPdSdjs+1k5O1lr4vVwtIwoutlib8UyO8tnBwngT6SoOqvyA==", "dependencies": { "Microsoft.Win32.Registry.AccessControl": "8.0.0", "Microsoft.Win32.SystemEvents": "8.0.0", "System.CodeDom": "8.0.0", "System.ComponentModel.Composition": "8.0.0", "System.ComponentModel.Composition.Registration": "8.0.0", - "System.Configuration.ConfigurationManager": "8.0.0", - "System.Data.Odbc": "8.0.0", - "System.Data.OleDb": "8.0.0", + "System.Configuration.ConfigurationManager": "8.0.1", + "System.Data.Odbc": "8.0.1", + "System.Data.OleDb": "8.0.1", "System.Data.SqlClient": "4.8.6", - "System.Diagnostics.EventLog": "8.0.0", - "System.Diagnostics.PerformanceCounter": "8.0.0", + "System.Diagnostics.EventLog": "8.0.1", + "System.Diagnostics.PerformanceCounter": "8.0.1", "System.DirectoryServices": "8.0.0", - "System.DirectoryServices.AccountManagement": "8.0.0", + "System.DirectoryServices.AccountManagement": "8.0.1", "System.DirectoryServices.Protocols": "8.0.0", - "System.Drawing.Common": "8.0.8", - "System.IO.Packaging": "8.0.0", + "System.Drawing.Common": "8.0.10", + "System.IO.Packaging": "8.0.1", "System.IO.Ports": "8.0.0", "System.Management": "8.0.0", "System.Reflection.Context": "8.0.0", - "System.Runtime.Caching": "8.0.0", - "System.Security.Cryptography.Pkcs": "8.0.0", + "System.Runtime.Caching": "8.0.1", + "System.Security.Cryptography.Pkcs": "8.0.1", "System.Security.Cryptography.ProtectedData": "8.0.0", - "System.Security.Cryptography.Xml": "8.0.1", + "System.Security.Cryptography.Xml": "8.0.2", "System.Security.Permissions": "8.0.0", "System.ServiceModel.Duplex": "4.10.0", "System.ServiceModel.Http": "4.10.0", @@ -688,7 +683,7 @@ "System.ServiceModel.Primitives": "4.10.0", "System.ServiceModel.Security": "4.10.0", "System.ServiceModel.Syndication": "8.0.0", - "System.ServiceProcess.ServiceController": "8.0.0", + "System.ServiceProcess.ServiceController": "8.0.1", "System.Speech": "8.0.0", "System.Text.Encoding.CodePages": "8.0.0", "System.Threading.AccessControl": "8.0.0", @@ -697,18 +692,18 @@ }, "Microsoft.WSMan.Management": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "6Rksg7i6GSctvvsB3P7uhU8ub9lVAPa/zsPn1GrZ8eLSApZVZqO4zQFMuylCosXsn9p8h/ktytFSpM2u9k8ujw==", + "resolved": "7.4.6", + "contentHash": "bBEx8+wstdxpsXjqkOPvVq53PwCksDWb7VtyzXjzLdMGY33gPo8H8P3OACUluOcMG/Kd7rPnBszsP7CFhSkKSg==", "dependencies": { - "Microsoft.WSMan.Runtime": "7.4.5", - "System.Management.Automation": "7.4.5", - "System.ServiceProcess.ServiceController": "8.0.0" + "Microsoft.WSMan.Runtime": "7.4.6", + "System.Management.Automation": "7.4.6", + "System.ServiceProcess.ServiceController": "8.0.1" } }, "Microsoft.WSMan.Runtime": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "vXoYVMP88tJ/EOkIB8/vFSc+aXjA+creJFGFIpFpLruSNDPjWHFHplnIkMXmgzHi5JITGSDt3JTz22e21qJhIg==" + "resolved": "7.4.6", + "contentHash": "pnNVcVUT+CP7r23Ju/+nhp0fLSdwevAZwe3qe8XQEahYOUv9ACIP29GijRsjdOwIL8+7DaLUQF5jjh+P/ZjGTQ==" }, "NETStandard.Library": { "type": "Transitive", @@ -1003,8 +998,8 @@ }, "System.Collections.Immutable": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==" + "resolved": "8.0.0", + "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==" }, "System.ComponentModel.Annotations": { "type": "Transitive", @@ -1027,10 +1022,10 @@ }, "System.Configuration.ConfigurationManager": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==", + "resolved": "8.0.1", + "contentHash": "gPYFPDyohW2gXNhdQRSjtmeS6FymL2crg4Sral1wtvEJ7DUqFCDWDVbbLobASbzxfic8U1hQEdC7hmg9LHncMw==", "dependencies": { - "System.Diagnostics.EventLog": "8.0.0", + "System.Diagnostics.EventLog": "8.0.1", "System.Security.Cryptography.ProtectedData": "8.0.0" } }, @@ -1048,19 +1043,16 @@ }, "System.Data.Odbc": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c+GfnZt2/HyU+voKw2fctLZClcNjPZPWS+mnIhGvDknRMqL/fwWlREWPgA4csbp9ZkQIgB4qkufgdh/oh5Ubow==", - "dependencies": { - "System.Text.Encoding.CodePages": "8.0.0" - } + "resolved": "8.0.1", + "contentHash": "JQd0QHOaZuH+ki+4Geas88dnLe/lZSaEYYmRdovZaqNVuExVlVFs/of2I1VaasMxzbO5+yrGDAP2rkazx/b8Sg==" }, "System.Data.OleDb": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "FpUTcQ0E8mFvcYp8UZA3NX8wgmhmsCue56g1zfkr1xdOnT5FrYYmC5DWQ9xCw8o8zuxVBKLZvliqEGgmeoalaQ==", + "resolved": "8.0.1", + "contentHash": "RO+/y2ggU5956uQDRXdjA1e2l5yJ4rTWNX76eZ+3sgtYGqGapCe2kQCyiUci+/y6Fyb21Irp4RQEdfrIiuYrxQ==", "dependencies": { - "System.Configuration.ConfigurationManager": "8.0.0", - "System.Diagnostics.PerformanceCounter": "8.0.0" + "System.Configuration.ConfigurationManager": "8.0.1", + "System.Diagnostics.PerformanceCounter": "8.0.1" } }, "System.Data.SqlClient": { @@ -1090,15 +1082,15 @@ }, "System.Diagnostics.EventLog": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A==" + "resolved": "8.0.1", + "contentHash": "n1ZP7NM2Gkn/MgD8+eOT5MulMj6wfeQMNS2Pizvq5GHCZfjlFMXV2irQlQmJhwA2VABC57M0auudO89Iu2uRLg==" }, "System.Diagnostics.PerformanceCounter": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "lX6DXxtJqVGWw7N/QmVoiCyVQ+Q/Xp+jVXPr3gLK1jJExSn1qmAjJQeb8gnOYeeBTG3E3PmG1nu92eYj/TEjpg==", + "resolved": "8.0.1", + "contentHash": "9RfEDiEjlUADeThs8IPdDVTXSnPRSqjfgTQJALpmGFPKC0k2mbdufOXnb/9JZ4I0TkmxOfy3VTJxrHOJSs8cXg==", "dependencies": { - "System.Configuration.ConfigurationManager": "8.0.0" + "System.Configuration.ConfigurationManager": "8.0.1" } }, "System.Diagnostics.Tools": { @@ -1128,10 +1120,10 @@ }, "System.DirectoryServices.AccountManagement": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "dCT8BYeeisx0IzAf6x+FSVWK3gz2fKI9pgLV16c7dY/lckw4aodNrgXqsFqyqJN5Kfxc3oklG+SCMYkRfg1V7A==", + "resolved": "8.0.1", + "contentHash": "qVDWKClyDY+rHVEnf11eU4evW25d5OeidrtMPSJv+fwG213wa2zJ+AuIFCxsuvNSCFyHo+DvQIVfBcoK3CL1pA==", "dependencies": { - "System.Configuration.ConfigurationManager": "8.0.0", + "System.Configuration.ConfigurationManager": "8.0.1", "System.DirectoryServices": "8.0.0", "System.DirectoryServices.Protocols": "8.0.0" } @@ -1143,10 +1135,10 @@ }, "System.Drawing.Common": { "type": "Transitive", - "resolved": "8.0.8", - "contentHash": "4ZM1wvLjz9nVVExsfPAaSl/qOvU+QNedJL5rQ+2Wbow+iGeyO0e7XN07707rMBgaffEeeLrCZBwC0oHUuvRdPw==", + "resolved": "9.0.0-preview.6.24327.6", + "contentHash": "4mNJBnN4iroaz44NIcYVKSPgOqhBTsPHqBG5aTfSjsKWCkRzhKnkVsWuYzx4FrZoEToGveMvUCanlY/V7GUZ5A==", "dependencies": { - "Microsoft.Win32.SystemEvents": "8.0.0" + "Microsoft.Win32.SystemEvents": "9.0.0-preview.6.24327.7" } }, "System.Formats.Asn1": { @@ -1263,8 +1255,8 @@ }, "System.IO.Packaging": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "8g1V4YRpdGAxFcK8v9OjuMdIOJSpF30Zb1JGicwVZhly3I994WFyBdV6mQEo8d3T+URQe55/M0U0eIH0Hts1bg==" + "resolved": "8.0.1", + "contentHash": "KYkIOAvPexQOLDxPO2g0BVoWInnQhPpkFzRqvNrNrMhVT6kqhVr0zEb6KCHlptLFukxnZrjuMVAnxK7pOGUYrw==" }, "System.IO.Ports": { "type": "Transitive", @@ -1320,23 +1312,23 @@ }, "System.Management.Automation": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "M8diPEl3ycy5juoSwCQkJVyiG6pHjv9IPD/bvqcnrQfAsbYzqDLVQLzfurHVrQHKvqAWB4k3xwtHMyYjG8xR9Q==", + "resolved": "7.4.6", + "contentHash": "RxrDUOJuLC7Sd2RaUHf52ujP5VpdfSVTv75nwgEVAROCfYV4IcSd5mL4mLrKa+6LjkXGli7aUs853UyCwidl1A==", "dependencies": { "Microsoft.ApplicationInsights": "2.21.0", "Microsoft.Management.Infrastructure": "3.0.0", - "Microsoft.PowerShell.CoreCLR.Eventing": "7.4.5", + "Microsoft.PowerShell.CoreCLR.Eventing": "7.4.6", "Microsoft.PowerShell.Native": "7.4.0", "Microsoft.Security.Extensions": "1.2.0", "Microsoft.Win32.Registry.AccessControl": "8.0.0", "Newtonsoft.Json": "13.0.3", - "System.Configuration.ConfigurationManager": "8.0.0", + "System.Configuration.ConfigurationManager": "8.0.1", "System.Diagnostics.DiagnosticSource": "8.0.1", "System.DirectoryServices": "8.0.0", "System.Formats.Asn1": "8.0.1", "System.Management": "8.0.0", "System.Security.AccessControl": "6.0.1", - "System.Security.Cryptography.Pkcs": "8.0.0", + "System.Security.Cryptography.Pkcs": "8.0.1", "System.Security.Permissions": "8.0.0", "System.Text.Encoding.CodePages": "8.0.0" } @@ -1501,10 +1493,10 @@ }, "System.Reflection.Metadata": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "MclTG61lsD9sYdpNz9xsKBzjsmsfCtcMZYXz/IUr2zlhaTaABonlr1ESeompTgM+Xk+IwtGYU7/voh3YWB/fWw==", + "resolved": "8.0.0", + "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==", "dependencies": { - "System.Collections.Immutable": "7.0.0" + "System.Collections.Immutable": "8.0.0" } }, "System.Reflection.Primitives": { @@ -1549,10 +1541,10 @@ }, "System.Runtime.Caching": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "4TmlmvGp4kzZomm7J2HJn6IIx0UUrQyhBDyb5O1XiunZlQImXW+B8b7W/sTPcXhSf9rp5NR5aDtQllwbB5elOQ==", + "resolved": "8.0.1", + "contentHash": "tdl7Q47P09UpRu0C/OQsGJU6GacBzzk4vfp5My9rodD+BchrxmajORnTthH8RxPUTPrIoVDJmLyvJcGxB267nQ==", "dependencies": { - "System.Configuration.ConfigurationManager": "8.0.0" + "System.Configuration.ConfigurationManager": "8.0.1" } }, "System.Runtime.CompilerServices.Unsafe": { @@ -1723,11 +1715,8 @@ }, "System.Security.Cryptography.Pkcs": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "ULmp3xoOwNYjOYp4JZ2NK/6NdTgiN1GQXzVVN1njQ7LOZ0d0B9vyMnhyqbIi9Qw4JXj1JgCsitkTShboHRx7Eg==", - "dependencies": { - "System.Formats.Asn1": "8.0.0" - } + "resolved": "8.0.1", + "contentHash": "CoCRHFym33aUSf/NtWSVSZa99dkd0Hm7OCZUxORBjRB16LNhIEOf8THPqzIYlvKM0nNDAPTRBa1FxEECrgaxxA==" }, "System.Security.Cryptography.Primitives": { "type": "Transitive", @@ -1782,10 +1771,10 @@ }, "System.Security.Cryptography.Xml": { "type": "Transitive", - "resolved": "8.0.1", - "contentHash": "hqu2ztecOf3BYg5q1R7QcyliX9L7r3mfsWtaRitAxcezH8hyZMB7zCmhi186hsUZXk1KxsAHXwyPEW+xvUED6g==", + "resolved": "8.0.2", + "contentHash": "aDM/wm0ZGEZ6ZYJLzgqjp2FZdHbDHh6/OmpGfb7AdZ105zYmPn/83JRU2xLIbwgoNz9U1SLUTJN0v5th3qmvjA==", "dependencies": { - "System.Security.Cryptography.Pkcs": "8.0.0" + "System.Security.Cryptography.Pkcs": "8.0.1" } }, "System.Security.Permissions": { @@ -1852,10 +1841,10 @@ }, "System.ServiceProcess.ServiceController": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "jtYVG3bpw2n/NvNnP2g/JLri0D4UtfusTvLeH6cZPNAEjJXJVGspS3wLgVvjNbm+wjaYkFgsXejMTocV1T5DIQ==", + "resolved": "8.0.1", + "contentHash": "02I0BXo1kmMBgw03E8Hu4K6nTqur4wpQdcDZrndczPzY2fEoGvlinE35AWbyzLZ2h2IksEZ6an4tVt3hi9j1oA==", "dependencies": { - "System.Diagnostics.EventLog": "8.0.0" + "System.Diagnostics.EventLog": "8.0.1" } }, "System.Speech": { @@ -1896,11 +1885,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "8.0.4", - "contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==", - "dependencies": { - "System.Text.Encodings.Web": "8.0.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading": { "type": "Transitive", @@ -1913,8 +1899,8 @@ }, "System.Threading.AccessControl": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cIed5+HuYz+eV9yu9TH95zPkqmm1J9Qps9wxjB335sU8tsqc2kGdlTEH9FZzZeCS8a7mNSEsN8ZkyhQp1gfdEw==" + "resolved": "9.0.0-preview.6.24327.7", + "contentHash": "t7e5cLBMvBx9/YhNsCp8W8iUw7geh08y0GKFawfJUD5YLgx6AjO2D497+0qHbXRQGpl2uxBGmkWKnCZ5azILZQ==" }, "System.Threading.Tasks": { "type": "Transitive", @@ -2017,13 +2003,13 @@ "type": "Project", "dependencies": { "Manatee.Json": "[13.0.5, )", - "Microsoft.Extensions.DependencyInjection": "[8.0.0, )", + "Microsoft.Extensions.DependencyInjection": "[8.0.1, )", "Microsoft.Extensions.Hosting": "[8.0.0, )", "Microsoft.PSRule.Badges": "[0.0.1, )", "Microsoft.PSRule.Types": "[0.0.1, )", "Sarif.Sdk": "[2.4.16, )", "System.Net.Http": "[4.3.4, )", - "System.Text.Json": "[8.0.4, )" + "System.Text.Json": "[8.0.5, )" } }, "Microsoft.PSRule.Types": { diff --git a/src/PSRule.BuildTool/PSRule.BuildTool.csproj b/src/PSRule.BuildTool/PSRule.BuildTool.csproj index 16a4b445d5..056524ac69 100644 --- a/src/PSRule.BuildTool/PSRule.BuildTool.csproj +++ b/src/PSRule.BuildTool/PSRule.BuildTool.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/PSRule.BuildTool/packages.lock.json b/src/PSRule.BuildTool/packages.lock.json index 0ca2af7466..049be9458e 100644 --- a/src/PSRule.BuildTool/packages.lock.json +++ b/src/PSRule.BuildTool/packages.lock.json @@ -30,9 +30,9 @@ }, "System.Drawing.Common": { "type": "Direct", - "requested": "[8.0.8, )", - "resolved": "8.0.8", - "contentHash": "4ZM1wvLjz9nVVExsfPAaSl/qOvU+QNedJL5rQ+2Wbow+iGeyO0e7XN07707rMBgaffEeeLrCZBwC0oHUuvRdPw==", + "requested": "[8.0.10, )", + "resolved": "8.0.10", + "contentHash": "MdajRp3P+FOlThgY6FBjAqnmLiVl5t2yWEC/2AsDMqx1zYbJG3G5TnscFBQ4obqcaGqvN5UnhQHSFaJFG2HftQ==", "dependencies": { "Microsoft.Win32.SystemEvents": "8.0.0" } diff --git a/src/PSRule.CommandLine/Commands/ModuleCommand.cs b/src/PSRule.CommandLine/Commands/ModuleCommand.cs index b92661f80b..58430a26b3 100644 --- a/src/PSRule.CommandLine/Commands/ModuleCommand.cs +++ b/src/PSRule.CommandLine/Commands/ModuleCommand.cs @@ -5,7 +5,9 @@ using System.Diagnostics.CodeAnalysis; using System.Management.Automation; using Newtonsoft.Json; +using NuGet.Common; using NuGet.Configuration; +using NuGet.Packaging; using NuGet.Protocol; using NuGet.Protocol.Core.Types; using NuGet.Versioning; @@ -15,9 +17,6 @@ using PSRule.Data; using PSRule.Pipeline.Dependencies; using SemanticVersion = PSRule.Data.SemanticVersion; -using NuGet.Packaging; -using NuGet.Common; -using PSRule.Pipeline; namespace PSRule.CommandLine.Commands; @@ -51,7 +50,7 @@ public static async Task ModuleRestoreAsync(RestoreOptions operationOptions var requires = clientContext.Option.Requires.ToDictionary(); var file = LockFile.Read(null); - using var pwsh = PowerShell.Create(); + using var pwsh = CreatePowerShell(); // Restore from the lock file. foreach (var kv in file.Modules) @@ -70,9 +69,11 @@ public static async Task ModuleRestoreAsync(RestoreOptions operationOptions var idealVersion = await FindVersionAsync(module, null, targetVersion, null, cancellationToken); if (idealVersion != null) - await InstallVersionAsync(clientContext, module, idealVersion.ToString(), cancellationToken); + { + installedVersion = await InstallVersionAsync(clientContext, module, idealVersion, cancellationToken); + } - if (pwsh.HadErrors || (idealVersion == null && installedVersion == null)) + if (pwsh.HadErrors || idealVersion == null || installedVersion == null) { exitCode = ERROR_MODULE_FAILED_TO_INSTALL; clientContext.LogError(Messages.Error_501, module, targetVersion); @@ -98,7 +99,7 @@ public static async Task ModuleRestoreAsync(RestoreOptions operationOptions // Check if the installed version matches the constraint. if (IsInstalled(pwsh, includeModule, null, out var installedVersion) && !operationOptions.Force && - (moduleConstraint == null || moduleConstraint.Equals(installedVersion))) + (moduleConstraint == null || moduleConstraint.Accepts(installedVersion))) { // invocation.Log(Messages.UsingModule, includeModule, installedVersion.ToString()); clientContext.LogVerbose($"The module {includeModule} is already installed."); @@ -109,7 +110,7 @@ public static async Task ModuleRestoreAsync(RestoreOptions operationOptions var idealVersion = await FindVersionAsync(includeModule, moduleConstraint, null, null, cancellationToken); if (idealVersion != null) { - await InstallVersionAsync(clientContext, includeModule, idealVersion.ToString(), cancellationToken); + await InstallVersionAsync(clientContext, includeModule, idealVersion, cancellationToken); } else if (idealVersion == null) { @@ -143,8 +144,7 @@ public static async Task ModuleInitAsync(ModuleOptions operationOptions, Cl var exitCode = 0; var requires = clientContext.Option.Requires.ToDictionary(); var file = !operationOptions.Force ? LockFile.Read(null) : new LockFile(); - - using var pwsh = PowerShell.Create(); + using var pwsh = CreatePowerShell(); // Add for any included modules. if (clientContext.Option?.Include?.Module != null && clientContext.Option.Include.Module.Length > 0) @@ -197,8 +197,7 @@ public static async Task ModuleListAsync(ModuleOptions operationOptions, Cl var exitCode = 0; var requires = clientContext.Option.Requires.ToDictionary(); var file = LockFile.Read(null); - - var pwsh = PowerShell.Create(); + var pwsh = CreatePowerShell(); if (exitCode == 0) { @@ -218,19 +217,19 @@ public static async Task ModuleAddAsync(ModuleOptions operationOptions, Cli var requires = clientContext.Option.Requires.ToDictionary(); var file = LockFile.Read(null); - using var pwsh = PowerShell.Create(); + using var pwsh = CreatePowerShell(); foreach (var module in operationOptions.Module) { if (!file.Modules.TryGetValue(module, out var item) || operationOptions.Force) { // Get a constraint if set from options. - var moduleConstraint = requires.TryGetValue(module, out var c) ? c : null; + var moduleConstraint = requires.TryGetValue(module, out var c) ? c : ModuleConstraint.Any(module, includePrerelease: operationOptions.Prerelease); // Get target version if specified in command-line. var targetVersion = !string.IsNullOrEmpty(operationOptions.Version) && SemanticVersion.TryParseVersion(operationOptions.Version, out var v) && v != null ? v : null; // Check if the target version is valid with the constraint if set. - if (targetVersion != null && moduleConstraint != null && !moduleConstraint.Constraint.Equals(targetVersion)) + if (targetVersion != null && moduleConstraint != null && !moduleConstraint.Constraint.Accepts(targetVersion)) { clientContext.LogError(Messages.Error_503, operationOptions.Version!); return ERROR_MODULE_ADD_VIOLATES_CONSTRAINT; @@ -250,7 +249,8 @@ public static async Task ModuleAddAsync(ModuleOptions operationOptions, Cli clientContext.LogVerbose(Messages.UsingModule, module, idealVersion.ToString()); item = new LockEntry { - Version = idealVersion + Version = idealVersion, + IncludePrerelease = operationOptions.Prerelease && !idealVersion.Stable ? true : null, }; file.Modules[module] = item; } @@ -281,7 +281,7 @@ public static async Task ModuleRemoveAsync(ModuleOptions operationOptions, var file = LockFile.Read(null); - using var pwsh = PowerShell.Create(); + using var pwsh = CreatePowerShell(); foreach (var module in operationOptions.Module) { if (file.Modules.TryGetValue(module, out var constraint)) @@ -311,12 +311,13 @@ public static async Task ModuleUpgradeAsync(ModuleOptions operationOptions, var exitCode = 0; var requires = clientContext.Option.Requires.ToDictionary(); var file = LockFile.Read(null); + var filteredModules = operationOptions.Module != null && operationOptions.Module.Length > 0 ? new HashSet(operationOptions.Module, StringComparer.OrdinalIgnoreCase) : null; - using var pwsh = PowerShell.Create(); - foreach (var kv in file.Modules) + using var pwsh = CreatePowerShell(); + foreach (var kv in file.Modules.Where(m => filteredModules == null || filteredModules.Contains(m.Key))) { // Get a constraint if set from options. - var moduleConstraint = requires.TryGetValue(kv.Key, out var c) ? c : null; + var moduleConstraint = requires.TryGetValue(kv.Key, out var c) ? c : ModuleConstraint.Any(kv.Key, includePrerelease: kv.Value.IncludePrerelease ?? operationOptions.Prerelease); // Find the ideal version. var idealVersion = await FindVersionAsync(kv.Key, moduleConstraint, null, null, cancellationToken); @@ -332,6 +333,7 @@ public static async Task ModuleUpgradeAsync(ModuleOptions operationOptions, clientContext.LogVerbose(Messages.UsingModule, kv.Key, idealVersion.ToString()); kv.Value.Version = idealVersion; + kv.Value.IncludePrerelease = (kv.Value.IncludePrerelease.GetValueOrDefault(false) || operationOptions.Prerelease) && !idealVersion.Stable ? true : null; file.Modules[kv.Key] = kv.Value; } @@ -408,7 +410,7 @@ private static bool IsInstalled(PowerShell pwsh, string module, SemanticVersion. versionString != null && SemanticVersion.TryParseVersion(versionString, out var v) && v != null && - (targetVersion == null || targetVersion.Equals(v)) && + (targetVersion == null || targetVersion.CompareTo(v) == 0) && v.CompareTo(installedVersion) > 0) installedVersion = v; } @@ -452,8 +454,8 @@ private static bool TryPrivateData(PSModuleInfo info, string propertyName, out H if (version.ToFullString() is string versionString && SemanticVersion.TryParseVersion(versionString, out var v) && v != null && - (constraint == null || constraint.Constraint.Equals(v)) && - (targetVersion == null || targetVersion.Equals(v)) && + (constraint == null || constraint.Accepts(v)) && + (targetVersion == null || targetVersion.CompareTo(v) == 0) && v.CompareTo(result) > 0 && v.CompareTo(installedVersion) > 0) result = v; @@ -461,24 +463,26 @@ private static bool TryPrivateData(PSModuleInfo info, string propertyName, out H return result; } - private static async Task InstallVersionAsync([DisallowNull] ClientContext context, [DisallowNull] string name, [DisallowNull] string version, CancellationToken cancellationToken) + private static async Task InstallVersionAsync([DisallowNull] ClientContext context, [DisallowNull] string name, [DisallowNull] SemanticVersion.Version version, CancellationToken cancellationToken) { context.LogVerbose(Messages.RestoringModule, name, version); var cache = new SourceCacheContext(); var logger = new NullLogger(); var resource = await GetSourceRepositoryAsync(); + var stringVersion = version.ToString(); - var packageVersion = new NuGetVersion(version); + var packageVersion = new NuGetVersion(stringVersion); using var packageStream = new MemoryStream(); - await resource.CopyNupkgToStreamAsync( + if (!await resource.CopyNupkgToStreamAsync( name, packageVersion, packageStream, cache, logger, - cancellationToken); + cancellationToken)) + return null; using var packageReader = new PackageArchiveReader(packageStream); var nuspecReader = await packageReader.GetNuspecReaderAsync(cancellationToken); @@ -489,6 +493,7 @@ await resource.CopyNupkgToStreamAsync( if (Directory.Exists(modulePath)) Directory.Delete(modulePath, true); + var count = 0; var files = packageReader.GetFiles(); packageReader.CopyFiles(modulePath, files, (name, targetPath, s) => { @@ -496,10 +501,18 @@ await resource.CopyNupkgToStreamAsync( return null; s.CopyToFile(targetPath); + count++; return targetPath; }, logger, cancellationToken); + + // Check module path exists. + if (!Directory.Exists(modulePath)) + return null; + + context.LogVerbose("Module saved to: {0} -- {1}", name, modulePath); + return count > 0 ? version : null; } private static async Task GetSourceRepositoryAsync() @@ -509,9 +522,9 @@ private static async Task GetSourceRepositoryAsync() return await repository.GetResourceAsync(); } - private static string GetModulePath(ClientContext context, string name, string version) + private static string GetModulePath(ClientContext context, string name, [DisallowNull] SemanticVersion.Version version) { - return Path.Combine(context.CachePath, MODULES_PATH, name, version); + return Path.Combine(context.CachePath, MODULES_PATH, name, version.ToShortString()); } private static bool ShouldIgnorePackageFile(string name) @@ -520,5 +533,10 @@ private static bool ShouldIgnorePackageFile(string name) string.Equals(name, "_rels/.rels", StringComparison.OrdinalIgnoreCase); } + private static PowerShell CreatePowerShell() + { + return PowerShell.Create(); + } + #endregion Helper methods } diff --git a/src/PSRule.CommandLine/Models/ModuleOptions.cs b/src/PSRule.CommandLine/Models/ModuleOptions.cs index 06289d1477..0fcbddf1d5 100644 --- a/src/PSRule.CommandLine/Models/ModuleOptions.cs +++ b/src/PSRule.CommandLine/Models/ModuleOptions.cs @@ -4,32 +4,37 @@ namespace PSRule.CommandLine.Models; /// -/// +/// Options for the module command. /// public sealed class ModuleOptions { /// - /// + /// A specific path to use for the operation. /// public string[]? Path { get; set; } /// - /// + /// The name of any specified modules. /// public string[]? Module { get; set; } /// - /// + /// Determines if the module is overridden if it already exists. /// public bool Force { get; set; } /// - /// + /// The target module version. /// public string? Version { get; set; } /// - /// + /// Determines if verification that the module exists is skipped. /// public bool SkipVerification { get; set; } + + /// + /// Accept pre-release versions in addition to stable module versions. + /// + public bool Prerelease { get; set; } } diff --git a/src/PSRule.CommandLine/PSRule.CommandLine.csproj b/src/PSRule.CommandLine/PSRule.CommandLine.csproj index 45f127ba91..5145dc7100 100644 --- a/src/PSRule.CommandLine/PSRule.CommandLine.csproj +++ b/src/PSRule.CommandLine/PSRule.CommandLine.csproj @@ -14,8 +14,8 @@ - - + + all diff --git a/src/PSRule.CommandLine/packages.lock.json b/src/PSRule.CommandLine/packages.lock.json index 1b571fb0d5..516b743ee0 100644 --- a/src/PSRule.CommandLine/packages.lock.json +++ b/src/PSRule.CommandLine/packages.lock.json @@ -10,25 +10,26 @@ }, "Microsoft.PowerShell.SDK": { "type": "Direct", - "requested": "[7.4.5, )", - "resolved": "7.4.5", - "contentHash": "POa9ARY/dyJy+rivTJ9skJ+qwbN5prjVgdxkdxiU7r77PZjBpY8fq2W/vsDuA7AqkL7q7Cb7IbdxWnxJgaFSPQ==", + "requested": "[7.4.6, )", + "resolved": "7.4.6", + "contentHash": "fQb9dKdx+fD5vbJ5wjFngeJ1qSkJ91M3jy6bomVsrs1tLNnB06/8pm42Cv+6xhUVQGKV0JKMFy44+QjHl5puqQ==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.ObjectPool": "8.0.8", - "Microsoft.Management.Infrastructure.CimCmdlets": "7.4.5", - "Microsoft.PowerShell.Commands.Diagnostics": "7.4.5", - "Microsoft.PowerShell.Commands.Management": "7.4.5", - "Microsoft.PowerShell.Commands.Utility": "7.4.5", - "Microsoft.PowerShell.ConsoleHost": "7.4.5", - "Microsoft.PowerShell.Security": "7.4.5", - "Microsoft.WSMan.Management": "7.4.5", - "Microsoft.Windows.Compatibility": "8.0.8", + "Microsoft.Extensions.ObjectPool": "8.0.10", + "Microsoft.Management.Infrastructure.CimCmdlets": "7.4.6", + "Microsoft.PowerShell.Commands.Diagnostics": "7.4.6", + "Microsoft.PowerShell.Commands.Management": "7.4.6", + "Microsoft.PowerShell.Commands.Utility": "7.4.6", + "Microsoft.PowerShell.ConsoleHost": "7.4.6", + "Microsoft.PowerShell.Security": "7.4.6", + "Microsoft.WSMan.Management": "7.4.6", + "Microsoft.Windows.Compatibility": "8.0.10", "System.Data.SqlClient": "4.8.6", - "System.IO.Packaging": "8.0.0", - "System.Management.Automation": "7.4.5", + "System.IO.Packaging": "8.0.1", + "System.Management.Automation": "7.4.6", "System.Net.Http.WinHttpHandler": "8.0.2", "System.Private.ServiceModel": "4.10.3", + "System.Runtime.Caching": "8.0.1", "System.ServiceModel.Duplex": "4.10.3", "System.ServiceModel.Http": "4.10.3", "System.ServiceModel.NetTcp": "4.10.3", @@ -50,11 +51,11 @@ }, "NuGet.Protocol": { "type": "Direct", - "requested": "[6.11.0, )", - "resolved": "6.11.0", - "contentHash": "p5B8oNLLnGhUfMbcS16aRiegj11pD6k+LELyRBqvNFR/pE3yR1XT+g1XS33ME9wvoU+xbCGnl4Grztt1jHPinw==", + "requested": "[6.11.1, )", + "resolved": "6.11.1", + "contentHash": "WkYlSuNHNt/j1tbHp/xjvwk2EsIdSM3raEjk3EfIFd62ER1+x4eC8/J1VKqnve6cTupF4LsuwD3Z4YCumnfCXw==", "dependencies": { - "NuGet.Packaging": "6.11.0" + "NuGet.Packaging": "6.11.1" } }, "System.CommandLine": { @@ -63,35 +64,31 @@ "resolved": "2.0.0-beta4.22272.1", "contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg==" }, - "JetBrains.Annotations": { + "Humanizer.Core": { "type": "Transitive", - "resolved": "2021.2.0", - "contentHash": "kKSyoVfndMriKHLfYGmr0uzQuI4jcc3TKGyww7buJFCYeHb/X0kodYBPL7n9454q7v6ASiRmDgpPGaDGerg/Hg==" + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" }, "Json.More.Net": { "type": "Transitive", - "resolved": "1.9.3", - "contentHash": "BKIsKHXR2Jq+LdLdxPo3L09Lv0ld9xs1fAMvSAe2cf2YOl3at9vw0RrMlhC2ookDi7VtrgHXzc2Et5mVBOAUdw==", - "dependencies": { - "System.Text.Json": "6.0.2" - } + "resolved": "2.0.1.2", + "contentHash": "uF3QeiaXEfH92emz0/BWUiNtMSfxIIvgynuB0Bf1vF4s8eWTcZitBx9l+g/FDaJk5XxqBv9buQXizXKQcXFG1w==" }, "JsonPointer.Net": { "type": "Transitive", - "resolved": "3.0.3", - "contentHash": "mCGQc15lHLp1R2CVhWiipnZurHXm93+LbPPAT/vXQm5PdHt6WQuYLhaEF8VZ+aXL9P2I6bGND6pDTEfqFs6gig==", + "resolved": "5.0.0", + "contentHash": "fm4T5w20AY6C+p5/pJr0vrXRNGgtSfHl34I1LxC9zdPwS9S3j0GiR1Mz/CVPWKDXXGDpCt1APHpCq7kn5adCfA==", "dependencies": { - "Json.More.Net": "1.8.0" + "Humanizer.Core": "2.14.1", + "Json.More.Net": "2.0.1.2" } }, "JsonSchema.Net": { "type": "Transitive", - "resolved": "5.2.7", - "contentHash": "8un7Xq2MoKiWNo0HQtf2sPr3764W9NjNELIx3l9d3fIKEjg3tYtrZmxN+CgXKtzku4g52CqYUZuI+o0ue226vw==", + "resolved": "7.0.4", + "contentHash": "R0Hk2Tr/np4Q1NO8CBjyQsoiD1iFJyEQP20Sw7JnZCNGJoaSBe+g4b+nZqnBXPQhiqY5LGZ8JZwZkRh/eKZhEQ==", "dependencies": { - "JetBrains.Annotations": "2021.2.0", - "Json.More.Net": "1.9.0", - "JsonPointer.Net": "3.0.3" + "JsonPointer.Net": "5.0.0" } }, "Manatee.Json": { @@ -135,21 +132,21 @@ }, "Microsoft.CodeAnalysis.Common": { "type": "Transitive", - "resolved": "4.8.0", - "contentHash": "/jR+e/9aT+BApoQJABlVCKnnggGQbvGh7BKq2/wI1LamxC+LbzhcLj4Vj7gXCofl1n4E521YfF9w0WcASGg/KA==", + "resolved": "4.9.2", + "contentHash": "M5PThug7b2AdxL7xKmQs50KzAQTl9jENw5jMT3iUt16k+DAFlw1S87juU3UuPs3gvBm8trMBSOEvSFDr31c9Vw==", "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "3.3.4", - "System.Collections.Immutable": "7.0.0", - "System.Reflection.Metadata": "7.0.0", + "System.Collections.Immutable": "8.0.0", + "System.Reflection.Metadata": "8.0.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "Microsoft.CodeAnalysis.CSharp": { "type": "Transitive", - "resolved": "4.8.0", - "contentHash": "+3+qfdb/aaGD8PZRCrsdobbzGs1m9u119SkkJt8e/mk3xLJz/udLtS2T6nY27OTXxBBw10HzAbC8Z9w08VyP/g==", + "resolved": "4.9.2", + "contentHash": "HGIo7E9Mf3exAJbUdYpDFfLoYkSVaHDJXPyusWTYUTBaOPCowGw+Gap5McE1w+K+ryIXre72oiqL88sQHmHBmg==", "dependencies": { - "Microsoft.CodeAnalysis.Common": "[4.8.0]" + "Microsoft.CodeAnalysis.Common": "[4.9.2]" } }, "Microsoft.Extensions.Configuration": { @@ -232,16 +229,16 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" }, "Microsoft.Extensions.Diagnostics": { "type": "Transitive", @@ -410,8 +407,8 @@ }, "Microsoft.Extensions.ObjectPool": { "type": "Transitive", - "resolved": "8.0.8", - "contentHash": "wnjTFjEvvSbOs3iMfl6CeJcUgPHZMYUB9uAQbGQGxGwVRl4GydNpMSkVntTzoi7AqQeYumU9yDSNeVbpq+ebow==" + "resolved": "8.0.10", + "contentHash": "u7gAG7JgxF8VSJUGPSudAcPxOt+ymJKQCSxNRxiuKV+klCQbHljQR75SilpedCTfhPWDhtUwIJpnDVtspr9nMg==" }, "Microsoft.Extensions.Options": { "type": "Transitive", @@ -450,10 +447,10 @@ }, "Microsoft.Management.Infrastructure.CimCmdlets": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "EFil5FH6/AaNXjpKfh97BaY9/jp7HbrPwjEpKxD3/4NsvilJExAfSTeCXo0nl6jGA0KuU9iSZmzLw+NqnvzIbA==", + "resolved": "7.4.6", + "contentHash": "pWlnAxpOLZf5lTX0SPX68dukSFowbEiGd39jRcO5rhsXDG5TjWrF3x1KxlFtMbiMGqJewVn5MJ7FlOWZDD7E3g==", "dependencies": { - "System.Management.Automation": "7.4.5" + "System.Management.Automation": "7.4.6" } }, "Microsoft.Management.Infrastructure.Runtime.Unix": { @@ -478,51 +475,49 @@ }, "Microsoft.PowerShell.Commands.Diagnostics": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "VART8UZ6cUXlEHGG+Elb7hn0TNL9+ISIOt8F0IxilmBbSTa79Ry3RFur+joWgTe2jLUHSpdHD5Nx1vmgfIdFUw==", + "resolved": "7.4.6", + "contentHash": "beys8wUfsWNda8JVr/Oj/Erx4PeC/AQC3X/TaU/O/BKKjzO1evm+Moz7wOOHb7bK3mqfGIWjsbOC9U/aRZGpGA==", "dependencies": { - "System.Management.Automation": "7.4.5" + "System.Management.Automation": "7.4.6" } }, "Microsoft.PowerShell.Commands.Management": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "O1qelje0o0/IcsS3U7rK8QW4H7WLZFo2WtIH07Nx31eX0CM/1MZrzGTOKE2c5KU/805HUsmZNltA0XJu/ba1dQ==", + "resolved": "7.4.6", + "contentHash": "IqM/FMnJMWpausi+ONG/eD2w7phwHG6SgKtLOG7anmlCiFOefjnQX2Jf3ihm7TYQJIFbxg05LtoIviyICJ8Wrw==", "dependencies": { - "Microsoft.PowerShell.Security": "7.4.5", - "System.ServiceProcess.ServiceController": "8.0.0" + "Microsoft.PowerShell.Security": "7.4.6", + "System.ServiceProcess.ServiceController": "8.0.1" } }, "Microsoft.PowerShell.Commands.Utility": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "5VcmxTv8LKyI2CrBVzl++o1Lc0nWth3WqhdTCUOHwWe69JWtsKIyTJk6YXx/tA8d9cJ9GXbtnZLQ2LC+IL0t6Q==", + "resolved": "7.4.6", + "contentHash": "dqu5AGtk/MH73osSK6on14X3a1TfBolKDnPMXM5EC6vo1XtpDL70MFoomypwgwkxz5B29p2+gtVoGqiQz5cicQ==", "dependencies": { - "Json.More.Net": "1.9.3", - "JsonSchema.Net": "5.2.7", + "JsonSchema.Net": "7.0.4", "Markdig.Signed": "0.33.0", - "Microsoft.CodeAnalysis.CSharp": "4.8.0", + "Microsoft.CodeAnalysis.CSharp": "4.9.2", "Microsoft.PowerShell.MarkdownRender": "7.2.1", - "System.Drawing.Common": "8.0.8", - "System.Management.Automation": "7.4.5", - "System.Text.Json": "8.0.4", - "System.Threading.AccessControl": "8.0.0" + "System.Drawing.Common": "9.0.0-preview.6.24327.6", + "System.Management.Automation": "7.4.6", + "System.Threading.AccessControl": "9.0.0-preview.6.24327.7" } }, "Microsoft.PowerShell.ConsoleHost": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "VOfuT4zf2lecjOf4ppyWaBj7as9rk+T3n5enGv/wb8Wrc4xRRp/fO/CZNjBFqE1jsWqZgghTWpIsAdgr2oMYPA==", + "resolved": "7.4.6", + "contentHash": "LOMd4Zl4rsf3gHHwDf0L8iFdvLgHBACMQlgS3viz0L49N2o+0v5TAJSLOpjg77lcuH43rKLfjN+aoEen78iUBQ==", "dependencies": { - "System.Management.Automation": "7.4.5" + "System.Management.Automation": "7.4.6" } }, "Microsoft.PowerShell.CoreCLR.Eventing": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "yygq1fnGM1cYINM9XfsWUo4K2AAWuKFM/IkTGNlhAMIvxdmPgawgzb9Ygm4u1KNrjdFiJEXsqKqQsMxE9yOwhw==", + "resolved": "7.4.6", + "contentHash": "z8XYESoGwJYT/L60192dE+n1dS7HRvbh2VinadhD3/zA6fDC/lu0VhQc42c56K4JiIMsLfHUUtaPJEa1JPaNBg==", "dependencies": { - "System.Diagnostics.EventLog": "8.0.0" + "System.Diagnostics.EventLog": "8.0.1" } }, "Microsoft.PowerShell.MarkdownRender": { @@ -540,10 +535,10 @@ }, "Microsoft.PowerShell.Security": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "slQplgdH36PvPpv4YZy0srkj3eKvENBmk1Rt/UHu/D0Eio4TIBNRqVLb3Y7824YE/VGuC4DBmQl6f6/UyCbZ2g==", + "resolved": "7.4.6", + "contentHash": "62WzHhtzItjTMbEV88J5zL65/1HIZspEoWGwFAAi9ajoUzJOZUtdGyBQPa7ZsxszPk/Ktk+h3QtW0hwBVdrrPg==", "dependencies": { - "System.Management.Automation": "7.4.5" + "System.Management.Automation": "7.4.6" } }, "Microsoft.Security.Extensions": { @@ -572,37 +567,37 @@ }, "Microsoft.Win32.SystemEvents": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "9opKRyOKMCi2xJ7Bj7kxtZ1r9vbzosMvRrdEhVhDz8j8MoBGgB+WmC94yH839NPH+BclAjtQ/pyagvi/8gDLkw==" + "resolved": "9.0.0-preview.6.24327.7", + "contentHash": "iY1g4tiJLFzV+Ygp+g78w8sNMkDLiEY9nYnZVhVto+lAt6UVzw5ZqwRvLGDPzyrV9J1D/MKchmnziLkL8Ro6hA==" }, "Microsoft.Windows.Compatibility": { "type": "Transitive", - "resolved": "8.0.8", - "contentHash": "9FWk4D5mNFz99rT7NvsqrDdT/6bjkNtxo7CPwajyudBTMXi7kro8ixr5pUnFYrhUBfimR+OUGDYk/se4NxJ6sA==", + "resolved": "8.0.10", + "contentHash": "V92Ri/nR0VqFT6vAVGj20sl0GI6tEFlZiB1IENyPdSdjs+1k5O1lr4vVwtIwoutlib8UyO8tnBwngT6SoOqvyA==", "dependencies": { "Microsoft.Win32.Registry.AccessControl": "8.0.0", "Microsoft.Win32.SystemEvents": "8.0.0", "System.CodeDom": "8.0.0", "System.ComponentModel.Composition": "8.0.0", "System.ComponentModel.Composition.Registration": "8.0.0", - "System.Configuration.ConfigurationManager": "8.0.0", - "System.Data.Odbc": "8.0.0", - "System.Data.OleDb": "8.0.0", + "System.Configuration.ConfigurationManager": "8.0.1", + "System.Data.Odbc": "8.0.1", + "System.Data.OleDb": "8.0.1", "System.Data.SqlClient": "4.8.6", - "System.Diagnostics.EventLog": "8.0.0", - "System.Diagnostics.PerformanceCounter": "8.0.0", + "System.Diagnostics.EventLog": "8.0.1", + "System.Diagnostics.PerformanceCounter": "8.0.1", "System.DirectoryServices": "8.0.0", - "System.DirectoryServices.AccountManagement": "8.0.0", + "System.DirectoryServices.AccountManagement": "8.0.1", "System.DirectoryServices.Protocols": "8.0.0", - "System.Drawing.Common": "8.0.8", - "System.IO.Packaging": "8.0.0", + "System.Drawing.Common": "8.0.10", + "System.IO.Packaging": "8.0.1", "System.IO.Ports": "8.0.0", "System.Management": "8.0.0", "System.Reflection.Context": "8.0.0", - "System.Runtime.Caching": "8.0.0", - "System.Security.Cryptography.Pkcs": "8.0.0", + "System.Runtime.Caching": "8.0.1", + "System.Security.Cryptography.Pkcs": "8.0.1", "System.Security.Cryptography.ProtectedData": "8.0.0", - "System.Security.Cryptography.Xml": "8.0.1", + "System.Security.Cryptography.Xml": "8.0.2", "System.Security.Permissions": "8.0.0", "System.ServiceModel.Duplex": "4.10.0", "System.ServiceModel.Http": "4.10.0", @@ -610,7 +605,7 @@ "System.ServiceModel.Primitives": "4.10.0", "System.ServiceModel.Security": "4.10.0", "System.ServiceModel.Syndication": "8.0.0", - "System.ServiceProcess.ServiceController": "8.0.0", + "System.ServiceProcess.ServiceController": "8.0.1", "System.Speech": "8.0.0", "System.Text.Encoding.CodePages": "8.0.0", "System.Threading.AccessControl": "8.0.0", @@ -619,18 +614,18 @@ }, "Microsoft.WSMan.Management": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "6Rksg7i6GSctvvsB3P7uhU8ub9lVAPa/zsPn1GrZ8eLSApZVZqO4zQFMuylCosXsn9p8h/ktytFSpM2u9k8ujw==", + "resolved": "7.4.6", + "contentHash": "bBEx8+wstdxpsXjqkOPvVq53PwCksDWb7VtyzXjzLdMGY33gPo8H8P3OACUluOcMG/Kd7rPnBszsP7CFhSkKSg==", "dependencies": { - "Microsoft.WSMan.Runtime": "7.4.5", - "System.Management.Automation": "7.4.5", - "System.ServiceProcess.ServiceController": "8.0.0" + "Microsoft.WSMan.Runtime": "7.4.6", + "System.Management.Automation": "7.4.6", + "System.ServiceProcess.ServiceController": "8.0.1" } }, "Microsoft.WSMan.Runtime": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "vXoYVMP88tJ/EOkIB8/vFSc+aXjA+creJFGFIpFpLruSNDPjWHFHplnIkMXmgzHi5JITGSDt3JTz22e21qJhIg==" + "resolved": "7.4.6", + "contentHash": "pnNVcVUT+CP7r23Ju/+nhp0fLSdwevAZwe3qe8XQEahYOUv9ACIP29GijRsjdOwIL8+7DaLUQF5jjh+P/ZjGTQ==" }, "Newtonsoft.Json": { "type": "Transitive", @@ -639,41 +634,41 @@ }, "NuGet.Common": { "type": "Transitive", - "resolved": "6.11.0", - "contentHash": "T3bCiKUSx8wdYpcqr6Dbx93zAqFp689ee/oa1tH22XI/xl7EUzQ7No/WlE1FUqvEX1+Mqar3wRNAn2O/yxo94g==", + "resolved": "6.11.1", + "contentHash": "6ouw0UC3TGaFHNJyoEK2/Q5jSryRHzcbKGv9C0+t/TPnTP8PoLqnyFxO1dwmQUmJkWuKAUo6Vu0kIXHY/8LyKQ==", "dependencies": { - "NuGet.Frameworks": "6.11.0" + "NuGet.Frameworks": "6.11.1" } }, "NuGet.Configuration": { "type": "Transitive", - "resolved": "6.11.0", - "contentHash": "73QprQqmumFrv3Ooi4YWpRYeBj8jZy9gNdOaOCp4pPInpt41SJJAz/aP4je+StwIJvi5HsgPPecLKekDIQEwKg==", + "resolved": "6.11.1", + "contentHash": "vbEe2acKrI2QmNx9U94ewhgUt6cLpwBeQfMtrie6NMz+GkJcX/4qIkKsX3SeBO4gFgCf8eeTyURl5hxPtiUctw==", "dependencies": { - "NuGet.Common": "6.11.0", + "NuGet.Common": "6.11.1", "System.Security.Cryptography.ProtectedData": "4.4.0" } }, "NuGet.Frameworks": { "type": "Transitive", - "resolved": "6.11.0", - "contentHash": "Ew/mrfmLF5phsprysHbph2+tdZ10HMHAURavsr/Kx1WhybDG4vmGuoNLbbZMZOqnPRdpyCTc42OKWLoedxpYtA==" + "resolved": "6.11.1", + "contentHash": "plTZ3ariSWQVsFn2mk83SsdmSg1VpgIMTSZpP/eSE/NNQF02p+M9ItxAYeUZBMX+cQ2nFkSwxQRJ0/fkaV9Hbg==" }, "NuGet.Packaging": { "type": "Transitive", - "resolved": "6.11.0", - "contentHash": "VmUv2LedVuPY1tfNybORO2I9IuqOzeV7I5JBD+PwNvJq2bAqovi4FCw2cYI0g+kjOJXBN2lAJfrfnqtUOlVJdQ==", + "resolved": "6.11.1", + "contentHash": "wiofIUUr7khwuaGXiOibMb7+dEkF97EVsAmzlaNc188HV9ujjqweQMuVCoAK2/MqXdhnrKjvicUfKo9CPsNnfg==", "dependencies": { "Newtonsoft.Json": "13.0.3", - "NuGet.Configuration": "6.11.0", - "NuGet.Versioning": "6.11.0", + "NuGet.Configuration": "6.11.1", + "NuGet.Versioning": "6.11.1", "System.Security.Cryptography.Pkcs": "6.0.4" } }, "NuGet.Versioning": { "type": "Transitive", - "resolved": "6.11.0", - "contentHash": "v/GGlIj2dd7svplFmASWEueu62veKW0MrMtBaZ7QG8aJTSGv2yE+pgUGhXRcQ4nxNOEq/wLBrz1vkth/1SND7A==" + "resolved": "6.11.1", + "contentHash": "YNn3BB71F+guJW42TbAhGcMh3gpyqFMZcPVD9pm5vcvGivTALtRely/VCPWQQ6JQ5PfwIrjPaJMO7VnqyeK3rg==" }, "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { "type": "Transitive", @@ -878,8 +873,8 @@ }, "System.Collections.Immutable": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==" + "resolved": "8.0.0", + "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==" }, "System.ComponentModel.Annotations": { "type": "Transitive", @@ -902,28 +897,25 @@ }, "System.Configuration.ConfigurationManager": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==", + "resolved": "8.0.1", + "contentHash": "gPYFPDyohW2gXNhdQRSjtmeS6FymL2crg4Sral1wtvEJ7DUqFCDWDVbbLobASbzxfic8U1hQEdC7hmg9LHncMw==", "dependencies": { - "System.Diagnostics.EventLog": "8.0.0", + "System.Diagnostics.EventLog": "8.0.1", "System.Security.Cryptography.ProtectedData": "8.0.0" } }, "System.Data.Odbc": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c+GfnZt2/HyU+voKw2fctLZClcNjPZPWS+mnIhGvDknRMqL/fwWlREWPgA4csbp9ZkQIgB4qkufgdh/oh5Ubow==", - "dependencies": { - "System.Text.Encoding.CodePages": "8.0.0" - } + "resolved": "8.0.1", + "contentHash": "JQd0QHOaZuH+ki+4Geas88dnLe/lZSaEYYmRdovZaqNVuExVlVFs/of2I1VaasMxzbO5+yrGDAP2rkazx/b8Sg==" }, "System.Data.OleDb": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "FpUTcQ0E8mFvcYp8UZA3NX8wgmhmsCue56g1zfkr1xdOnT5FrYYmC5DWQ9xCw8o8zuxVBKLZvliqEGgmeoalaQ==", + "resolved": "8.0.1", + "contentHash": "RO+/y2ggU5956uQDRXdjA1e2l5yJ4rTWNX76eZ+3sgtYGqGapCe2kQCyiUci+/y6Fyb21Irp4RQEdfrIiuYrxQ==", "dependencies": { - "System.Configuration.ConfigurationManager": "8.0.0", - "System.Diagnostics.PerformanceCounter": "8.0.0" + "System.Configuration.ConfigurationManager": "8.0.1", + "System.Diagnostics.PerformanceCounter": "8.0.1" } }, "System.Data.SqlClient": { @@ -953,15 +945,15 @@ }, "System.Diagnostics.EventLog": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A==" + "resolved": "8.0.1", + "contentHash": "n1ZP7NM2Gkn/MgD8+eOT5MulMj6wfeQMNS2Pizvq5GHCZfjlFMXV2irQlQmJhwA2VABC57M0auudO89Iu2uRLg==" }, "System.Diagnostics.PerformanceCounter": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "lX6DXxtJqVGWw7N/QmVoiCyVQ+Q/Xp+jVXPr3gLK1jJExSn1qmAjJQeb8gnOYeeBTG3E3PmG1nu92eYj/TEjpg==", + "resolved": "8.0.1", + "contentHash": "9RfEDiEjlUADeThs8IPdDVTXSnPRSqjfgTQJALpmGFPKC0k2mbdufOXnb/9JZ4I0TkmxOfy3VTJxrHOJSs8cXg==", "dependencies": { - "System.Configuration.ConfigurationManager": "8.0.0" + "System.Configuration.ConfigurationManager": "8.0.1" } }, "System.Diagnostics.Tracing": { @@ -981,10 +973,10 @@ }, "System.DirectoryServices.AccountManagement": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "dCT8BYeeisx0IzAf6x+FSVWK3gz2fKI9pgLV16c7dY/lckw4aodNrgXqsFqyqJN5Kfxc3oklG+SCMYkRfg1V7A==", + "resolved": "8.0.1", + "contentHash": "qVDWKClyDY+rHVEnf11eU4evW25d5OeidrtMPSJv+fwG213wa2zJ+AuIFCxsuvNSCFyHo+DvQIVfBcoK3CL1pA==", "dependencies": { - "System.Configuration.ConfigurationManager": "8.0.0", + "System.Configuration.ConfigurationManager": "8.0.1", "System.DirectoryServices": "8.0.0", "System.DirectoryServices.Protocols": "8.0.0" } @@ -996,10 +988,10 @@ }, "System.Drawing.Common": { "type": "Transitive", - "resolved": "8.0.8", - "contentHash": "4ZM1wvLjz9nVVExsfPAaSl/qOvU+QNedJL5rQ+2Wbow+iGeyO0e7XN07707rMBgaffEeeLrCZBwC0oHUuvRdPw==", + "resolved": "9.0.0-preview.6.24327.6", + "contentHash": "4mNJBnN4iroaz44NIcYVKSPgOqhBTsPHqBG5aTfSjsKWCkRzhKnkVsWuYzx4FrZoEToGveMvUCanlY/V7GUZ5A==", "dependencies": { - "Microsoft.Win32.SystemEvents": "8.0.0" + "Microsoft.Win32.SystemEvents": "9.0.0-preview.6.24327.7" } }, "System.Formats.Asn1": { @@ -1078,8 +1070,8 @@ }, "System.IO.Packaging": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "8g1V4YRpdGAxFcK8v9OjuMdIOJSpF30Zb1JGicwVZhly3I994WFyBdV6mQEo8d3T+URQe55/M0U0eIH0Hts1bg==" + "resolved": "8.0.1", + "contentHash": "KYkIOAvPexQOLDxPO2g0BVoWInnQhPpkFzRqvNrNrMhVT6kqhVr0zEb6KCHlptLFukxnZrjuMVAnxK7pOGUYrw==" }, "System.IO.Ports": { "type": "Transitive", @@ -1111,23 +1103,23 @@ }, "System.Management.Automation": { "type": "Transitive", - "resolved": "7.4.5", - "contentHash": "M8diPEl3ycy5juoSwCQkJVyiG6pHjv9IPD/bvqcnrQfAsbYzqDLVQLzfurHVrQHKvqAWB4k3xwtHMyYjG8xR9Q==", + "resolved": "7.4.6", + "contentHash": "RxrDUOJuLC7Sd2RaUHf52ujP5VpdfSVTv75nwgEVAROCfYV4IcSd5mL4mLrKa+6LjkXGli7aUs853UyCwidl1A==", "dependencies": { "Microsoft.ApplicationInsights": "2.21.0", "Microsoft.Management.Infrastructure": "3.0.0", - "Microsoft.PowerShell.CoreCLR.Eventing": "7.4.5", + "Microsoft.PowerShell.CoreCLR.Eventing": "7.4.6", "Microsoft.PowerShell.Native": "7.4.0", "Microsoft.Security.Extensions": "1.2.0", "Microsoft.Win32.Registry.AccessControl": "8.0.0", "Newtonsoft.Json": "13.0.3", - "System.Configuration.ConfigurationManager": "8.0.0", + "System.Configuration.ConfigurationManager": "8.0.1", "System.Diagnostics.DiagnosticSource": "8.0.1", "System.DirectoryServices": "8.0.0", "System.Formats.Asn1": "8.0.1", "System.Management": "8.0.0", "System.Security.AccessControl": "6.0.1", - "System.Security.Cryptography.Pkcs": "8.0.0", + "System.Security.Cryptography.Pkcs": "8.0.1", "System.Security.Permissions": "8.0.0", "System.Text.Encoding.CodePages": "8.0.0" } @@ -1245,10 +1237,10 @@ }, "System.Reflection.Metadata": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "MclTG61lsD9sYdpNz9xsKBzjsmsfCtcMZYXz/IUr2zlhaTaABonlr1ESeompTgM+Xk+IwtGYU7/voh3YWB/fWw==", + "resolved": "8.0.0", + "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==", "dependencies": { - "System.Collections.Immutable": "7.0.0" + "System.Collections.Immutable": "8.0.0" } }, "System.Reflection.Primitives": { @@ -1284,10 +1276,10 @@ }, "System.Runtime.Caching": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "4TmlmvGp4kzZomm7J2HJn6IIx0UUrQyhBDyb5O1XiunZlQImXW+B8b7W/sTPcXhSf9rp5NR5aDtQllwbB5elOQ==", + "resolved": "8.0.1", + "contentHash": "tdl7Q47P09UpRu0C/OQsGJU6GacBzzk4vfp5My9rodD+BchrxmajORnTthH8RxPUTPrIoVDJmLyvJcGxB267nQ==", "dependencies": { - "System.Configuration.ConfigurationManager": "8.0.0" + "System.Configuration.ConfigurationManager": "8.0.1" } }, "System.Runtime.CompilerServices.Unsafe": { @@ -1444,11 +1436,8 @@ }, "System.Security.Cryptography.Pkcs": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "ULmp3xoOwNYjOYp4JZ2NK/6NdTgiN1GQXzVVN1njQ7LOZ0d0B9vyMnhyqbIi9Qw4JXj1JgCsitkTShboHRx7Eg==", - "dependencies": { - "System.Formats.Asn1": "8.0.0" - } + "resolved": "8.0.1", + "contentHash": "CoCRHFym33aUSf/NtWSVSZa99dkd0Hm7OCZUxORBjRB16LNhIEOf8THPqzIYlvKM0nNDAPTRBa1FxEECrgaxxA==" }, "System.Security.Cryptography.Primitives": { "type": "Transitive", @@ -1503,10 +1492,10 @@ }, "System.Security.Cryptography.Xml": { "type": "Transitive", - "resolved": "8.0.1", - "contentHash": "hqu2ztecOf3BYg5q1R7QcyliX9L7r3mfsWtaRitAxcezH8hyZMB7zCmhi186hsUZXk1KxsAHXwyPEW+xvUED6g==", + "resolved": "8.0.2", + "contentHash": "aDM/wm0ZGEZ6ZYJLzgqjp2FZdHbDHh6/OmpGfb7AdZ105zYmPn/83JRU2xLIbwgoNz9U1SLUTJN0v5th3qmvjA==", "dependencies": { - "System.Security.Cryptography.Pkcs": "8.0.0" + "System.Security.Cryptography.Pkcs": "8.0.1" } }, "System.Security.Permissions": { @@ -1573,10 +1562,10 @@ }, "System.ServiceProcess.ServiceController": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "jtYVG3bpw2n/NvNnP2g/JLri0D4UtfusTvLeH6cZPNAEjJXJVGspS3wLgVvjNbm+wjaYkFgsXejMTocV1T5DIQ==", + "resolved": "8.0.1", + "contentHash": "02I0BXo1kmMBgw03E8Hu4K6nTqur4wpQdcDZrndczPzY2fEoGvlinE35AWbyzLZ2h2IksEZ6an4tVt3hi9j1oA==", "dependencies": { - "System.Diagnostics.EventLog": "8.0.0" + "System.Diagnostics.EventLog": "8.0.1" } }, "System.Speech": { @@ -1606,11 +1595,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "8.0.4", - "contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==", - "dependencies": { - "System.Text.Encodings.Web": "8.0.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading": { "type": "Transitive", @@ -1623,8 +1609,8 @@ }, "System.Threading.AccessControl": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cIed5+HuYz+eV9yu9TH95zPkqmm1J9Qps9wxjB335sU8tsqc2kGdlTEH9FZzZeCS8a7mNSEsN8ZkyhQp1gfdEw==" + "resolved": "9.0.0-preview.6.24327.7", + "contentHash": "t7e5cLBMvBx9/YhNsCp8W8iUw7geh08y0GKFawfJUD5YLgx6AjO2D497+0qHbXRQGpl2uxBGmkWKnCZ5azILZQ==" }, "System.Threading.Tasks": { "type": "Transitive", @@ -1666,13 +1652,13 @@ "type": "Project", "dependencies": { "Manatee.Json": "[13.0.5, )", - "Microsoft.Extensions.DependencyInjection": "[8.0.0, )", + "Microsoft.Extensions.DependencyInjection": "[8.0.1, )", "Microsoft.Extensions.Hosting": "[8.0.0, )", "Microsoft.PSRule.Badges": "[0.0.1, )", "Microsoft.PSRule.Types": "[0.0.1, )", "Sarif.Sdk": "[2.4.16, )", "System.Net.Http": "[4.3.4, )", - "System.Text.Json": "[8.0.4, )" + "System.Text.Json": "[8.0.5, )" } }, "Microsoft.PSRule.SDK": { diff --git a/src/PSRule.SDK/packages.lock.json b/src/PSRule.SDK/packages.lock.json index 5e14f66f92..c22b484c95 100644 --- a/src/PSRule.SDK/packages.lock.json +++ b/src/PSRule.SDK/packages.lock.json @@ -125,18 +125,18 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", "System.Threading.Tasks.Extensions": "4.5.4" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==", + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "8.0.0", "System.Threading.Tasks.Extensions": "4.5.4" @@ -987,8 +987,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "8.0.4", - "contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==", + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "8.0.0", "System.Buffers": "4.5.1", @@ -1045,13 +1045,13 @@ "type": "Project", "dependencies": { "Manatee.Json": "[13.0.5, )", - "Microsoft.Extensions.DependencyInjection": "[8.0.0, )", + "Microsoft.Extensions.DependencyInjection": "[8.0.1, )", "Microsoft.Extensions.Hosting": "[8.0.0, )", "Microsoft.PSRule.Badges": "[0.0.1, )", "Microsoft.PSRule.Types": "[0.0.1, )", "Sarif.Sdk": "[2.4.16, )", "System.Net.Http": "[4.3.4, )", - "System.Text.Json": "[8.0.4, )" + "System.Text.Json": "[8.0.5, )" } }, "Microsoft.PSRule.Types": { diff --git a/src/PSRule.Tool/ClientBuilder.cs b/src/PSRule.Tool/ClientBuilder.cs index 0f2efcab1c..72207a80da 100644 --- a/src/PSRule.Tool/ClientBuilder.cs +++ b/src/PSRule.Tool/ClientBuilder.cs @@ -29,6 +29,7 @@ internal sealed class ClientBuilder private readonly Option _Module_Add_Version; private readonly Option _Module_Add_Force; private readonly Option _Module_Add_SkipVerification; + private readonly Option _Module_Prerelease; private readonly Option _Global_Path; private readonly Option _Run_OutputPath; private readonly Option _Run_OutputFormat; @@ -43,70 +44,74 @@ private ClientBuilder(RootCommand cmd) // Global options. _Global_Option = new Option( - new string[] { "--option" }, + ["--option"], getDefaultValue: () => "ps-rule.yaml", description: CmdStrings.Global_Option_Description ); _Global_Verbose = new Option( - new string[] { "--verbose" }, + ["--verbose"], description: CmdStrings.Global_Verbose_Description ); _Global_Debug = new Option( - new string[] { "--debug" }, + ["--debug"], description: CmdStrings.Global_Debug_Description ); _Global_Path = new Option( - new string[] { "-p", "--path" }, + ["-p", "--path"], description: CmdStrings.Global_Path_Description ); // Options for the run command. _Run_OutputPath = new Option( - new string[] { "--output-path" }, + ["--output-path"], description: CmdStrings.Run_OutputPath_Description ); _Run_OutputFormat = new Option( - new string[] { "-o", "--output" }, + ["-o", "--output"], description: CmdStrings.Run_OutputFormat_Description ); _Run_InputPath = new Option( - new string[] { "-f", "--input-path" }, + ["-f", "--input-path"], description: CmdStrings.Run_InputPath_Description ); _Run_Module = new Option( - new string[] { "-m", "--module" }, + ["-m", "--module"], description: CmdStrings.Run_Module_Description ); _Run_Baseline = new Option( - new string[] { "--baseline" }, + ["--baseline"], description: CmdStrings.Run_Baseline_Description ); _Run_Outcome = new Option( - new string[] { "--outcome" }, + ["--outcome"], description: CmdStrings.Run_Outcome_Description ).FromAmong("Pass", "Fail", "Error", "Processed", "Problem"); _Run_Outcome.Arity = ArgumentArity.ZeroOrMore; // Options for the module command. _Module_Init_Force = new Option( - new string[] { ARG_FORCE }, + [ARG_FORCE], description: CmdStrings.Module_Init_Force_Description ); _Module_Add_Version = new Option ( - new string[] { "--version" }, + ["--version"], description: CmdStrings.Module_Add_Version_Description ); _Module_Add_Force = new Option( - new string[] { ARG_FORCE }, + [ARG_FORCE], description: CmdStrings.Module_Add_Force_Description ); _Module_Add_SkipVerification = new Option( - new string[] { "--skip-verification" }, + ["--skip-verification"], description: CmdStrings.Module_Add_SkipVerification_Description ); + _Module_Prerelease = new Option( + ["--prerelease"], + description: CmdStrings.Module_Prerelease_Description + ); _Module_Restore_Force = new Option( - new string[] { ARG_FORCE }, + [ARG_FORCE], description: CmdStrings.Module_Restore_Force_Description ); @@ -165,12 +170,12 @@ private void AddModule() { var cmd = new Command("module", CmdStrings.Module_Description); - var moduleArg = new Argument + var requiredModuleArg = new Argument ( "module", - CmdStrings.Module_Module_Description + CmdStrings.Module_RequiredModule_Description ); - moduleArg.Arity = ArgumentArity.OneOrMore; + requiredModuleArg.Arity = ArgumentArity.OneOrMore; // Init var init = new Command @@ -219,19 +224,21 @@ private void AddModule() "add", CmdStrings.Module_Add_Description ); - add.AddArgument(moduleArg); + add.AddArgument(requiredModuleArg); add.AddOption(_Module_Add_Version); add.AddOption(_Module_Add_Force); add.AddOption(_Module_Add_SkipVerification); + add.AddOption(_Module_Prerelease); add.SetHandler(async (invocation) => { var option = new ModuleOptions { Path = invocation.ParseResult.GetValueForOption(_Global_Path), - Module = invocation.ParseResult.GetValueForArgument(moduleArg), + Module = invocation.ParseResult.GetValueForArgument(requiredModuleArg), Version = invocation.ParseResult.GetValueForOption(_Module_Add_Version), Force = invocation.ParseResult.GetValueForOption(_Module_Add_Force), SkipVerification = invocation.ParseResult.GetValueForOption(_Module_Add_SkipVerification), + Prerelease = invocation.ParseResult.GetValueForOption(_Module_Prerelease), }; var client = GetClientContext(invocation); @@ -244,13 +251,13 @@ private void AddModule() "remove", CmdStrings.Module_Remove_Description ); - remove.AddArgument(moduleArg); + remove.AddArgument(requiredModuleArg); remove.SetHandler(async (invocation) => { var option = new ModuleOptions { Path = invocation.ParseResult.GetValueForOption(_Global_Path), - Module = invocation.ParseResult.GetValueForArgument(moduleArg), + Module = invocation.ParseResult.GetValueForArgument(requiredModuleArg), }; var client = GetClientContext(invocation); @@ -263,11 +270,21 @@ private void AddModule() "upgrade", CmdStrings.Module_Upgrade_Description ); + var optionalModuleArg = new Argument + ( + "module", + CmdStrings.Module_OptionalModule_Description + ); + optionalModuleArg.Arity = ArgumentArity.ZeroOrMore; + upgrade.AddArgument(optionalModuleArg); + upgrade.AddOption(_Module_Prerelease); upgrade.SetHandler(async (invocation) => { var option = new ModuleOptions { Path = invocation.ParseResult.GetValueForOption(_Global_Path), + Module = invocation.ParseResult.GetValueForArgument(requiredModuleArg), + Prerelease = invocation.ParseResult.GetValueForOption(_Module_Prerelease), }; var client = GetClientContext(invocation); diff --git a/src/PSRule.Tool/Properties/launchSettings.json b/src/PSRule.Tool/Properties/launchSettings.json index 87369a4063..f2faf71154 100644 --- a/src/PSRule.Tool/Properties/launchSettings.json +++ b/src/PSRule.Tool/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "ps-rule module add": { "commandName": "Project", - "commandLineArgs": "module add abc --version 1.0.0", + "commandLineArgs": "module add PSRule.Rules.Azure", "workingDirectory": "../../" }, "ps-rule run": { @@ -14,6 +14,11 @@ "commandName": "Project", "commandLineArgs": "module restore", "workingDirectory": "../../" + }, + "ps-rule module upgrade": { + "commandName": "Project", + "commandLineArgs": "module upgrade", + "workingDirectory": "../../" } } -} \ No newline at end of file +} diff --git a/src/PSRule.Tool/Resources/CmdStrings.Designer.cs b/src/PSRule.Tool/Resources/CmdStrings.Designer.cs index 2ef61e47f0..b468a88bfa 100644 --- a/src/PSRule.Tool/Resources/CmdStrings.Designer.cs +++ b/src/PSRule.Tool/Resources/CmdStrings.Designer.cs @@ -178,11 +178,20 @@ internal static string Module_List_Description { } /// - /// Looks up a localized string similar to The name of one or more modules.. + /// Looks up a localized string similar to Optionally specifies one or more modules to apply to.. + /// + internal static string Module_OptionalModule_Description { + get { + return ResourceManager.GetString("Module_OptionalModule_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Accept pre-release versions in addition to stable module versions.. /// - internal static string Module_Module_Description { + internal static string Module_Prerelease_Description { get { - return ResourceManager.GetString("Module_Module_Description", resourceCulture); + return ResourceManager.GetString("Module_Prerelease_Description", resourceCulture); } } @@ -195,6 +204,15 @@ internal static string Module_Remove_Description { } } + /// + /// Looks up a localized string similar to The name of one or more modules.. + /// + internal static string Module_RequiredModule_Description { + get { + return ResourceManager.GetString("Module_RequiredModule_Description", resourceCulture); + } + } + /// /// Looks up a localized string similar to Restore from the module lock file and configured options.. /// diff --git a/src/PSRule.Tool/Resources/CmdStrings.resx b/src/PSRule.Tool/Resources/CmdStrings.resx index 4df7b89b9d..17470763da 100644 --- a/src/PSRule.Tool/Resources/CmdStrings.resx +++ b/src/PSRule.Tool/Resources/CmdStrings.resx @@ -141,9 +141,12 @@ Manage or restore modules tracked by the module lock file and configured options. - + The name of one or more modules. + + Optionally specifies one or more modules to apply to. + Remove one or more modules from the lock file. @@ -192,4 +195,7 @@ Specifies a path to write results to. - \ No newline at end of file + + Accept pre-release versions in addition to stable module versions. + + diff --git a/src/PSRule.Tool/packages.lock.json b/src/PSRule.Tool/packages.lock.json index cc4c911dad..54ee914951 100644 --- a/src/PSRule.Tool/packages.lock.json +++ b/src/PSRule.Tool/packages.lock.json @@ -187,16 +187,16 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" }, "Microsoft.Extensions.Diagnostics": { "type": "Transitive", @@ -623,49 +623,49 @@ }, "NuGet.Common": { "type": "Transitive", - "resolved": "6.11.0", - "contentHash": "T3bCiKUSx8wdYpcqr6Dbx93zAqFp689ee/oa1tH22XI/xl7EUzQ7No/WlE1FUqvEX1+Mqar3wRNAn2O/yxo94g==", + "resolved": "6.11.1", + "contentHash": "6ouw0UC3TGaFHNJyoEK2/Q5jSryRHzcbKGv9C0+t/TPnTP8PoLqnyFxO1dwmQUmJkWuKAUo6Vu0kIXHY/8LyKQ==", "dependencies": { - "NuGet.Frameworks": "6.11.0" + "NuGet.Frameworks": "6.11.1" } }, "NuGet.Configuration": { "type": "Transitive", - "resolved": "6.11.0", - "contentHash": "73QprQqmumFrv3Ooi4YWpRYeBj8jZy9gNdOaOCp4pPInpt41SJJAz/aP4je+StwIJvi5HsgPPecLKekDIQEwKg==", + "resolved": "6.11.1", + "contentHash": "vbEe2acKrI2QmNx9U94ewhgUt6cLpwBeQfMtrie6NMz+GkJcX/4qIkKsX3SeBO4gFgCf8eeTyURl5hxPtiUctw==", "dependencies": { - "NuGet.Common": "6.11.0", + "NuGet.Common": "6.11.1", "System.Security.Cryptography.ProtectedData": "4.4.0" } }, "NuGet.Frameworks": { "type": "Transitive", - "resolved": "6.11.0", - "contentHash": "Ew/mrfmLF5phsprysHbph2+tdZ10HMHAURavsr/Kx1WhybDG4vmGuoNLbbZMZOqnPRdpyCTc42OKWLoedxpYtA==" + "resolved": "6.11.1", + "contentHash": "plTZ3ariSWQVsFn2mk83SsdmSg1VpgIMTSZpP/eSE/NNQF02p+M9ItxAYeUZBMX+cQ2nFkSwxQRJ0/fkaV9Hbg==" }, "NuGet.Packaging": { "type": "Transitive", - "resolved": "6.11.0", - "contentHash": "VmUv2LedVuPY1tfNybORO2I9IuqOzeV7I5JBD+PwNvJq2bAqovi4FCw2cYI0g+kjOJXBN2lAJfrfnqtUOlVJdQ==", + "resolved": "6.11.1", + "contentHash": "wiofIUUr7khwuaGXiOibMb7+dEkF97EVsAmzlaNc188HV9ujjqweQMuVCoAK2/MqXdhnrKjvicUfKo9CPsNnfg==", "dependencies": { "Newtonsoft.Json": "13.0.3", - "NuGet.Configuration": "6.11.0", - "NuGet.Versioning": "6.11.0", + "NuGet.Configuration": "6.11.1", + "NuGet.Versioning": "6.11.1", "System.Security.Cryptography.Pkcs": "6.0.4" } }, "NuGet.Protocol": { "type": "Transitive", - "resolved": "6.11.0", - "contentHash": "p5B8oNLLnGhUfMbcS16aRiegj11pD6k+LELyRBqvNFR/pE3yR1XT+g1XS33ME9wvoU+xbCGnl4Grztt1jHPinw==", + "resolved": "6.11.1", + "contentHash": "WkYlSuNHNt/j1tbHp/xjvwk2EsIdSM3raEjk3EfIFd62ER1+x4eC8/J1VKqnve6cTupF4LsuwD3Z4YCumnfCXw==", "dependencies": { - "NuGet.Packaging": "6.11.0" + "NuGet.Packaging": "6.11.1" } }, "NuGet.Versioning": { "type": "Transitive", - "resolved": "6.11.0", - "contentHash": "v/GGlIj2dd7svplFmASWEueu62veKW0MrMtBaZ7QG8aJTSGv2yE+pgUGhXRcQ4nxNOEq/wLBrz1vkth/1SND7A==" + "resolved": "6.11.1", + "contentHash": "YNn3BB71F+guJW42TbAhGcMh3gpyqFMZcPVD9pm5vcvGivTALtRely/VCPWQQ6JQ5PfwIrjPaJMO7VnqyeK3rg==" }, "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { "type": "Transitive", @@ -1603,11 +1603,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "8.0.4", - "contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==", - "dependencies": { - "System.Text.Encodings.Web": "8.0.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading": { "type": "Transitive", @@ -1664,7 +1661,7 @@ "dependencies": { "Microsoft.PSRule.SDK": "[0.0.1, )", "Microsoft.PowerShell.SDK": "[7.4.5, )", - "NuGet.Protocol": "[6.11.0, )", + "NuGet.Protocol": "[6.11.1, )", "System.CommandLine": "[2.0.0-beta4.22272.1, )" } }, @@ -1672,13 +1669,13 @@ "type": "Project", "dependencies": { "Manatee.Json": "[13.0.5, )", - "Microsoft.Extensions.DependencyInjection": "[8.0.0, )", + "Microsoft.Extensions.DependencyInjection": "[8.0.1, )", "Microsoft.Extensions.Hosting": "[8.0.0, )", "Microsoft.PSRule.Badges": "[0.0.1, )", "Microsoft.PSRule.Types": "[0.0.1, )", "Sarif.Sdk": "[2.4.16, )", "System.Net.Http": "[4.3.4, )", - "System.Text.Json": "[8.0.4, )" + "System.Text.Json": "[8.0.5, )" } }, "Microsoft.PSRule.SDK": { diff --git a/src/PSRule.Types/Data/DateVersion.cs b/src/PSRule.Types/Data/DateVersion.cs index 92a32d9b71..c457b22ca8 100644 --- a/src/PSRule.Types/Data/DateVersion.cs +++ b/src/PSRule.Types/Data/DateVersion.cs @@ -5,17 +5,6 @@ namespace PSRule.Data; -/// -/// An date version constraint. -/// -public interface IDateVersionConstraint -{ - /// - /// Determines if the date version meets the requirments of the constraint. - /// - bool Equals(DateVersion.Version version); -} - /// /// A helper for comparing date version strings. /// An date version is represented as YYYY-MM-DD-prerelease. @@ -75,18 +64,37 @@ internal enum ConstraintModifier public sealed class VersionConstraint : IDateVersionConstraint { private List? _Constraints; + private readonly string _Value; + private readonly bool _IncludePrerelease; + + /// + /// A version constraint that accepts any version including pre-releases. + /// + public static readonly VersionConstraint Any = new(string.Empty, includePrerelease: true); + + /// + /// A version constraint that accepts any stable version. + /// + public static readonly VersionConstraint AnyStable = new(string.Empty, includePrerelease: false); + + internal VersionConstraint(string value, bool includePrerelease) + { + _Value = value; + _IncludePrerelease = includePrerelease; + } /// - public bool Equals(Version version) + public bool Accepts(Version? version) { + if (version is null) return false; if (_Constraints == null || _Constraints.Count == 0) - return true; + return version.Stable || _IncludePrerelease; var match = false; var i = 0; while (!match && i < _Constraints.Count) { - var result = _Constraints[i].Equals(version); + var result = _Constraints[i].Accepts(version); // True OR if (result && _Constraints[i].Join == JoinOperator.Or) @@ -159,12 +167,13 @@ public static bool TryParse(string value, out IDateVersionConstraint constraint) return TryParseConstraint(value, out constraint); } - public bool Equals(Version version) + /// + public bool Accepts(Version? version) { - return Equals(version.Year, version.Month, version.Day, version.Prerelease); + return version is not null && Accepts(version.Year, version.Month, version.Day, version.Prerelease); } - public bool Equals(int year, int month, int day, PR prid) + public bool Accepts(int year, int month, int day, PR prid) { if (_Flag == ComparisonOperator.Equals) return EQ(year, month, day, prid); @@ -277,8 +286,11 @@ private static bool IsStable(PR prid) /// /// An date version. /// + [DebuggerDisplay("{_VersionString}")] public sealed class Version : IComparable, IEquatable { + private readonly string _VersionString; + /// /// The year part of the version. /// @@ -305,12 +317,19 @@ internal Version(int year, int month, int day, PR prerelease) Month = month; Day = day; Prerelease = prerelease; + + _VersionString = GetVersionString(); } + /// + /// Determines if the version is stable or a pre-release. + /// + public bool Stable => Prerelease == null || Prerelease.Stable; + /// public override string ToString() { - return string.Concat(Year, DASH, Month, DASH, Day); + return _VersionString; } /// @@ -338,7 +357,7 @@ public override int GetHashCode() /// public bool Equals(Version other) { - return other != null && + return other is not null && Equals(other.Year, other.Month, other.Day); } @@ -357,7 +376,7 @@ public bool Equals(int year, int month, int day) /// public int CompareTo(Version other) { - if (other == null) + if (other is null) return 1; if (Year != other.Year) @@ -369,13 +388,34 @@ public int CompareTo(Version other) if (Day != other.Day) return Day > other.Day ? 8 : -8; - if ((Prerelease == null || Prerelease.Stable) && (other.Prerelease == null || other.Prerelease.Stable)) + if ((Prerelease is null || Prerelease.Stable) && (other.Prerelease is null || other.Prerelease.Stable)) return 0; - if (Prerelease != null && !Prerelease.Stable && other.Prerelease != null && !other.Prerelease.Stable) + if (Prerelease is not null && !Prerelease.Stable && other.Prerelease is not null && !other.Prerelease.Stable) return Prerelease.CompareTo(other.Prerelease); - return Prerelease == null || Prerelease.Stable ? 1 : -1; + return Prerelease is null || Prerelease.Stable ? 1 : -1; + } + + private string GetVersionString() + { + var count = 5 + (Prerelease != null && !Prerelease.Stable ? 2 : 0); + var parts = new object[count]; + + parts[0] = Year; + parts[1] = DASH; + parts[2] = Month; + parts[3] = DASH; + parts[4] = Day; + + var next = 5; + if (Prerelease != null && !Prerelease.Stable) + { + parts[next++] = DASH; + parts[next++] = Prerelease.Value; + } + + return string.Concat(parts); } } @@ -730,7 +770,7 @@ private static bool IsLetter(char c) /// public static bool TryParseConstraint(string value, out IDateVersionConstraint constraint, bool includePrerelease = false) { - var c = new VersionConstraint(); + var c = new VersionConstraint(value, includePrerelease); constraint = c; if (string.IsNullOrEmpty(value)) return true; diff --git a/src/PSRule.Types/Data/IDateVersionConstraint.cs b/src/PSRule.Types/Data/IDateVersionConstraint.cs new file mode 100644 index 0000000000..d872039ba6 --- /dev/null +++ b/src/PSRule.Types/Data/IDateVersionConstraint.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace PSRule.Data; + +/// +/// An date version constraint. +/// +public interface IDateVersionConstraint +{ + /// + /// Determines if the date version meets the requirments of the constraint. + /// + bool Accepts(DateVersion.Version? version); +} diff --git a/src/PSRule.Types/Data/ISemanticVersionConstraint.cs b/src/PSRule.Types/Data/ISemanticVersionConstraint.cs new file mode 100644 index 0000000000..d74097af8f --- /dev/null +++ b/src/PSRule.Types/Data/ISemanticVersionConstraint.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace PSRule.Data; + +/// +/// A semantic version constraint. +/// +public interface ISemanticVersionConstraint +{ + /// + /// Determines if the semantic version meets the requirments of the constraint. + /// + bool Accepts(SemanticVersion.Version? version); +} diff --git a/src/PSRule.Types/Data/ModuleConstraint.cs b/src/PSRule.Types/Data/ModuleConstraint.cs index e163ec9b5a..bda9c70b85 100644 --- a/src/PSRule.Types/Data/ModuleConstraint.cs +++ b/src/PSRule.Types/Data/ModuleConstraint.cs @@ -1,31 +1,44 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Diagnostics; + namespace PSRule.Data; /// /// A version constraint for a PSRule module. /// -public sealed class ModuleConstraint +/// The name of the module. +/// The version constraint of the module. +/// Both and must not be null or empty. +[DebuggerDisplay("{Module}")] +public sealed class ModuleConstraint(string module, ISemanticVersionConstraint constraint) : ISemanticVersionConstraint { /// - /// Create a version constraint for a PSRule module. + /// The name of the module. /// - /// The name of the module. - /// The version constraint of the module. - public ModuleConstraint(string module, ISemanticVersionConstraint constraint) - { - Module = module; - Constraint = constraint; - } + public string Module { get; } = !string.IsNullOrEmpty(module) ? module : throw new ArgumentNullException(nameof(module)); /// - /// The name of the module. + /// The version constraint of the module. /// - public string Module { get; } + public ISemanticVersionConstraint Constraint { get; } = constraint ?? throw new ArgumentNullException(nameof(constraint)); + + /// + public bool Accepts(SemanticVersion.Version? version) => Constraint.Accepts(version); /// - /// The version constraint of the module. + /// Get a constraint that accepts any version of the specified module. /// - public ISemanticVersionConstraint Constraint { get; } + /// + /// Determines if pre-releases are accepted or only stable versions. + /// A . + public static ModuleConstraint Any(string module, bool includePrerelease = false) + { + return new ModuleConstraint + ( + module, + includePrerelease ? SemanticVersion.VersionConstraint.Any : SemanticVersion.VersionConstraint.AnyStable + ); + } } diff --git a/src/PSRule.Types/Data/SemanticVersion.cs b/src/PSRule.Types/Data/SemanticVersion.cs index 3d4d921754..0f0138bfb9 100644 --- a/src/PSRule.Types/Data/SemanticVersion.cs +++ b/src/PSRule.Types/Data/SemanticVersion.cs @@ -5,17 +5,6 @@ namespace PSRule.Data; -/// -/// A semantic version constraint. -/// -public interface ISemanticVersionConstraint -{ - /// - /// Determines if the semantic version meets the requirments of the constraint. - /// - bool Equals(SemanticVersion.Version version); -} - /// /// A helper for comparing semantic version strings. /// @@ -28,7 +17,7 @@ public static class SemanticVersion private const char VLOWER = 'v'; private const char GREATER = '>'; private const char LESS = '<'; - private const char SEPARATOR = '.'; + private const char DOT = '.'; private const char DASH = '-'; private const char PLUS = '+'; private const char ZERO = '0'; @@ -89,23 +78,36 @@ public sealed class VersionConstraint : ISemanticVersionConstraint { private List? _Constraints; private readonly string _Value; + private readonly bool _IncludePrerelease; + + /// + /// A version constraint that accepts any version including pre-releases. + /// + public static readonly VersionConstraint Any = new(string.Empty, includePrerelease: true); - internal VersionConstraint(string value) + /// + /// A version constraint that accepts any stable version. + /// + public static readonly VersionConstraint AnyStable = new(string.Empty, includePrerelease: false); + + internal VersionConstraint(string value, bool includePrerelease) { _Value = value; + _IncludePrerelease = includePrerelease; } /// - public bool Equals(Version version) + public bool Accepts(Version? version) { + if (version is null) return false; if (_Constraints == null || _Constraints.Count == 0) - return true; + return version.Stable || _IncludePrerelease; var match = false; var i = 0; while (!match && i < _Constraints.Count) { - var result = _Constraints[i].Equals(version); + var result = _Constraints[i].Accepts(version); // True OR if (result && _Constraints[i].Join == JoinOperator.Or) @@ -133,9 +135,21 @@ public bool Equals(Version version) return false; } + /// + public override string ToString() + { + return _Value; + } + + /// + public override int GetHashCode() + { + return _Value.GetHashCode(); + } + internal void Join(int major, int minor, int patch, PR prid, ComparisonOperator flag, JoinOperator join, bool includePrerelease) { - _Constraints ??= new List(); + _Constraints ??= []; _Constraints.Add(new ConstraintExpression( major, minor, @@ -146,18 +160,6 @@ internal void Join(int major, int minor, int patch, PR prid, ComparisonOperator includePrerelease )); } - - /// - public override string ToString() - { - return _Value; - } - - /// - public override int GetHashCode() - { - return _Value.GetHashCode(); - } } [DebuggerDisplay("{_Major}.{_Minor}.{_Patch}")] @@ -190,17 +192,18 @@ public static bool TryParse(string value, out ISemanticVersionConstraint constra return TryParseConstraint(value, out constraint); } - public bool Equals(System.Version version) + public bool Accepts(System.Version version) { - return Equals(version.Major, version.Minor, version.Build, null); + return Accepts(version.Major, version.Minor, version.Build, null); } - public bool Equals(Version version) + /// + public bool Accepts(Version? version) { - return Equals(version.Major, version.Minor, version.Patch, version.Prerelease); + return version is not null && Accepts(version.Major, version.Minor, version.Patch, version.Prerelease); } - public bool Equals(int major, int minor, int patch, PR? prid) + public bool Accepts(int major, int minor, int patch, PR? prid) { if (_Flag == ComparisonOperator.Equals) return EQ(major, minor, patch, prid); @@ -340,8 +343,12 @@ private static bool IsStable(PR? prid) /// /// A semantic version. /// + [DebuggerDisplay("{_VersionString}")] public sealed class Version : IComparable, IEquatable { + private string? _VersionString; + private string? _ShortVersionString; + /// /// The major part of the version. /// @@ -376,6 +383,11 @@ internal Version(int major, int minor, int patch, PR prerelease, string build) Build = build; } + /// + /// Determines if the version is stable or a pre-release. + /// + public bool Stable => Prerelease == null || Prerelease.Stable; + /// /// Try to parse a semantic version from a string. /// @@ -384,10 +396,20 @@ public static bool TryParse(string value, out Version? version) return TryParseVersion(value, out version); } - /// + /// + /// Get the version as a string. + /// public override string ToString() { - return string.Concat(Major, '.', Minor, '.', Patch); + return _VersionString ??= GetVersionString(simple: false); + } + + /// + /// Get the version as a string returning only the major.minor.patch part of the version. + /// + public string ToShortString() + { + return _ShortVersionString ??= GetVersionString(simple: true); } /// @@ -432,7 +454,7 @@ public override int GetHashCode() /// public bool Equals(Version? other) { - return other != null && + return other is not null && Equals(other.Major, other.Minor, other.Patch, other.Prerelease?.Value); } @@ -452,7 +474,7 @@ public bool Equals(int major, int minor, int patch, string? prerelease = null) /// public int CompareTo(Version? other) { - if (other == null) + if (other is null) return 1; if (Major != other.Major) @@ -461,7 +483,43 @@ public int CompareTo(Version? other) if (Minor != other.Minor) return Minor > other.Minor ? 16 : -16; - return Patch != other.Patch ? Patch > other.Patch ? 8 : -8 : 0; + if (Patch != other.Patch) + return Patch > other.Patch ? 8 : -8; + + return Prerelease != other.Prerelease ? PR.Compare(Prerelease, other.Prerelease) : 0; + } + + /// + /// Returns a version string. + /// + /// When true, only return the major.minor.patch version. + private string GetVersionString(bool simple = false) + { + var size = 5 + (!simple && Prerelease != null && !Prerelease.Stable ? 2 : 0) + (!simple && Build != null && Build.Length > 0 ? 2 : 0); + var parts = new object[size]; + + parts[0] = Major; + parts[1] = DOT; + parts[2] = Minor; + parts[3] = DOT; + parts[4] = Patch; + + if (size > 5) + { + var next = 5; + if (Prerelease != null && !Prerelease.Stable) + { + parts[next++] = DASH; + parts[next++] = Prerelease.Value; + } + + if (Build != null && Build.Length > 0) + { + parts[next++] = PLUS; + parts[next++] = Build; + } + } + return string.Concat(parts); } } @@ -469,10 +527,10 @@ public int CompareTo(Version? other) /// A semantic version pre-release identifier. /// [DebuggerDisplay("{Value}")] - public sealed class PR + public sealed class PR : IComparable, IEquatable { internal static readonly PR Empty = new(); - private static readonly char[] SEPARATORS = new char[] { SEPARATOR }; + private static readonly char[] SEPARATORS = new char[] { DOT }; private readonly string[]? _Identifiers; @@ -501,16 +559,16 @@ internal PR(string? value) /// /// Compare the pre-release identifer to another pre-release identifier. /// - public int CompareTo(PR? pr) + public int CompareTo(PR? other) { - if (pr == null || pr.Stable || pr._Identifiers == null) + if (other is null || other.Stable || other._Identifiers == null) return Stable ? 0 : -1; else if (Stable || _Identifiers == null) return 1; var i = -1; var left = _Identifiers; - var right = pr._Identifiers; + var right = other._Identifiers; while (++i < left.Length && i < right.Length) { @@ -543,10 +601,21 @@ public int CompareTo(PR? pr) return left.Length > right.Length ? 1 : -1; } + /// + public bool Equals(PR? other) + { + if (other is null) + return Stable; + + return Stable && other.Stable || + Value.Equals(other.Value); + } + + /// public override bool Equals(object obj) { - return obj is PR prerelease && Value.Equals(prerelease.Value); + return obj is PR other && Equals(other); } /// @@ -560,6 +629,18 @@ public override string ToString() { return Value.ToString(); } + + /// + /// Compare two instances. + /// + public static int Compare(PR pr1, PR pr2) + { + if (pr1 == pr2) return 0; + if (pr1 == null || pr1.Stable) return 1; + if (pr2 == null || pr2.Stable) return -1; + + return pr1.CompareTo(pr2); + } } [DebuggerDisplay("Current = {_Current}, Position = {_Position}, Value = {_Value}")] @@ -652,7 +733,7 @@ internal bool TryDigit(out int digit) internal bool TrySegments(out int[] segments) { - segments = new int[] { -1, -1, -1, -1 }; + segments = [-1, -1, -1, -1]; var segmentIndex = 0; SkipLeading(); while (!EOF) @@ -766,7 +847,7 @@ private static bool IsWildcard(char c) [DebuggerStepThrough()] private static bool IsSeparator(char c) { - return c == SEPARATOR; + return c == DOT; } [DebuggerStepThrough()] @@ -782,7 +863,7 @@ private static bool IsPrereleaseChar(char c, ref bool numeric) return true; numeric = false; - return char.IsDigit(c) || IsLetter(c) || c == DASH || c == SEPARATOR; + return char.IsDigit(c) || IsLetter(c) || c == DASH || c == DOT; } [DebuggerStepThrough()] @@ -819,7 +900,7 @@ private static bool IsLetter(char c) /// public static bool TryParseConstraint(string value, out ISemanticVersionConstraint constraint, bool includePrerelease = false) { - var c = new VersionConstraint(value); + var c = new VersionConstraint(value, includePrerelease); constraint = c; if (string.IsNullOrEmpty(value)) return true; diff --git a/src/PSRule.Types/Definitions/IResourceAnnotations.cs b/src/PSRule.Types/Definitions/IResourceAnnotations.cs index 4acc58b8f1..60e1f2e19f 100644 --- a/src/PSRule.Types/Definitions/IResourceAnnotations.cs +++ b/src/PSRule.Types/Definitions/IResourceAnnotations.cs @@ -7,5 +7,5 @@ namespace PSRule.Definitions; /// /// public interface IResourceAnnotations : IDictionary -{ +{ } diff --git a/src/PSRule.Types/Definitions/IResourceMetadata.cs b/src/PSRule.Types/Definitions/IResourceMetadata.cs index 416891a163..68e74937b3 100644 --- a/src/PSRule.Types/Definitions/IResourceMetadata.cs +++ b/src/PSRule.Types/Definitions/IResourceMetadata.cs @@ -11,5 +11,5 @@ public interface IResourceMetadata /// /// Annotations on the resource. /// - public IResourceAnnotations Annotations { get;} + public IResourceAnnotations Annotations { get; } } diff --git a/src/PSRule.Types/Definitions/ResourceHelpInfo.cs b/src/PSRule.Types/Definitions/ResourceHelpInfo.cs index 0bab43fe5c..7f22c458ff 100644 --- a/src/PSRule.Types/Definitions/ResourceHelpInfo.cs +++ b/src/PSRule.Types/Definitions/ResourceHelpInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Newtonsoft.Json; diff --git a/src/PSRule.Types/Definitions/ResourceHelper.cs b/src/PSRule.Types/Definitions/ResourceHelper.cs index d56611e438..4815ccf681 100644 --- a/src/PSRule.Types/Definitions/ResourceHelper.cs +++ b/src/PSRule.Types/Definitions/ResourceHelper.cs @@ -11,7 +11,7 @@ internal static class ResourceHelper private const char SCOPE_SEPARATOR = '\\'; - internal const string STANDALONE_SCOPENAME = "."; + internal const string STANDALONE_SCOPE_NAME = "."; internal static string GetIdString(string scope, string name) { @@ -63,7 +63,7 @@ internal static void ParseIdString(string id, out string? scope, out string? nam internal static ResourceId GetRuleId(string? defaultScope, string name, ResourceIdKind kind) { - defaultScope ??= STANDALONE_SCOPENAME; + defaultScope ??= STANDALONE_SCOPE_NAME; return name.IndexOf(SCOPE_SEPARATOR) > 0 ? ResourceId.Parse(name, kind) : new ResourceId(defaultScope, name, kind); } @@ -87,6 +87,6 @@ internal static SeverityLevel GetLevel(SeverityLevel? level) internal static string NormalizeScope(string? scope) { - return scope == null || string.IsNullOrEmpty(scope) ? STANDALONE_SCOPENAME : scope; + return scope == null || string.IsNullOrEmpty(scope) ? STANDALONE_SCOPE_NAME : scope; } } diff --git a/src/PSRule.Types/Definitions/ResourceId.cs b/src/PSRule.Types/Definitions/ResourceId.cs index 593bb0c386..50eafff381 100644 --- a/src/PSRule.Types/Definitions/ResourceId.cs +++ b/src/PSRule.Types/Definitions/ResourceId.cs @@ -134,7 +134,7 @@ private static bool TryParse(string id, ResourceIdKind kind, out ResourceId? val if (string.IsNullOrEmpty(id) || !TryParse(id, out var scope, out var name) || name == null) return false; - scope ??= ResourceHelper.STANDALONE_SCOPENAME; + scope ??= ResourceHelper.STANDALONE_SCOPE_NAME; value = new ResourceId(id, scope, name, kind); return true; } diff --git a/src/PSRule.Types/Definitions/ResourceIdKind.cs b/src/PSRule.Types/Definitions/ResourceIdKind.cs index 980d2f0577..ad0d96a593 100644 --- a/src/PSRule.Types/Definitions/ResourceIdKind.cs +++ b/src/PSRule.Types/Definitions/ResourceIdKind.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Definitions; diff --git a/src/PSRule.Types/Emitters/InternalFileStream.cs b/src/PSRule.Types/Emitters/InternalFileStream.cs index 1665c31131..42510e4925 100644 --- a/src/PSRule.Types/Emitters/InternalFileStream.cs +++ b/src/PSRule.Types/Emitters/InternalFileStream.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Emitters; @@ -8,4 +8,4 @@ internal class InternalFileStream public InternalFileStream() { } -} \ No newline at end of file +} diff --git a/src/PSRule.Types/Options/BaselineOption.cs b/src/PSRule.Types/Options/BaselineOption.cs index 3444dd5f9d..a796e89b39 100644 --- a/src/PSRule.Types/Options/BaselineOption.cs +++ b/src/PSRule.Types/Options/BaselineOption.cs @@ -6,23 +6,6 @@ namespace PSRule.Options; -/// -/// Options that configure baselines. -/// -/// -/// See . -/// -public interface IBaselineOption : IOption -{ - /// - /// A mapping of baseline group names to baselines. - /// - /// - /// See . - /// - StringArrayMap? Group { get; } -} - /// /// Options that configure baselines. /// diff --git a/src/PSRule.Types/Options/IBaselineOption.cs b/src/PSRule.Types/Options/IBaselineOption.cs new file mode 100644 index 0000000000..cea4b24658 --- /dev/null +++ b/src/PSRule.Types/Options/IBaselineOption.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Data; + +namespace PSRule.Options; + +/// +/// Options that configure baselines. +/// +/// +/// See . +/// +public interface IBaselineOption : IOption +{ + /// + /// A mapping of baseline group names to baselines. + /// + /// + /// See . + /// + StringArrayMap? Group { get; } +} diff --git a/src/PSRule.Types/Options/IOption.cs b/src/PSRule.Types/Options/IOption.cs index 38ecc0faf8..355bf9d0d6 100644 --- a/src/PSRule.Types/Options/IOption.cs +++ b/src/PSRule.Types/Options/IOption.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Options; diff --git a/src/PSRule.Types/Options/IOptionContext.cs b/src/PSRule.Types/Options/IOptionContext.cs index 0e94c7813b..ab6eacf737 100644 --- a/src/PSRule.Types/Options/IOptionContext.cs +++ b/src/PSRule.Types/Options/IOptionContext.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Options; diff --git a/src/PSRule.Types/Properties/AssemblyInfo.cs b/src/PSRule.Types/Properties/AssemblyInfo.cs index d084029391..bbd0530f63 100644 --- a/src/PSRule.Types/Properties/AssemblyInfo.cs +++ b/src/PSRule.Types/Properties/AssemblyInfo.cs @@ -6,3 +6,4 @@ [assembly: InternalsVisibleTo("Microsoft.PSRule.Core")] [assembly: InternalsVisibleTo("Microsoft.PSRule.Tool")] [assembly: InternalsVisibleTo("PSRule.Tests")] +[assembly: InternalsVisibleTo("PSRule.Types.Tests")] diff --git a/src/PSRule.Types/Runtime/NullLogger_T.cs b/src/PSRule.Types/Runtime/NullLogger_T.cs index 71bd03e12c..d2d0187a2b 100644 --- a/src/PSRule.Types/Runtime/NullLogger_T.cs +++ b/src/PSRule.Types/Runtime/NullLogger_T.cs @@ -22,6 +22,6 @@ public bool IsEnabled(LogLevel logLevel) /// public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { - + } } diff --git a/src/PSRule/Annotations/CommentMetadata.cs b/src/PSRule/Annotations/CommentMetadata.cs index 03a2d773cd..dcad0a7a87 100644 --- a/src/PSRule/Annotations/CommentMetadata.cs +++ b/src/PSRule/Annotations/CommentMetadata.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Diagnostics; diff --git a/src/PSRule/Commands/AssertAllOfCommand.cs b/src/PSRule/Commands/AssertAllOfCommand.cs index a25d390709..f258233852 100644 --- a/src/PSRule/Commands/AssertAllOfCommand.cs +++ b/src/PSRule/Commands/AssertAllOfCommand.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/src/PSRule/Commands/AssertAnyOfCommand.cs b/src/PSRule/Commands/AssertAnyOfCommand.cs index 6e1bece6a1..427cb0b905 100644 --- a/src/PSRule/Commands/AssertAnyOfCommand.cs +++ b/src/PSRule/Commands/AssertAnyOfCommand.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/src/PSRule/Commands/ExportConventionCommand.cs b/src/PSRule/Commands/ExportConventionCommand.cs index 7ce5ef4fce..1a5e75be5b 100644 --- a/src/PSRule/Commands/ExportConventionCommand.cs +++ b/src/PSRule/Commands/ExportConventionCommand.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Diagnostics.CodeAnalysis; using System.Management.Automation; using PSRule.Definitions; using PSRule.Definitions.Conventions; diff --git a/src/PSRule/Commands/InvokeConventionCommand.cs b/src/PSRule/Commands/InvokeConventionCommand.cs index 90b254e5a3..13516ecf92 100644 --- a/src/PSRule/Commands/InvokeConventionCommand.cs +++ b/src/PSRule/Commands/InvokeConventionCommand.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/src/PSRule/Commands/InvokeRuleBlockCommand.cs b/src/PSRule/Commands/InvokeRuleBlockCommand.cs index edbb6db9fa..e0003fefc2 100644 --- a/src/PSRule/Commands/InvokeRuleBlockCommand.cs +++ b/src/PSRule/Commands/InvokeRuleBlockCommand.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System.Management.Automation; -using PSRule.Configuration; using PSRule.Definitions; using PSRule.Pipeline; using PSRule.Resources; @@ -103,7 +102,7 @@ private bool AcceptsType() if (Type == null) return true; - var comparer = RunspaceContext.CurrentThread.LanguageScope.Binding.GetComparer(); + var comparer = RunspaceContext.CurrentThread.LanguageScope.GetBindingComparer(); var targetType = RunspaceContext.CurrentThread.RuleRecord.TargetType; for (var i = 0; i < Type.Length; i++) { diff --git a/src/PSRule/Commands/LanguageKeywords.cs b/src/PSRule/Commands/LanguageKeywords.cs index bea69610f8..1c28f6ad6c 100644 --- a/src/PSRule/Commands/LanguageKeywords.cs +++ b/src/PSRule/Commands/LanguageKeywords.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Commands; diff --git a/src/PSRule/Commands/NewRuleDefinitionCommand.cs b/src/PSRule/Commands/NewRuleDefinitionCommand.cs index 7114b71187..7a1bdb10fe 100644 --- a/src/PSRule/Commands/NewRuleDefinitionCommand.cs +++ b/src/PSRule/Commands/NewRuleDefinitionCommand.cs @@ -14,6 +14,7 @@ namespace PSRule.Commands; /// /// A Rule language block. +/// When processed, creates a rule language element that can be used later during rule execution. /// [Cmdlet(VerbsCommon.New, RuleLanguageNouns.RuleDefinition)] internal sealed class NewRuleDefinitionCommand : LanguageBlock @@ -89,7 +90,7 @@ internal sealed class NewRuleDefinitionCommand : LanguageBlock public string[] Alias { get; set; } /// - /// An optional reference identifer for the resource. + /// An optional reference identifier for the resource. /// [Parameter(Mandatory = false)] [ValidateLength(3, 128)] diff --git a/src/PSRule/Commands/RuleLanguageNouns.cs b/src/PSRule/Commands/RuleLanguageNouns.cs index 10d7f90880..7006349b4d 100644 --- a/src/PSRule/Commands/RuleLanguageNouns.cs +++ b/src/PSRule/Commands/RuleLanguageNouns.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Commands; diff --git a/src/PSRule/Common/BaselineJsonSerializationMapper.cs b/src/PSRule/Common/BaselineJsonSerializationMapper.cs index ba4bdc21d8..d12441baed 100644 --- a/src/PSRule/Common/BaselineJsonSerializationMapper.cs +++ b/src/PSRule/Common/BaselineJsonSerializationMapper.cs @@ -47,7 +47,6 @@ private static void MapBaselineSpec(JsonWriter writer, JsonSerializer serializer MapPropertyName(writer, propertyName); writer.WriteStartObject(); - MapProperty(writer, serializer, nameof(baselineSpec.Binding), baselineSpec.Binding); MapProperty(writer, serializer, nameof(baselineSpec.Configuration), baselineSpec.Configuration); MapProperty(writer, nameof(baselineSpec.Convention), baselineSpec.Convention); MapProperty(writer, serializer, nameof(baselineSpec.Rule), baselineSpec.Rule); diff --git a/src/PSRule/Common/BaselineYamlSerializationMapper.cs b/src/PSRule/Common/BaselineYamlSerializationMapper.cs index aa23557d4e..6e19c27c8b 100644 --- a/src/PSRule/Common/BaselineYamlSerializationMapper.cs +++ b/src/PSRule/Common/BaselineYamlSerializationMapper.cs @@ -50,7 +50,6 @@ private static void MapBaselineSpec(IEmitter emitter, string propertyName, Basel MapPropertyName(emitter, propertyName); emitter.Emit(new MappingStart()); - MapProperty(emitter, nameof(baselineSpec.Binding), baselineSpec.Binding); MapProperty(emitter, nameof(baselineSpec.Configuration), baselineSpec.Configuration); MapProperty(emitter, nameof(baselineSpec.Convention), baselineSpec.Convention); MapProperty(emitter, nameof(baselineSpec.Rule), baselineSpec.Rule); diff --git a/src/PSRule/Common/ListExtensions.cs b/src/PSRule/Common/ListExtensions.cs index 675f0c784b..558504fc5b 100644 --- a/src/PSRule/Common/ListExtensions.cs +++ b/src/PSRule/Common/ListExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule; diff --git a/src/PSRule/Common/LoggerExtensions.cs b/src/PSRule/Common/LoggerExtensions.cs index 3af2e689d4..cee7fa2c0a 100644 --- a/src/PSRule/Common/LoggerExtensions.cs +++ b/src/PSRule/Common/LoggerExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Management.Automation; using PSRule.Definitions; using PSRule.Pipeline; using PSRule.Resources; diff --git a/src/PSRule/Common/RuleOutcomeExtensions.cs b/src/PSRule/Common/RuleOutcomeExtensions.cs index f195c62de6..e7a3b8f6de 100644 --- a/src/PSRule/Common/RuleOutcomeExtensions.cs +++ b/src/PSRule/Common/RuleOutcomeExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Rules; diff --git a/src/PSRule/Configuration/BaselineOption.cs b/src/PSRule/Configuration/BaselineOption.cs index 786cee2cc9..1ca945dc34 100644 --- a/src/PSRule/Configuration/BaselineOption.cs +++ b/src/PSRule/Configuration/BaselineOption.cs @@ -26,13 +26,10 @@ internal sealed class BaselineInline : BaselineOption, IBaselineV1Spec { public BaselineInline() { - Binding = new BindingOption(); Configuration = new ConfigurationOption(); Rule = new RuleOption(); } - public BindingOption Binding { get; set; } - public ConfigurationOption Configuration { get; set; } public ConventionOption Convention { get; set; } @@ -87,28 +84,11 @@ public static BaselineOption FromString(string value) return string.IsNullOrEmpty(value) ? null : new BaselineRef(value); } + /// + /// Load from environment variables. + /// internal static void Load(IBaselineV1Spec option) { - // Binding.Field - currently not supported - - if (Environment.TryBool("PSRULE_BINDING_IGNORECASE", out var ignoreCase)) - option.Binding.IgnoreCase = ignoreCase; - - if (Environment.TryString("PSRULE_BINDING_NAMESEPARATOR", out var nameSeparator)) - option.Binding.NameSeparator = nameSeparator; - - if (Environment.TryBool("PSRULE_BINDING_PREFERTARGETINFO", out var preferTargetInfo)) - option.Binding.PreferTargetInfo = preferTargetInfo; - - if (Environment.TryStringArray("PSRULE_BINDING_TARGETNAME", out var targetName)) - option.Binding.TargetName = targetName; - - if (Environment.TryStringArray("PSRULE_BINDING_TARGETTYPE", out var targetType)) - option.Binding.TargetType = targetType; - - if (Environment.TryBool("PSRULE_BINDING_USEQUALIFIEDNAME", out var useQualifiedName)) - option.Binding.UseQualifiedName = useQualifiedName; - if (Environment.TryString("PSRULE_RULE_BASELINE", out var baseline)) option.Rule.Baseline = baseline; @@ -128,33 +108,10 @@ internal static void Load(IBaselineV1Spec option) } /// - /// Load matching values + /// Load from a dictionary. /// - /// A baseline options object to load. - /// One or more indexed properties. internal static void Load(IBaselineV1Spec option, Dictionary properties) { - if (properties.TryPopValue("Binding.Field", out Hashtable map)) - option.Binding.Field = new FieldMap(map); - - if (properties.TryPopBool("Binding.IgnoreCase", out var ignoreCase)) - option.Binding.IgnoreCase = ignoreCase; - - if (properties.TryPopString("Binding.NameSeparator", out var nameSeparator)) - option.Binding.NameSeparator = nameSeparator; - - if (properties.TryPopBool("Binding.PreferTargetInfo", out var preferTargetInfo)) - option.Binding.PreferTargetInfo = preferTargetInfo; - - if (properties.TryPopStringArray("Binding.TargetName", out var targetName)) - option.Binding.TargetName = targetName; - - if (properties.TryPopStringArray("Binding.TargetType", out var targetType)) - option.Binding.TargetType = targetType; - - if (properties.TryPopValue("Binding.UseQualifiedName", out bool useQualifiedName)) - option.Binding.UseQualifiedName = useQualifiedName; - if (properties.TryPopString("Rule.Baseline", out var baseline)) option.Rule.Baseline = baseline; diff --git a/src/PSRule/Configuration/BindingOption.cs b/src/PSRule/Configuration/BindingOption.cs index b721179b62..fb8e0fad8c 100644 --- a/src/PSRule/Configuration/BindingOption.cs +++ b/src/PSRule/Configuration/BindingOption.cs @@ -1,14 +1,18 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Collections; using System.ComponentModel; namespace PSRule.Configuration; /// -/// Options that affect property binding of TargetName and TargetType. +/// Options that configure property binding. /// -public sealed class BindingOption : IEquatable +/// +/// See . +/// +public sealed class BindingOption : IEquatable, IBindingOption { private const bool DEFAULT_IGNORECASE = true; private const bool DEFAULT_PREFERTARGETINFO = false; @@ -172,4 +176,57 @@ internal static BindingOption Combine(BindingOption o1, BindingOption o2) /// [DefaultValue(null)] public bool? UseQualifiedName { get; set; } + + /// + /// Load from environment variables. + /// + internal void Load() + { + // Binding.Field - currently not supported + + if (Environment.TryBool("PSRULE_BINDING_IGNORECASE", out var ignoreCase)) + IgnoreCase = ignoreCase; + + if (Environment.TryString("PSRULE_BINDING_NAMESEPARATOR", out var nameSeparator)) + NameSeparator = nameSeparator; + + if (Environment.TryBool("PSRULE_BINDING_PREFERTARGETINFO", out var preferTargetInfo)) + PreferTargetInfo = preferTargetInfo; + + if (Environment.TryStringArray("PSRULE_BINDING_TARGETNAME", out var targetName)) + TargetName = targetName; + + if (Environment.TryStringArray("PSRULE_BINDING_TARGETTYPE", out var targetType)) + TargetType = targetType; + + if (Environment.TryBool("PSRULE_BINDING_USEQUALIFIEDNAME", out var useQualifiedName)) + UseQualifiedName = useQualifiedName; + } + + /// + /// Load from a dictionary. + /// + internal void Load(Dictionary index) + { + if (index.TryPopValue("Binding.Field", out Hashtable map)) + Field = new FieldMap(map); + + if (index.TryPopBool("Binding.IgnoreCase", out var ignoreCase)) + IgnoreCase = ignoreCase; + + if (index.TryPopString("Binding.NameSeparator", out var nameSeparator)) + NameSeparator = nameSeparator; + + if (index.TryPopBool("Binding.PreferTargetInfo", out var preferTargetInfo)) + PreferTargetInfo = preferTargetInfo; + + if (index.TryPopStringArray("Binding.TargetName", out var targetName)) + TargetName = targetName; + + if (index.TryPopStringArray("Binding.TargetType", out var targetType)) + TargetType = targetType; + + if (index.TryPopValue("Binding.UseQualifiedName", out bool useQualifiedName)) + UseQualifiedName = useQualifiedName; + } } diff --git a/src/PSRule/Configuration/IBindingOption.cs b/src/PSRule/Configuration/IBindingOption.cs new file mode 100644 index 0000000000..5c7c58ab3c --- /dev/null +++ b/src/PSRule/Configuration/IBindingOption.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Options; + +namespace PSRule.Configuration; + +/// +/// Options that configure property binding. +/// +/// +/// See . +/// +public interface IBindingOption : IOption +{ + /// + /// One or more custom fields to bind. + /// + /// + /// See . + /// + FieldMap Field { get; } + + /// + /// Determines if custom binding uses ignores case when matching properties. + /// + /// + /// See . + /// + bool? IgnoreCase { get; } + + /// + /// Configures the separator to use for building a qualified name. + /// + /// + /// See . + /// + string NameSeparator { get; } + + /// + /// Determines if binding prefers target info provided by the object over custom configuration. + /// + /// + /// See . + /// + bool? PreferTargetInfo { get; } + + /// + /// Property names to use to bind TargetName. + /// + /// + /// See . + /// + string[] TargetName { get; } + + /// + /// Property names to use to bind TargetType. + /// + /// + /// See . + /// + string[] TargetType { get; } + + /// + /// Determines if a qualified TargetName is used. + /// + /// + /// See . + /// + bool? UseQualifiedName { get; } +} diff --git a/src/PSRule/Configuration/OutputStyle.cs b/src/PSRule/Configuration/OutputStyle.cs index 5ba566aa57..2c8f6e10a3 100644 --- a/src/PSRule/Configuration/OutputStyle.cs +++ b/src/PSRule/Configuration/OutputStyle.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Newtonsoft.Json; diff --git a/src/PSRule/Configuration/PSRuleOption.cs b/src/PSRule/Configuration/PSRuleOption.cs index e442dc74b5..0217f170f8 100644 --- a/src/PSRule/Configuration/PSRuleOption.cs +++ b/src/PSRule/Configuration/PSRuleOption.cs @@ -4,9 +4,7 @@ using System.Collections; using System.Diagnostics; using System.Management.Automation; -using Newtonsoft.Json; using PSRule.Converters.Yaml; -using PSRule.Definitions; using PSRule.Definitions.Baselines; using PSRule.Pipeline; using PSRule.Resources; @@ -59,7 +57,6 @@ public PSRuleOption() Input = new InputOption(); Logging = new LoggingOption(); Output = new OutputOption(); - Pipeline = new PipelineHook(); Repository = new RepositoryOption(); Requires = new RequiresOption(); Rule = new RuleOption(); @@ -80,7 +77,6 @@ private PSRuleOption(string sourcePath, PSRuleOption option) Input = new InputOption(option?.Input); Logging = new LoggingOption(option?.Logging); Output = new OutputOption(option?.Output); - Pipeline = new PipelineHook(option?.Pipeline); Repository = new RepositoryOption(option?.Repository); Requires = new RequiresOption(option?.Requires); Rule = new RuleOption(option?.Rule); @@ -132,13 +128,6 @@ private PSRuleOption(string sourcePath, PSRuleOption option) /// public OutputOption Output { get; set; } - /// - /// Configures pipeline hooks. - /// - [YamlIgnore] - [JsonIgnore] - public PipelineHook Pipeline { get; set; } - /// /// Options for repository properties that are used by PSRule. /// @@ -336,6 +325,7 @@ private static PSRuleOption FromEnvironment(PSRuleOption option) // Start loading matching values option.Baseline.Load(); + option.Binding.Load(); option.Convention.Load(); option.Execution.Load(); option.Include.Load(); @@ -365,6 +355,7 @@ public static PSRuleOption FromHashtable(Hashtable hashtable) // Start loading matching values var index = BuildIndex(hashtable); option.Baseline.Load(index); + option.Binding.Load(index); option.Convention.Load(index); option.Execution.Load(index); option.Include.Load(index); @@ -437,7 +428,6 @@ public bool Equals(PSRuleOption other) Logging == other.Logging && Output == other.Output && Suppression == other.Suppression && - Pipeline == other.Pipeline && Repository == other.Repository && Rule == other.Rule; } @@ -458,7 +448,6 @@ public override int GetHashCode() hash = hash * 23 + (Logging != null ? Logging.GetHashCode() : 0); hash = hash * 23 + (Output != null ? Output.GetHashCode() : 0); hash = hash * 23 + (Suppression != null ? Suppression.GetHashCode() : 0); - hash = hash * 23 + (Pipeline != null ? Pipeline.GetHashCode() : 0); hash = hash * 23 + (Repository != null ? Repository.GetHashCode() : 0); hash = hash * 23 + (Rule != null ? Rule.GetHashCode() : 0); return hash; diff --git a/src/PSRule/Configuration/PipelineHook.cs b/src/PSRule/Configuration/PipelineHook.cs index 249166458b..b649346a5f 100644 --- a/src/PSRule/Configuration/PipelineHook.cs +++ b/src/PSRule/Configuration/PipelineHook.cs @@ -10,38 +10,3 @@ namespace PSRule.Configuration; internal delegate string BindTargetMethod(string[] propertyNames, bool caseSensitive, bool preferTargetInfo, object targetObject, out string path); internal delegate string BindTargetFunc(string[] propertyNames, bool caseSensitive, bool preferTargetInfo, object targetObject, BindTargetMethod next, out string path); - -/// -/// Hooks that provide customize pipeline execution. -/// -public sealed class PipelineHook -{ - /// - /// Create an empty set of pipeline hooks. - /// - public PipelineHook() - { - BindTargetName = new List(); - BindTargetType = new List(); - } - - /// - /// Create pipeline hooks based on an existing option instance. - /// - /// An existing pipeline hook option. - public PipelineHook(PipelineHook option) - { - BindTargetName = option?.BindTargetName ?? new List(); - BindTargetType = option?.BindTargetType ?? new List(); - } - - /// - /// One or more custom functions to use to bind TargetName of a pipeline object. - /// - public List BindTargetName { get; set; } - - /// - /// One or more custom functions to use to bind TargetType of a pipeline object. - /// - public List BindTargetType { get; set; } -} diff --git a/src/PSRule/Data/IInputFileInfoCollection.cs b/src/PSRule/Data/IInputFileInfoCollection.cs index 79dbc34a15..831feca3be 100644 --- a/src/PSRule/Data/IInputFileInfoCollection.cs +++ b/src/PSRule/Data/IInputFileInfoCollection.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Data; diff --git a/src/PSRule/Data/TargetIssueCollection.cs b/src/PSRule/Data/TargetIssueCollection.cs index d78af94ce6..c2572d2724 100644 --- a/src/PSRule/Data/TargetIssueCollection.cs +++ b/src/PSRule/Data/TargetIssueCollection.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Data; diff --git a/src/PSRule/Data/TargetSourceCollection.cs b/src/PSRule/Data/TargetSourceCollection.cs index 3a82339429..5e0842693e 100644 --- a/src/PSRule/Data/TargetSourceCollection.cs +++ b/src/PSRule/Data/TargetSourceCollection.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Data; diff --git a/src/PSRule/Definitions/AnnotatedExtensions.cs b/src/PSRule/Definitions/AnnotatedExtensions.cs index f0a3f9b8ed..8e121061b0 100644 --- a/src/PSRule/Definitions/AnnotatedExtensions.cs +++ b/src/PSRule/Definitions/AnnotatedExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Definitions; diff --git a/src/PSRule/Definitions/Baselines/BaselineFilter.cs b/src/PSRule/Definitions/Baselines/BaselineFilter.cs index 4101b0d742..db2528ab47 100644 --- a/src/PSRule/Definitions/Baselines/BaselineFilter.cs +++ b/src/PSRule/Definitions/Baselines/BaselineFilter.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/src/PSRule/Definitions/Baselines/BaselineRef.cs b/src/PSRule/Definitions/Baselines/BaselineRef.cs index 19f909c9e5..1a2e56f868 100644 --- a/src/PSRule/Definitions/Baselines/BaselineRef.cs +++ b/src/PSRule/Definitions/Baselines/BaselineRef.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Pipeline; diff --git a/src/PSRule/Definitions/Baselines/BaselineSpec.cs b/src/PSRule/Definitions/Baselines/BaselineSpec.cs index 2550f3549f..6753ee90d8 100644 --- a/src/PSRule/Definitions/Baselines/BaselineSpec.cs +++ b/src/PSRule/Definitions/Baselines/BaselineSpec.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Configuration; @@ -10,9 +10,6 @@ namespace PSRule.Definitions.Baselines; /// public sealed class BaselineSpec : Spec, IBaselineV1Spec { - /// - public BindingOption Binding { get; set; } - /// public ConfigurationOption Configuration { get; set; } diff --git a/src/PSRule/Definitions/Baselines/IBaselineV1Spec.cs b/src/PSRule/Definitions/Baselines/IBaselineV1Spec.cs index cf23413366..7024cdf25b 100644 --- a/src/PSRule/Definitions/Baselines/IBaselineV1Spec.cs +++ b/src/PSRule/Definitions/Baselines/IBaselineV1Spec.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Configuration; @@ -10,11 +10,6 @@ namespace PSRule.Definitions.Baselines; /// internal interface IBaselineV1Spec { - /// - /// Options that affect property binding. - /// - BindingOption Binding { get; set; } - /// /// Allows configuration key/ values to be specified that can be used within rule definitions. /// diff --git a/src/PSRule/Definitions/Conventions/ConventionFilter.cs b/src/PSRule/Definitions/Conventions/ConventionFilter.cs index 30c767ee16..b3c41a68e6 100644 --- a/src/PSRule/Definitions/Conventions/ConventionFilter.cs +++ b/src/PSRule/Definitions/Conventions/ConventionFilter.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/src/PSRule/Definitions/Expressions/LanguageExpressionBuilder.cs b/src/PSRule/Definitions/Expressions/LanguageExpressionBuilder.cs index 706e96d8bf..bee6f85b86 100644 --- a/src/PSRule/Definitions/Expressions/LanguageExpressionBuilder.cs +++ b/src/PSRule/Definitions/Expressions/LanguageExpressionBuilder.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System.Diagnostics; -using PSRule.Configuration; using PSRule.Resources; using PSRule.Runtime; @@ -287,7 +286,7 @@ private static bool AcceptsType(string[] type) if (type == null) return true; - var comparer = RunspaceContext.CurrentThread.LanguageScope.Binding.GetComparer(); + var comparer = RunspaceContext.CurrentThread.LanguageScope.GetBindingComparer(); var targetType = RunspaceContext.CurrentThread.RuleRecord.TargetType; for (var i = 0; i < type.Length; i++) { @@ -322,7 +321,7 @@ private static bool AcceptsRule(string[] rule) var context = RunspaceContext.CurrentThread; - var stringComparer = context.LanguageScope.Binding.GetComparer(); + var stringComparer = context.LanguageScope.GetBindingComparer(); var resourceIdComparer = ResourceIdEqualityComparer.Default; var ruleRecord = context.RuleRecord; diff --git a/src/PSRule/Definitions/Expressions/LanguageExpressions.cs b/src/PSRule/Definitions/Expressions/LanguageExpressions.cs index 9c0c0785af..fb1bd84974 100644 --- a/src/PSRule/Definitions/Expressions/LanguageExpressions.cs +++ b/src/PSRule/Definitions/Expressions/LanguageExpressions.cs @@ -1128,8 +1128,8 @@ internal static bool Version(IExpressionContext context, ExpressionInfo info, ob if (!SemanticVersion.TryParseConstraint(expectedValue, out var constraint, includePrerelease)) throw new RuleException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.VersionConstraintInvalid, expectedValue)); - if (constraint != null && !constraint.Equals(actualVersion)) - return Fail(context, operand, ReasonStrings.VersionContraint, actualVersion, constraint); + if (constraint != null && !constraint.Accepts(actualVersion)) + return Fail(context, operand, ReasonStrings.VersionConstraint, actualVersion, constraint); return Pass(); } @@ -1153,8 +1153,8 @@ internal static bool APIVersion(IExpressionContext context, ExpressionInfo info, if (!DateVersion.TryParseConstraint(expectedValue, out var constraint, includePrerelease)) throw new RuleException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.VersionConstraintInvalid, expectedValue)); - if (constraint != null && !constraint.Equals(actualVersion)) - return Fail(context, operand, ReasonStrings.VersionContraint, actualVersion, constraint); + if (constraint != null && !constraint.Accepts(actualVersion)) + return Fail(context, operand, ReasonStrings.VersionConstraint, actualVersion, constraint); return Pass(); } @@ -1354,7 +1354,7 @@ private static bool TryScope(IExpressionContext context, LanguageExpression.Prop if (svalue != DOT || context?.Context?.LanguageScope == null) return Invalid(context, svalue); - if (!context.Context.LanguageScope.TryGetScope(o, out var scope)) + if (!context.Context.TryGetScope(o, out var scope)) return Invalid(context, svalue); operand = Operand.FromScope(scope); diff --git a/src/PSRule/Definitions/Expressions/Primitives.cs b/src/PSRule/Definitions/Expressions/Primitives.cs index 4a5b2f9563..fe894fe70f 100644 --- a/src/PSRule/Definitions/Expressions/Primitives.cs +++ b/src/PSRule/Definitions/Expressions/Primitives.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System.Diagnostics; -using PSRule.Pipeline; namespace PSRule.Definitions.Expressions; diff --git a/src/PSRule/Definitions/IConditionResult.cs b/src/PSRule/Definitions/IConditionResult.cs index 58263aea5f..fa467c33f0 100644 --- a/src/PSRule/Definitions/IConditionResult.cs +++ b/src/PSRule/Definitions/IConditionResult.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Definitions; diff --git a/src/PSRule/Definitions/IDetailedRuleResultV2.cs b/src/PSRule/Definitions/IDetailedRuleResultV2.cs index 404c369c8c..fa2f3dcf46 100644 --- a/src/PSRule/Definitions/IDetailedRuleResultV2.cs +++ b/src/PSRule/Definitions/IDetailedRuleResultV2.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections; diff --git a/src/PSRule/Definitions/IResourceDiscoveryContext.cs b/src/PSRule/Definitions/IResourceDiscoveryContext.cs new file mode 100644 index 0000000000..8a667303f0 --- /dev/null +++ b/src/PSRule/Definitions/IResourceDiscoveryContext.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Pipeline; +using PSRule.Runtime; + +namespace PSRule.Definitions; + +#nullable enable + +/// +/// A context that is used for discovery of resources. +/// +internal interface IResourceDiscoveryContext +{ + /// + /// A writer to log messages. + /// + IPipelineWriter Writer { get; } + + /// + /// Enter a language scope. + /// + /// The source file to enter. + void EnterLanguageScope(ISourceFile file); + + /// + /// Exit a language scope. + /// + /// The source file to exit. + void ExitLanguageScope(ISourceFile file); + + void PushScope(RunspaceScope scope); + + void PopScope(RunspaceScope scope); +} + +#nullable restore diff --git a/src/PSRule/Definitions/IResourceFilter.cs b/src/PSRule/Definitions/IResourceFilter.cs index d11355235a..daaea799c0 100644 --- a/src/PSRule/Definitions/IResourceFilter.cs +++ b/src/PSRule/Definitions/IResourceFilter.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Definitions; diff --git a/src/PSRule/Definitions/IResultDetailV2.cs b/src/PSRule/Definitions/IResultDetailV2.cs index e25b105655..daae852ded 100644 --- a/src/PSRule/Definitions/IResultDetailV2.cs +++ b/src/PSRule/Definitions/IResultDetailV2.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Definitions; diff --git a/src/PSRule/Definitions/IResultReasonV2.cs b/src/PSRule/Definitions/IResultReasonV2.cs index 2f0c29287e..db518222dc 100644 --- a/src/PSRule/Definitions/IResultReasonV2.cs +++ b/src/PSRule/Definitions/IResultReasonV2.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Definitions; diff --git a/src/PSRule/Definitions/IScriptResourceDiscoveryContext.cs b/src/PSRule/Definitions/IScriptResourceDiscoveryContext.cs new file mode 100644 index 0000000000..55283fe4ae --- /dev/null +++ b/src/PSRule/Definitions/IScriptResourceDiscoveryContext.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Management.Automation; +using PSRule.Options; + +namespace PSRule.Definitions; + +/// +/// A context that is used for discovery of resources defined as script blocks. +/// +internal interface IScriptResourceDiscoveryContext : IResourceDiscoveryContext +{ + PowerShell GetPowerShell(); + + ExecutionOption GetExecutionOption(); +} diff --git a/src/PSRule/Definitions/ISpecDescriptor.cs b/src/PSRule/Definitions/ISpecDescriptor.cs index 1cfe30017c..f6c441063c 100644 --- a/src/PSRule/Definitions/ISpecDescriptor.cs +++ b/src/PSRule/Definitions/ISpecDescriptor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Annotations; diff --git a/src/PSRule/Definitions/ISuppressionInfoComparer.cs b/src/PSRule/Definitions/ISuppressionInfoComparer.cs index 600138f2d9..f84ee4bd11 100644 --- a/src/PSRule/Definitions/ISuppressionInfoComparer.cs +++ b/src/PSRule/Definitions/ISuppressionInfoComparer.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Definitions; diff --git a/src/PSRule/Definitions/InternalResource.cs b/src/PSRule/Definitions/InternalResource.cs index 681d66c6ba..bfe643d767 100644 --- a/src/PSRule/Definitions/InternalResource.cs +++ b/src/PSRule/Definitions/InternalResource.cs @@ -17,7 +17,7 @@ namespace PSRule.Definitions; private protected InternalResource(ResourceKind kind, string apiVersion, SourceFile source, ResourceMetadata metadata, IResourceHelpInfo info, ISourceExtent extent, TSpec spec) : base(kind, apiVersion, source, metadata, info, extent, spec) { - _Annotations = new Dictionary(); + _Annotations = []; Obsolete = ResourceHelper.IsObsolete(metadata); Flags |= ResourceHelper.IsObsolete(metadata) ? ResourceFlags.Obsolete : ResourceFlags.None; } diff --git a/src/PSRule/Definitions/ModuleConfigs/ModuleConfigV1Spec.cs b/src/PSRule/Definitions/ModuleConfigs/ModuleConfigV1Spec.cs index 4103a9a5bd..9d9b05cadd 100644 --- a/src/PSRule/Definitions/ModuleConfigs/ModuleConfigV1Spec.cs +++ b/src/PSRule/Definitions/ModuleConfigs/ModuleConfigV1Spec.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Configuration; diff --git a/src/PSRule/Definitions/Resource.cs b/src/PSRule/Definitions/Resource.cs index 13f06ec343..f7c8f44f7c 100644 --- a/src/PSRule/Definitions/Resource.cs +++ b/src/PSRule/Definitions/Resource.cs @@ -22,12 +22,12 @@ protected internal Resource(ResourceKind kind, string apiVersion, SourceFile sou Kind = kind; ApiVersion = apiVersion; Info = info; - Source = source; + Source = source ?? throw new ArgumentNullException(nameof(source)); Extent = extent; - Spec = spec; - Metadata = metadata; + Spec = spec ?? throw new ArgumentNullException(nameof(spec)); + Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); Name = metadata.Name; - Id = new ResourceId(source.Module, Name, ResourceIdKind.Id); + Id = new ResourceId(Source.Module, Name, ResourceIdKind.Id); } /// diff --git a/src/PSRule/Definitions/ResourceValidator.cs b/src/PSRule/Definitions/ResourceValidator.cs index 4894cd00a1..4080a81e0c 100644 --- a/src/PSRule/Definitions/ResourceValidator.cs +++ b/src/PSRule/Definitions/ResourceValidator.cs @@ -3,8 +3,8 @@ using System.Management.Automation; using System.Text.RegularExpressions; +using PSRule.Pipeline; using PSRule.Resources; -using PSRule.Runtime; namespace PSRule.Definitions; @@ -22,11 +22,11 @@ internal sealed class ResourceValidator : IResourceValidator private static readonly Regex ValidName = new("^[^<>:/\\\\|?*\"'`+@._\\-\x00-\x1F][^<>:/\\\\|?*\"'`+@\x00-\x1F]{1,126}[^<>:/\\\\|?*\"'`+@._\\-\x00-\x1F]$", RegexOptions.Compiled, TimeSpan.FromSeconds(5)); - private readonly RunspaceContext _Context; + private readonly IPipelineWriter _Writer; - public ResourceValidator(RunspaceContext context) + public ResourceValidator(IPipelineWriter writer) { - _Context = context; + _Writer = writer; } internal static bool IsNameValid(string name) @@ -74,7 +74,7 @@ private static string ReportExtent(ISourceExtent extent) private void ReportError(string errorId, string message, params object[] args) { - if (_Context == null) + if (_Writer == null) return; ReportError(new Pipeline.ParseException( @@ -85,10 +85,10 @@ private void ReportError(string errorId, string message, params object[] args) private void ReportError(Pipeline.ParseException exception) { - if (_Context == null) + if (_Writer == null) return; - _Context.WriteError(new ErrorRecord( + _Writer.WriteError(new ErrorRecord( exception: exception, errorId: exception.ErrorId, errorCategory: ErrorCategory.InvalidOperation, diff --git a/src/PSRule/Definitions/Rules/IRuleSpec.cs b/src/PSRule/Definitions/Rules/IRuleSpec.cs index b46f544ade..a7775d8356 100644 --- a/src/PSRule/Definitions/Rules/IRuleSpec.cs +++ b/src/PSRule/Definitions/Rules/IRuleSpec.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Definitions.Expressions; diff --git a/src/PSRule/Definitions/Rules/RuleV1Spec.cs b/src/PSRule/Definitions/Rules/RuleV1Spec.cs index a288fa0981..6e8c949c08 100644 --- a/src/PSRule/Definitions/Rules/RuleV1Spec.cs +++ b/src/PSRule/Definitions/Rules/RuleV1Spec.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Definitions.Expressions; diff --git a/src/PSRule/Definitions/Rules/RuleVisitor.cs b/src/PSRule/Definitions/Rules/RuleVisitor.cs index 2b2e253c05..403e297fa9 100644 --- a/src/PSRule/Definitions/Rules/RuleVisitor.cs +++ b/src/PSRule/Definitions/Rules/RuleVisitor.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Management.Automation; using PSRule.Definitions.Expressions; -using PSRule.Pipeline; using PSRule.Resources; using PSRule.Runtime; diff --git a/src/PSRule/Definitions/Selectors/ISelector.cs b/src/PSRule/Definitions/Selectors/ISelector.cs index e2f362640d..616f173080 100644 --- a/src/PSRule/Definitions/Selectors/ISelector.cs +++ b/src/PSRule/Definitions/Selectors/ISelector.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Definitions.Selectors; diff --git a/src/PSRule/Definitions/Selectors/SelectorV1.cs b/src/PSRule/Definitions/Selectors/SelectorV1.cs index ccbea2f6b5..b2c884a4de 100644 --- a/src/PSRule/Definitions/Selectors/SelectorV1.cs +++ b/src/PSRule/Definitions/Selectors/SelectorV1.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Pipeline; diff --git a/src/PSRule/Definitions/SpecAttribute.cs b/src/PSRule/Definitions/SpecAttribute.cs index dec059e1d6..a815be79be 100644 --- a/src/PSRule/Definitions/SpecAttribute.cs +++ b/src/PSRule/Definitions/SpecAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Definitions; diff --git a/src/PSRule/Definitions/SuppressionGroups/ISuppressionGroupV1Spec.cs b/src/PSRule/Definitions/SuppressionGroups/ISuppressionGroupV1Spec.cs index 1e69df583e..d620ee5bd0 100644 --- a/src/PSRule/Definitions/SuppressionGroups/ISuppressionGroupV1Spec.cs +++ b/src/PSRule/Definitions/SuppressionGroups/ISuppressionGroupV1Spec.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Definitions.Expressions; diff --git a/src/PSRule/Definitions/SuppressionGroups/SuppressionGroupV1Spec.cs b/src/PSRule/Definitions/SuppressionGroups/SuppressionGroupV1Spec.cs index f539806a2d..4e7548d064 100644 --- a/src/PSRule/Definitions/SuppressionGroups/SuppressionGroupV1Spec.cs +++ b/src/PSRule/Definitions/SuppressionGroups/SuppressionGroupV1Spec.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Definitions.Expressions; diff --git a/src/PSRule/Definitions/SuppressionGroups/SuppressionGroupVisitor.cs b/src/PSRule/Definitions/SuppressionGroups/SuppressionGroupVisitor.cs index b294dd8ef0..5c0dbd13f5 100644 --- a/src/PSRule/Definitions/SuppressionGroups/SuppressionGroupVisitor.cs +++ b/src/PSRule/Definitions/SuppressionGroups/SuppressionGroupVisitor.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using PSRule.Definitions.Expressions; -using PSRule.Pipeline; using PSRule.Resources; using PSRule.Runtime; diff --git a/src/PSRule/Help/FormatOptions.cs b/src/PSRule/Help/FormatOptions.cs index 87ed6b6ccf..0abb1f46a5 100644 --- a/src/PSRule/Help/FormatOptions.cs +++ b/src/PSRule/Help/FormatOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Help; diff --git a/src/PSRule/Help/IHelpDocument.cs b/src/PSRule/Help/IHelpDocument.cs index 5d89c01990..58e5180a5c 100644 --- a/src/PSRule/Help/IHelpDocument.cs +++ b/src/PSRule/Help/IHelpDocument.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Definitions; diff --git a/src/PSRule/Help/Link.cs b/src/PSRule/Help/Link.cs index a2a4ccd9d8..eef4067b39 100644 --- a/src/PSRule/Help/Link.cs +++ b/src/PSRule/Help/Link.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Help; diff --git a/src/PSRule/Help/RuleDocument.cs b/src/PSRule/Help/RuleDocument.cs index b5b310a20e..d9a4681a53 100644 --- a/src/PSRule/Help/RuleDocument.cs +++ b/src/PSRule/Help/RuleDocument.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Definitions; diff --git a/src/PSRule/Help/TextBlock.cs b/src/PSRule/Help/TextBlock.cs index adfc56f43a..532f867c78 100644 --- a/src/PSRule/Help/TextBlock.cs +++ b/src/PSRule/Help/TextBlock.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Help; diff --git a/src/PSRule/Host/AssertVariable.cs b/src/PSRule/Host/AssertVariable.cs index 5602b92ac0..70a33c93a9 100644 --- a/src/PSRule/Host/AssertVariable.cs +++ b/src/PSRule/Host/AssertVariable.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/src/PSRule/Host/HostHelper.cs b/src/PSRule/Host/HostHelper.cs index 8fa94597cf..dcbfdb9aef 100644 --- a/src/PSRule/Host/HostHelper.cs +++ b/src/PSRule/Host/HostHelper.cs @@ -31,11 +31,11 @@ internal static class HostHelper { private const string Markdown_Extension = ".md"; - internal static IRuleV1[] GetRule(Source[] source, RunspaceContext context, bool includeDependencies) + internal static IRuleV1[] GetRule(Source[] source, RunspaceContext runspaceContext, bool includeDependencies) { - var rules = ToRuleV1(GetLanguageBlock(context, source), context); - var builder = new DependencyGraphBuilder(context, includeDependencies, includeDisabled: true); - builder.Include(rules, filter: (b) => Match(context, b)); + var rules = ToRuleV1(GetLanguageBlock(runspaceContext, source), runspaceContext); + var builder = new DependencyGraphBuilder(runspaceContext, includeDependencies, includeDisabled: true); + builder.Include(rules, filter: (b) => Match(runspaceContext, b)); return builder.GetItems(); } @@ -54,11 +54,16 @@ internal static DependencyGraph GetRuleBlockGraph(Source[] source, Ru return builder.Build(); } - private static IEnumerable GetYamlJsonLanguageBlocks(Source[] source, RunspaceContext context) + /// + /// Get meta resources which are resource defined in YAML or JSON. + /// + internal static IEnumerable GetMetaResources(Source[] source, IResourceDiscoveryContext context) where T : ILanguageBlock { - var results = new List(); - results.AddRange(GetYamlLanguageBlocks(source, context)); - results.AddRange(GetJsonLanguageBlocks(source, context)); + if (source == null || source.Length == 0) return []; + + var results = new List(); + results.AddRange(GetYamlLanguageBlocks(source, context).OfType()); + results.AddRange(GetJsonLanguageBlocks(source, context).OfType()); return results; } @@ -67,7 +72,7 @@ private static IEnumerable GetYamlJsonLanguageBlocks(Source[] so /// internal static IEnumerable GetBaseline(Source[] source, RunspaceContext context) { - return ToBaselineV1(GetYamlJsonLanguageBlocks(source, context), context); + return ToBaselineV1(GetMetaResources(source, context), context); } /// @@ -75,7 +80,7 @@ internal static IEnumerable GetBaseline(Source[] source, RunspaceConte /// internal static IEnumerable GetModuleConfigForTests(Source[] source, RunspaceContext context) { - return ToModuleConfigV1(GetYamlJsonLanguageBlocks(source, context), context); + return ToModuleConfigV1(GetMetaResources(source, context), context); } /// @@ -83,7 +88,7 @@ internal static IEnumerable GetModuleConfigForTests(Source[] sou /// internal static IEnumerable GetSelectorForTests(Source[] source, RunspaceContext context) { - return ToSelectorV1(GetYamlJsonLanguageBlocks(source, context), context); + return ToSelectorV1(GetMetaResources(source, context), context); } /// @@ -91,16 +96,11 @@ internal static IEnumerable GetSelectorForTests(Source[] source, Run /// internal static IEnumerable GetSuppressionGroupForTests(Source[] source, RunspaceContext context) { - return ToSuppressionGroupV1(GetYamlJsonLanguageBlocks(source, context), context); - } - - internal static IEnumerable ImportResource(Source[] source, RunspaceContext context) - { - return source == null || source.Length == 0 ? Array.Empty() : GetYamlJsonLanguageBlocks(source, context); + return ToSuppressionGroupV1(GetMetaResources(source, context), context); } /// - /// Called from PowerShell to get additional metdata from a language block, such as comment help. + /// Called from PowerShell to get additional metadata from a language block, such as comment help. /// internal static CommentMetadata GetCommentMeta(ISourceFile file, int lineNumber, int offset) { @@ -154,28 +154,31 @@ internal static void UnblockFile(IPipelineWriter writer, string[] publisher, str } } + /// + /// Get all the language elements. + /// private static ILanguageBlock[] GetLanguageBlock(RunspaceContext context, Source[] sources) { var results = new List(); results.AddRange(GetPSLanguageBlocks(context, sources)); - results.AddRange(GetYamlJsonLanguageBlocks(sources, context)); - return results.ToArray(); + results.AddRange(GetMetaResources(sources, context)); + return [.. results]; } /// /// Execute PowerShell script files to get language blocks. /// - private static ILanguageBlock[] GetPSLanguageBlocks(RunspaceContext context, Source[] sources) + private static ILanguageBlock[] GetPSLanguageBlocks(IScriptResourceDiscoveryContext context, Source[] sources) { - if (context.Pipeline.Option.Execution.RestrictScriptSource == Options.RestrictScriptSource.DisablePowerShell) - return Array.Empty(); + if (context.GetExecutionOption().RestrictScriptSource == Options.RestrictScriptSource.DisablePowerShell) + return []; var results = new List(); var ps = context.GetPowerShell(); try { - context.Writer.EnterScope("[Discovery.Rule]"); + context.Writer?.EnterScope("[Discovery.Rule]"); context.PushScope(RunspaceScope.Source); // Process scripts @@ -187,7 +190,7 @@ private static ILanguageBlock[] GetPSLanguageBlocks(RunspaceContext context, Sou continue; ps.Commands.Clear(); - context.VerboseRuleDiscovery(path: file.Path); + context.Writer?.VerboseRuleDiscovery(path: file.Path); context.EnterLanguageScope(file); try { @@ -198,14 +201,14 @@ private static ILanguageBlock[] GetPSLanguageBlocks(RunspaceContext context, Sou if (visitor.Errors != null && visitor.Errors.Count > 0) { foreach (var record in visitor.Errors) - context.WriteError(record); + context.Writer?.WriteError(record); continue; } if (errors != null && errors.Length > 0) { foreach (var error in errors) - context.WriteError(error); + context.Writer?.WriteError(error); continue; } @@ -233,21 +236,21 @@ private static ILanguageBlock[] GetPSLanguageBlocks(RunspaceContext context, Sou } finally { - context.Writer.ExitScope(); + context.Writer?.ExitScope(); context.PopScope(RunspaceScope.Source); ps.Runspace = null; ps.Dispose(); } - return results.ToArray(); + return [.. results]; } /// /// Get language blocks from YAML source files. /// - private static ILanguageBlock[] GetYamlLanguageBlocks(Source[] sources, RunspaceContext context) + private static ILanguageBlock[] GetYamlLanguageBlocks(Source[] sources, IResourceDiscoveryContext context) { var result = new Collection(); - var visitor = new ResourceValidator(context); + var visitor = new ResourceValidator(context.Writer); var d = new DeserializerBuilder() .IgnoreUnmatchedProperties() .WithNamingConvention(CamelCaseNamingConvention.Instance) @@ -275,7 +278,7 @@ private static ILanguageBlock[] GetYamlLanguageBlocks(Source[] sources, Runspace if (file.Type != SourceType.Yaml) continue; - context.VerboseRuleDiscovery(path: file.Path); + context.Writer?.VerboseRuleDiscovery(path: file.Path); context.EnterLanguageScope(file); try { @@ -304,16 +307,16 @@ private static ILanguageBlock[] GetYamlLanguageBlocks(Source[] sources, Runspace context.Writer?.ExitScope(); context.PopScope(RunspaceScope.Resource); } - return result.Count == 0 ? Array.Empty() : result.ToArray(); + return result.Count == 0 ? [] : [.. result]; } /// /// Get language blocks from JSON source files. /// - private static ILanguageBlock[] GetJsonLanguageBlocks(Source[] sources, RunspaceContext context) + private static ILanguageBlock[] GetJsonLanguageBlocks(Source[] sources, IResourceDiscoveryContext context) { var result = new Collection(); - var visitor = new ResourceValidator(context); + var visitor = new ResourceValidator(context.Writer); var deserializer = new JsonSerializer { DateTimeZoneHandling = DateTimeZoneHandling.Utc @@ -335,7 +338,7 @@ private static ILanguageBlock[] GetJsonLanguageBlocks(Source[] sources, Runspace if (file.Type != SourceType.Json) continue; - context.VerboseRuleDiscovery(file.Path); + context.Writer?.VerboseRuleDiscovery(file.Path); context.EnterLanguageScope(file); try { @@ -369,7 +372,7 @@ private static ILanguageBlock[] GetJsonLanguageBlocks(Source[] sources, Runspace context.Writer?.ExitScope(); context.PopScope(RunspaceScope.Resource); } - return result.Count == 0 ? Array.Empty() : result.ToArray(); + return result.Count == 0 ? [] : [.. result]; } public static void InvokeRuleBlock(RunspaceContext context, RuleBlock ruleBlock, RuleRecord ruleRecord) @@ -421,9 +424,9 @@ public static void InvokeRuleBlock(RunspaceContext context, RuleBlock ruleBlock, } /// - /// Convert matching langauge blocks to rules. + /// Convert matching language blocks to rules. /// - private static DependencyTargetCollection ToRuleV1(ILanguageBlock[] blocks, RunspaceContext context) + private static DependencyTargetCollection ToRuleV1(ILanguageBlock[] blocks, RunspaceContext runspaceContext) { // Index rules by RuleId var results = new DependencyTargetCollection(); @@ -437,12 +440,12 @@ private static DependencyTargetCollection ToRuleV1(ILanguageBlock[] blo { if (knownRuleIds.ContainsIds(block.Id, block.Ref, block.Alias, out var duplicateId)) { - context.DuplicateResourceId(block.Id, duplicateId.Value); + runspaceContext.DuplicateResourceId(block.Id, duplicateId.Value); continue; } if (knownRuleNames.ContainsNames(block.Id, block.Ref, block.Alias, out var duplicateName)) - context.WarnDuplicateRuleName(duplicateName); + runspaceContext.WarnDuplicateRuleName(duplicateName); results.TryAdd(new Rule { @@ -466,17 +469,17 @@ private static DependencyTargetCollection ToRuleV1(ILanguageBlock[] blo { if (knownRuleIds.ContainsIds(block.Id, block.Ref, block.Alias, out var duplicateId)) { - context.DuplicateResourceId(block.Id, duplicateId.Value); + runspaceContext.DuplicateResourceId(block.Id, duplicateId.Value); continue; } if (knownRuleNames.ContainsNames(block.Id, block.Ref, block.Alias, out var duplicateName)) - context.WarnDuplicateRuleName(duplicateName); + runspaceContext.WarnDuplicateRuleName(duplicateName); - context.EnterLanguageScope(block.Source); + runspaceContext.EnterLanguageScope(block.Source); try { - var info = GetRuleHelpInfo(context, block); + var info = GetRuleHelpInfo(runspaceContext, block); results.TryAdd(new Rule { Id = block.Id, @@ -496,7 +499,7 @@ private static DependencyTargetCollection ToRuleV1(ILanguageBlock[] blo } finally { - context.ExitLanguageScope(block.Source); + runspaceContext.ExitLanguageScope(block.Source); } } return results; @@ -627,7 +630,7 @@ private static RuleHelpInfo[] ToRuleHelp(IEnumerable blocks, Run private static Baseline[] ToBaselineV1(IEnumerable blocks, RunspaceContext context) { if (blocks == null) - return Array.Empty(); + return []; // Index baselines by BaselineId var results = new Dictionary(StringComparer.OrdinalIgnoreCase); diff --git a/src/PSRule/Host/PSRuleVariable.cs b/src/PSRule/Host/PSRuleVariable.cs index 5c40fc5379..2976a2807c 100644 --- a/src/PSRule/Host/PSRuleVariable.cs +++ b/src/PSRule/Host/PSRuleVariable.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/src/PSRule/Host/RuleVariable.cs b/src/PSRule/Host/RuleVariable.cs index e0ae39a27c..7f6c103b61 100644 --- a/src/PSRule/Host/RuleVariable.cs +++ b/src/PSRule/Host/RuleVariable.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/src/PSRule/PSRule.csproj b/src/PSRule/PSRule.csproj index f6768e9014..33a8d07f17 100644 --- a/src/PSRule/PSRule.csproj +++ b/src/PSRule/PSRule.csproj @@ -19,10 +19,10 @@ - - + + - + @@ -38,8 +38,8 @@ - - + + diff --git a/src/PSRule/PSRule.psm1 b/src/PSRule/PSRule.psm1 index 7d2494f9a9..efc8092666 100644 --- a/src/PSRule/PSRule.psm1 +++ b/src/PSRule/PSRule.psm1 @@ -1116,12 +1116,6 @@ function New-PSRuleOption { [Parameter(Mandatory = $False)] [PSRule.Configuration.SuppressionOption]$SuppressTargetName, - [Parameter(Mandatory = $False)] - [PSRule.Configuration.BindTargetName[]]$BindTargetName, - - [Parameter(Mandatory = $False)] - [PSRule.Configuration.BindTargetName[]]$BindTargetType, - # Options # Sets the Baseline.Group option @@ -1315,7 +1309,7 @@ function New-PSRuleOption { # Sets the Output.Outcome option [Parameter(Mandatory = $False)] - [ValidateSet('None', 'Fail', 'Pass', 'Error', 'Processed', 'All')] + [ValidateSet('None', 'Fail', 'Pass', 'Error', 'Problem', 'Processed', 'All')] [Alias('Outcome')] [PSRule.Rules.RuleOutcome]$OutputOutcome = 'Processed', @@ -1371,12 +1365,6 @@ function New-PSRuleOption { if ($optionParams.ContainsKey('SuppressTargetName')) { $optionParams.Remove('SuppressTargetName'); } - if ($optionParams.ContainsKey('BindTargetName')) { - $optionParams.Remove('BindTargetName'); - } - if ($optionParams.ContainsKey('BindTargetType')) { - $optionParams.Remove('BindTargetType'); - } if ($PSBoundParameters.ContainsKey('Option')) { $Option = [PSRule.Configuration.PSRuleOption]::FromFileOrEmpty($Option, $Path); } @@ -1400,14 +1388,6 @@ function New-PSRuleOption { if ($PSBoundParameters.ContainsKey('SuppressTargetName')) { $Option.Suppression = $SuppressTargetName; } - if ($PSBoundParameters.ContainsKey('BindTargetName')) { - Write-Verbose -Message 'Set BindTargetName pipeline hook'; - $Option.Pipeline.BindTargetName.AddRange($BindTargetName); - } - if ($PSBoundParameters.ContainsKey('BindTargetType')) { - Write-Verbose -Message 'Set BindTargetType pipeline hook'; - $Option.Pipeline.BindTargetType.AddRange($BindTargetType); - } # Options $Option | SetOptions @optionParams -Verbose:$VerbosePreference; @@ -1632,7 +1612,7 @@ function Set-PSRuleOption { # Sets the Output.Outcome option [Parameter(Mandatory = $False)] - [ValidateSet('None', 'Fail', 'Pass', 'Error', 'Processed', 'All')] + [ValidateSet('None', 'Fail', 'Pass', 'Error', 'Problem', 'Processed', 'All')] [Alias('Outcome')] [PSRule.Rules.RuleOutcome]$OutputOutcome = 'Processed', @@ -2396,7 +2376,7 @@ function SetOptions { # Sets the Output.Outcome option [Parameter(Mandatory = $False)] - [ValidateSet('None', 'Fail', 'Pass', 'Error', 'Processed', 'All')] + [ValidateSet('None', 'Fail', 'Pass', 'Error', 'Problem', 'Processed', 'All')] [Alias('Outcome')] [PSRule.Rules.RuleOutcome]$OutputOutcome = 'Processed', diff --git a/src/PSRule/Pipeline/AssertPipelineBuilder.cs b/src/PSRule/Pipeline/AssertPipelineBuilder.cs index de0a7999a4..4bfe3eb804 100644 --- a/src/PSRule/Pipeline/AssertPipelineBuilder.cs +++ b/src/PSRule/Pipeline/AssertPipelineBuilder.cs @@ -201,10 +201,7 @@ public sealed override IPipeline Build(IPipelineWriter writer = null) return !RequireModules() || !RequireSources() ? null : (IPipeline)new InvokeRulePipeline( - context: PrepareContext( - bindTargetName: BindTargetNameHook, - bindTargetType: BindTargetTypeHook, - bindField: BindFieldHook), + context: PrepareContext(PipelineHookActions.Default), source: Source, writer: HandleJobSummary(writer ?? PrepareWriter()), outcome: RuleOutcome.Processed); diff --git a/src/PSRule/Pipeline/Dependencies/LockEntry.cs b/src/PSRule/Pipeline/Dependencies/LockEntry.cs index b7952671f6..e0afdf7d42 100644 --- a/src/PSRule/Pipeline/Dependencies/LockEntry.cs +++ b/src/PSRule/Pipeline/Dependencies/LockEntry.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.ComponentModel; using Newtonsoft.Json; using PSRule.Data; @@ -16,4 +17,10 @@ public sealed class LockEntry /// [JsonProperty("version", NullValueHandling = NullValueHandling.Include)] public SemanticVersion.Version Version { get; set; } + + /// + /// Accept pre-release versions in addition to stable module versions. + /// + [DefaultValue(null), JsonProperty("includePrerelease", NullValueHandling = NullValueHandling.Ignore)] + public bool? IncludePrerelease { get; set; } } diff --git a/src/PSRule/Pipeline/ExecutionScope.cs b/src/PSRule/Pipeline/ExecutionScope.cs index 6e51f6afb3..67ceeaa872 100644 --- a/src/PSRule/Pipeline/ExecutionScope.cs +++ b/src/PSRule/Pipeline/ExecutionScope.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Pipeline; diff --git a/src/PSRule/Pipeline/ExportBaselinePipelineBuilder.cs b/src/PSRule/Pipeline/ExportBaselinePipelineBuilder.cs index de5a805b81..65ed92f87b 100644 --- a/src/PSRule/Pipeline/ExportBaselinePipelineBuilder.cs +++ b/src/PSRule/Pipeline/ExportBaselinePipelineBuilder.cs @@ -43,11 +43,7 @@ public override IPipeline Build(IPipelineWriter writer = null) { var filter = new BaselineFilter(_Name); return new GetBaselinePipeline( - pipeline: PrepareContext( - bindTargetName: null, - bindTargetType: null, - bindField: null - ), + pipeline: PrepareContext(PipelineHookActions.Empty), source: Source, reader: PrepareReader(), writer: writer ?? PrepareWriter(), diff --git a/src/PSRule/Pipeline/GetBaselinePipelineBuilder.cs b/src/PSRule/Pipeline/GetBaselinePipelineBuilder.cs index a8636ced86..191e2cf3d5 100644 --- a/src/PSRule/Pipeline/GetBaselinePipelineBuilder.cs +++ b/src/PSRule/Pipeline/GetBaselinePipelineBuilder.cs @@ -41,11 +41,7 @@ public override IPipeline Build(IPipelineWriter writer = null) { var filter = new BaselineFilter(ResolveBaselineGroup(_Name)); return new GetBaselinePipeline( - pipeline: PrepareContext( - bindTargetName: null, - bindTargetType: null, - bindField: null - ), + pipeline: PrepareContext(PipelineHookActions.Empty), source: Source, reader: PrepareReader(), writer: writer ?? PrepareWriter(), diff --git a/src/PSRule/Pipeline/GetRuleHelpPipeline.cs b/src/PSRule/Pipeline/GetRuleHelpPipeline.cs index b0dbdef933..b89a30ab9e 100644 --- a/src/PSRule/Pipeline/GetRuleHelpPipeline.cs +++ b/src/PSRule/Pipeline/GetRuleHelpPipeline.cs @@ -50,10 +50,7 @@ public void Online() public override IPipeline Build(IPipelineWriter writer = null) { return new GetRuleHelpPipeline( - pipeline: PrepareContext( - bindTargetName: null, - bindTargetType: null, - bindField: null), + pipeline: PrepareContext(PipelineHookActions.Empty), source: Source, reader: PrepareReader(), writer: writer ?? PrepareWriter()); diff --git a/src/PSRule/Pipeline/GetRulePipelineBuilder.cs b/src/PSRule/Pipeline/GetRulePipelineBuilder.cs index 3e8f6c8079..cc7b423d59 100644 --- a/src/PSRule/Pipeline/GetRulePipelineBuilder.cs +++ b/src/PSRule/Pipeline/GetRulePipelineBuilder.cs @@ -45,11 +45,7 @@ public override IPipeline Build(IPipelineWriter writer = null) return !RequireModules() || !RequireSources() ? null : (IPipeline)new GetRulePipeline( - pipeline: PrepareContext( - bindTargetName: null, - bindTargetType: null, - bindField: null - ), + pipeline: PrepareContext(PipelineHookActions.Empty), source: Source, reader: PrepareReader(), writer: writer ?? PrepareWriter(), diff --git a/src/PSRule/Pipeline/GetTargetPipelineBuilder.cs b/src/PSRule/Pipeline/GetTargetPipelineBuilder.cs index 9b2d93bbd6..5d5c92f483 100644 --- a/src/PSRule/Pipeline/GetTargetPipelineBuilder.cs +++ b/src/PSRule/Pipeline/GetTargetPipelineBuilder.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using PSRule.Configuration; -using PSRule.Options; namespace PSRule.Pipeline; @@ -57,48 +56,12 @@ public void InputPath(string[] path) /// public override IPipeline Build(IPipelineWriter writer = null) { - return new GetTargetPipeline(PrepareContext(null, null, null), PrepareReader(), writer ?? PrepareWriter()); + return new GetTargetPipeline(PrepareContext(PipelineHookActions.Empty), PrepareReader(), writer ?? PrepareWriter()); } /// protected override PipelineInputStream PrepareReader() { - if (!string.IsNullOrEmpty(Option.Input.ObjectPath)) - { - AddVisitTargetObjectAction((sourceObject, next) => - { - return PipelineReceiverActions.ReadObjectPath(sourceObject, next, Option.Input.ObjectPath, true); - }); - } - - if (Option.Input.Format == InputFormat.Yaml) - { - AddVisitTargetObjectAction((sourceObject, next) => - { - return PipelineReceiverActions.ConvertFromYaml(sourceObject, next); - }); - } - else if (Option.Input.Format == InputFormat.Json) - { - AddVisitTargetObjectAction((sourceObject, next) => - { - return PipelineReceiverActions.ConvertFromJson(sourceObject, next); - }); - } - else if (Option.Input.Format == InputFormat.Markdown) - { - AddVisitTargetObjectAction((sourceObject, next) => - { - return PipelineReceiverActions.ConvertFromMarkdown(sourceObject, next); - }); - } - else if (Option.Input.Format == InputFormat.PowerShellData) - { - AddVisitTargetObjectAction((sourceObject, next) => - { - return PipelineReceiverActions.ConvertFromPowerShellData(sourceObject, next); - }); - } - return new PipelineInputStream(VisitTargetObject, _InputPath, GetInputObjectSourceFilter(), Option); + return new PipelineInputStream(_InputPath, GetInputObjectSourceFilter(), Option); } } diff --git a/src/PSRule/Pipeline/HostContextExtensions.cs b/src/PSRule/Pipeline/HostContextExtensions.cs index fe1b27344f..0e695305e8 100644 --- a/src/PSRule/Pipeline/HostContextExtensions.cs +++ b/src/PSRule/Pipeline/HostContextExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/src/PSRule/Pipeline/IGetPipelineBuilder.cs b/src/PSRule/Pipeline/IGetPipelineBuilder.cs index 832ba886ce..70d6202b3e 100644 --- a/src/PSRule/Pipeline/IGetPipelineBuilder.cs +++ b/src/PSRule/Pipeline/IGetPipelineBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Pipeline; diff --git a/src/PSRule/Pipeline/IGetTargetPipelineBuilder.cs b/src/PSRule/Pipeline/IGetTargetPipelineBuilder.cs index d895baa4af..aa1e6aa45e 100644 --- a/src/PSRule/Pipeline/IGetTargetPipelineBuilder.cs +++ b/src/PSRule/Pipeline/IGetTargetPipelineBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Pipeline; diff --git a/src/PSRule/Pipeline/IHelpPipelineBuilder.cs b/src/PSRule/Pipeline/IHelpPipelineBuilder.cs index f9904516a8..5584f1a517 100644 --- a/src/PSRule/Pipeline/IHelpPipelineBuilder.cs +++ b/src/PSRule/Pipeline/IHelpPipelineBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Pipeline; diff --git a/src/PSRule/Pipeline/IHostContext.cs b/src/PSRule/Pipeline/IHostContext.cs index 51619e7242..44997fc0e5 100644 --- a/src/PSRule/Pipeline/IHostContext.cs +++ b/src/PSRule/Pipeline/IHostContext.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/src/PSRule/Pipeline/IInvokePipelineBuilder.cs b/src/PSRule/Pipeline/IInvokePipelineBuilder.cs index d5bf4a9b10..1b6553db2f 100644 --- a/src/PSRule/Pipeline/IInvokePipelineBuilder.cs +++ b/src/PSRule/Pipeline/IInvokePipelineBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Pipeline; diff --git a/src/PSRule/Pipeline/IPipelineBuilder.cs b/src/PSRule/Pipeline/IPipelineBuilder.cs new file mode 100644 index 0000000000..4c9612bd8b --- /dev/null +++ b/src/PSRule/Pipeline/IPipelineBuilder.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Configuration; + +namespace PSRule.Pipeline; + +/// +/// A helper to build a PSRule pipeline. +/// +public interface IPipelineBuilder +{ + /// + /// Configure the pipeline with options. + /// + IPipelineBuilder Configure(PSRuleOption option); + + /// + /// Configure the pipeline to use a specific baseline. + /// + /// A baseline option or the name of a baseline. + void Baseline(BaselineOption baseline); + + /// + /// Build the pipeline. + /// + /// Optionally specify a custom writer which will handle output processing. + IPipeline Build(IPipelineWriter writer = null); +} diff --git a/src/PSRule/Pipeline/IPipelineReader.cs b/src/PSRule/Pipeline/IPipelineReader.cs new file mode 100644 index 0000000000..3d6cb63fd1 --- /dev/null +++ b/src/PSRule/Pipeline/IPipelineReader.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Data; + +namespace PSRule.Pipeline; + +#nullable enable + +internal interface IPipelineReader +{ + int Count { get; } + + bool IsEmpty { get; } + + /// + /// Add a new object into the stream. + /// + /// An object to process. + /// A pre-bound type. + /// Determines if expansion is skipped. + void Enqueue(object sourceObject, string? targetType = null, bool skipExpansion = false); + + bool TryDequeue(out ITargetObject sourceObject); + + void Open(); + + /// + /// Add a path to the list of inputs. + /// + /// The path of files to add. + void Add(string path); +} + +#nullable restore diff --git a/src/PSRule/Pipeline/IPipelineResult.cs b/src/PSRule/Pipeline/IPipelineResult.cs index fe0ff10d9c..76b1a85487 100644 --- a/src/PSRule/Pipeline/IPipelineResult.cs +++ b/src/PSRule/Pipeline/IPipelineResult.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Pipeline; diff --git a/src/PSRule/Pipeline/IResourceCache.cs b/src/PSRule/Pipeline/IResourceCache.cs new file mode 100644 index 0000000000..a4ca666e3b --- /dev/null +++ b/src/PSRule/Pipeline/IResourceCache.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Definitions; + +namespace PSRule.Pipeline; + +/// +/// A cache that stores resources. +/// +internal interface IResourceCache +{ + bool Import(IResource resource); +} diff --git a/src/PSRule/Pipeline/ISourceCommandLineBuilder.cs b/src/PSRule/Pipeline/ISourceCommandLineBuilder.cs index a0d9c90fa3..13a26f4bc6 100644 --- a/src/PSRule/Pipeline/ISourceCommandLineBuilder.cs +++ b/src/PSRule/Pipeline/ISourceCommandLineBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Pipeline; diff --git a/src/PSRule/Pipeline/ISourcePipelineBuilder.cs b/src/PSRule/Pipeline/ISourcePipelineBuilder.cs index 065a7ad634..ebdee01c46 100644 --- a/src/PSRule/Pipeline/ISourcePipelineBuilder.cs +++ b/src/PSRule/Pipeline/ISourcePipelineBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/src/PSRule/Pipeline/InvokePipelineBuilderBase.cs b/src/PSRule/Pipeline/InvokePipelineBuilderBase.cs index fc35064dcb..cd7d3a2bcf 100644 --- a/src/PSRule/Pipeline/InvokePipelineBuilderBase.cs +++ b/src/PSRule/Pipeline/InvokePipelineBuilderBase.cs @@ -4,10 +4,12 @@ using PSRule.Configuration; using PSRule.Definitions; using PSRule.Host; -using PSRule.Options; namespace PSRule.Pipeline; +/// +/// A pipeline builder for any pipelines that test objects against rules. +/// internal abstract class InvokePipelineBuilderBase : PipelineBuilderBase, IInvokePipelineBuilder { protected InputPathBuilder _InputPath; @@ -91,7 +93,7 @@ public override IPipeline Build(IPipelineWriter writer = null) Unblock(writer); return !RequireModules() || !RequireSources() ? null - : (IPipeline)new InvokeRulePipeline(PrepareContext(BindTargetNameHook, BindTargetTypeHook, BindFieldHook), Source, writer, Option.Output.Outcome.Value); + : (IPipeline)new InvokeRulePipeline(PrepareContext(PipelineHookActions.Default), Source, writer, Option.Output.Outcome.Value); } protected void Unblock(IPipelineWriter writer) @@ -131,42 +133,6 @@ private static bool IsBlocked(string path) protected override PipelineInputStream PrepareReader() { - if (!string.IsNullOrEmpty(Option.Input.ObjectPath)) - { - AddVisitTargetObjectAction((sourceObject, next) => - { - return PipelineReceiverActions.ReadObjectPath(sourceObject, next, Option.Input.ObjectPath, true); - }); - } - - if (Option.Input.Format == InputFormat.Yaml) - { - AddVisitTargetObjectAction((sourceObject, next) => - { - return PipelineReceiverActions.ConvertFromYaml(sourceObject, next); - }); - } - else if (Option.Input.Format == InputFormat.Json) - { - AddVisitTargetObjectAction((sourceObject, next) => - { - return PipelineReceiverActions.ConvertFromJson(sourceObject, next); - }); - } - else if (Option.Input.Format == InputFormat.Markdown) - { - AddVisitTargetObjectAction((sourceObject, next) => - { - return PipelineReceiverActions.ConvertFromMarkdown(sourceObject, next); - }); - } - else if (Option.Input.Format == InputFormat.PowerShellData) - { - AddVisitTargetObjectAction((sourceObject, next) => - { - return PipelineReceiverActions.ConvertFromPowerShellData(sourceObject, next); - }); - } - return new PipelineInputStream(VisitTargetObject, _InputPath, GetInputObjectSourceFilter(), Option); + return new PipelineInputStream(_InputPath, GetInputObjectSourceFilter(), Option); } } diff --git a/src/PSRule/Pipeline/InvokeRulePipeline.cs b/src/PSRule/Pipeline/InvokeRulePipeline.cs index 85f7252f93..f300f947f2 100644 --- a/src/PSRule/Pipeline/InvokeRulePipeline.cs +++ b/src/PSRule/Pipeline/InvokeRulePipeline.cs @@ -101,13 +101,12 @@ private InvokeResult ProcessTargetObject(TargetObject targetObject) foreach (var ruleBlockTarget in _RuleGraph.GetSingleTarget()) { // Enter rule block scope - Context.EnterLanguageScope(ruleBlockTarget.Value.Source); var ruleRecord = Context.EnterRuleBlock(ruleBlock: ruleBlockTarget.Value); ruleCounter++; try { - if (Context.Binding.ShouldFilter) + if (Context.Binding != null && Context.Binding.ShouldFilter) continue; // Check if dependency failed @@ -163,11 +162,9 @@ private InvokeResult ProcessTargetObject(TargetObject targetObject) finally { // Exit rule block scope - Context.ExitRuleBlock(); + Context.ExitRuleBlock(ruleBlock: ruleBlockTarget.Value); if (ShouldOutput(ruleRecord.Outcome)) result.Add(ruleRecord); - - Context.ExitLanguageScope(ruleBlockTarget.Value.Source); } } diff --git a/src/PSRule/Pipeline/OptionContext.cs b/src/PSRule/Pipeline/OptionContext.cs index f4ade6d621..91a9a6466d 100644 --- a/src/PSRule/Pipeline/OptionContext.cs +++ b/src/PSRule/Pipeline/OptionContext.cs @@ -7,14 +7,19 @@ namespace PSRule.Pipeline; +#nullable enable + internal sealed class OptionContext { private ConventionOption _Convention; private List _ConventionOrder; - public OptionContext() + public OptionContext(BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField, string[] inputTargetType) { - + BindTargetName = bindTargetName; + BindTargetType = bindTargetType; + BindField = bindField; + InputTargetType = inputTargetType; } public Options.BaselineOption Baseline { get; set; } @@ -58,6 +63,12 @@ public ConventionOption Convention public IResourceFilter RuleFilter { get; set; } + public BindTargetMethod BindTargetName { get; } + public BindTargetMethod BindTargetType { get; } + public BindTargetMethod BindField { get; } + + public string[] InputTargetType { get; } + internal int GetConventionOrder(IConvention convention) { if (Convention?.Include == null || Convention.Include.Length == 0) @@ -71,3 +82,5 @@ internal int GetConventionOrder(IConvention convention) return index > -1 ? index : int.MaxValue; } } + +#nullable restore diff --git a/src/PSRule/Pipeline/OptionContextBuilder.cs b/src/PSRule/Pipeline/OptionContextBuilder.cs index cb086c239c..519b70feec 100644 --- a/src/PSRule/Pipeline/OptionContextBuilder.cs +++ b/src/PSRule/Pipeline/OptionContextBuilder.cs @@ -13,6 +13,8 @@ namespace PSRule.Pipeline; +#nullable enable + /// /// A helper to create an . /// @@ -23,14 +25,18 @@ internal sealed class OptionContextBuilder private readonly OptionScopeComparer _Comparer; private readonly string[] _DefaultCulture; private readonly List _ConventionOrder; + private readonly BindTargetMethod _BindTargetName; + private readonly BindTargetMethod _BindTargetType; + private readonly BindTargetMethod _BindField; + private readonly string[] _InputTargetType; internal OptionContextBuilder(string[] include = null, Hashtable tag = null, string[] convention = null) { - _ModuleBaselineScope = new Dictionary(); - _Scopes = new List(); + _ModuleBaselineScope = []; + _Scopes = []; _Comparer = new OptionScopeComparer(); _DefaultCulture = GetDefaultCulture(); - _ConventionOrder = new List(); + _ConventionOrder = []; Parameter(include, tag, convention); } @@ -41,10 +47,17 @@ internal OptionContextBuilder(string[] include = null, Hashtable tag = null, str /// A list of rule identifiers to include set by parameters. If not set all rules that meet filters are included. /// A tag filter to determine which rules are included by parameters. /// A list of conventions to include by parameters. - internal OptionContextBuilder(PSRuleOption option, string[] include = null, Hashtable tag = null, string[] convention = null) + /// + /// + /// + internal OptionContextBuilder(PSRuleOption option, string[] include = null, Hashtable tag = null, string[] convention = null, BindTargetMethod bindTargetName = null, BindTargetMethod bindTargetType = null, BindTargetMethod bindField = null) : this(include, tag, convention) { Workspace(option); + _BindTargetName = bindTargetName; + _BindTargetType = bindTargetType; + _BindField = bindField; + _InputTargetType = option.Input.TargetType; } /// @@ -53,7 +66,7 @@ internal OptionContextBuilder(PSRuleOption option, string[] include = null, Hash internal OptionContext Build(string languageScope) { languageScope = ResourceHelper.NormalizeScope(languageScope); - var context = new OptionContext(); + var context = new OptionContext(_BindTargetName, _BindTargetType, _BindField, _InputTargetType); _Scopes.Sort(_Comparer); @@ -72,6 +85,7 @@ internal OptionContext Build(string languageScope) { Include = GetConventions(_Scopes) }; + return context; } @@ -119,7 +133,7 @@ internal void ModuleConfig(string module, ModuleConfigV1Spec spec) private static bool ShouldCombine(string languageScope, OptionScope optionScope) { - return optionScope.LanguageScope == ResourceHelper.STANDALONE_SCOPENAME || optionScope.LanguageScope == languageScope || optionScope.Type == ScopeType.Explicit; + return optionScope.LanguageScope == ResourceHelper.STANDALONE_SCOPE_NAME || optionScope.LanguageScope == languageScope || optionScope.Type == ScopeType.Explicit; } /// @@ -220,3 +234,5 @@ private static string[] GetDefaultCulture() return result.ToArray(); } } + +#nullable restore diff --git a/src/PSRule/Pipeline/OptionScope.cs b/src/PSRule/Pipeline/OptionScope.cs index 8140081227..2494db4f07 100644 --- a/src/PSRule/Pipeline/OptionScope.cs +++ b/src/PSRule/Pipeline/OptionScope.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Collections; +using System.Diagnostics; using PSRule.Configuration; using PSRule.Definitions; using PSRule.Definitions.Baselines; @@ -38,6 +39,7 @@ internal enum ScopeType Module = 4 } +[DebuggerDisplay("{Type} - {LanguageScope}")] internal class OptionScope { public readonly ScopeType Type; @@ -136,7 +138,6 @@ internal static OptionScope FromBaseline(ScopeType type, string baselineId, stri { return new OptionScope(type, module) { - Binding = spec.Binding, Configuration = spec.Configuration, Rule = new RuleOption { diff --git a/src/PSRule/Pipeline/PSHostContext.cs b/src/PSRule/Pipeline/PSHostContext.cs index dbe931f8e9..d38859c328 100644 --- a/src/PSRule/Pipeline/PSHostContext.cs +++ b/src/PSRule/Pipeline/PSHostContext.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/src/PSRule/Pipeline/PipelineBuilder.cs b/src/PSRule/Pipeline/PipelineBuilder.cs index 73d31c5aa9..9de9fae046 100644 --- a/src/PSRule/Pipeline/PipelineBuilder.cs +++ b/src/PSRule/Pipeline/PipelineBuilder.cs @@ -1,15 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Collections; -using System.Globalization; using PSRule.Configuration; -using PSRule.Data; -using PSRule.Definitions; -using PSRule.Definitions.Baselines; -using PSRule.Options; -using PSRule.Pipeline.Output; -using PSRule.Resources; namespace PSRule.Pipeline; @@ -164,453 +156,3 @@ public static IGetTargetPipelineBuilder GetTarget(PSRuleOption option, IHostCont return pipeline; } } - -/// -/// A helper to build a PSRule pipeline. -/// -public interface IPipelineBuilder -{ - /// - /// Configure the pipeline with options. - /// - IPipelineBuilder Configure(PSRuleOption option); - - /// - /// Configure the pipeline to use a specific baseline. - /// - /// A baseline option or the name of a baseline. - void Baseline(Configuration.BaselineOption baseline); - - /// - /// Build the pipeline. - /// - /// Optionally specify a custom writer which will handle output processing. - IPipeline Build(IPipelineWriter writer = null); -} - -internal abstract class PipelineBuilderBase : IPipelineBuilder -{ - private const string ENGINE_MODULE_NAME = "PSRule"; - - protected readonly PSRuleOption Option; - protected readonly Source[] Source; - protected readonly IHostContext HostContext; - protected BindTargetMethod BindTargetNameHook; - protected BindTargetMethod BindTargetTypeHook; - protected BindTargetMethod BindFieldHook; - protected VisitTargetObject VisitTargetObject; - - private string[] _Include; - private Hashtable _Tag; - private Configuration.BaselineOption _Baseline; - private string[] _Convention; - private PathFilter _InputFilter; - private PipelineWriter _Writer; - - private readonly HostPipelineWriter _Output; - - private const int MIN_JSON_INDENT = 0; - private const int MAX_JSON_INDENT = 4; - - protected PipelineBuilderBase(Source[] source, IHostContext hostContext) - { - Option = new PSRuleOption(); - Source = source; - _Output = new HostPipelineWriter(hostContext, Option, ShouldProcess); - HostContext = hostContext; - BindTargetNameHook = PipelineHookActions.BindTargetName; - BindTargetTypeHook = PipelineHookActions.BindTargetType; - BindFieldHook = PipelineHookActions.BindField; - VisitTargetObject = PipelineReceiverActions.PassThru; - } - - /// - /// Determines if the pipeline is executing in a remote PowerShell session. - /// - public bool InSession => HostContext != null && HostContext.InSession; - - /// - public void Name(string[] name) - { - if (name == null || name.Length == 0) - return; - - _Include = name; - } - - /// - public void Tag(Hashtable tag) - { - if (tag == null || tag.Count == 0) - return; - - _Tag = tag; - } - - /// - public void Convention(string[] convention) - { - if (convention == null || convention.Length == 0) - return; - - _Convention = convention; - } - - /// - public virtual IPipelineBuilder Configure(PSRuleOption option) - { - if (option == null) - return this; - - Option.Baseline = new Options.BaselineOption(option.Baseline); - Option.Binding = new BindingOption(option.Binding); - Option.Convention = new ConventionOption(option.Convention); - Option.Execution = GetExecutionOption(option.Execution); - Option.Input = new InputOption(option.Input); - Option.Input.Format ??= InputOption.Default.Format; - Option.Output = new OutputOption(option.Output); - Option.Output.Outcome ??= OutputOption.Default.Outcome; - Option.Output.Banner ??= OutputOption.Default.Banner; - Option.Output.Style = GetStyle(option.Output.Style ?? OutputOption.Default.Style.Value); - Option.Repository = GetRepository(option.Repository); - return this; - } - - /// - public abstract IPipeline Build(IPipelineWriter writer = null); - - /// - public void Baseline(Configuration.BaselineOption baseline) - { - if (baseline == null) - return; - - _Baseline = baseline; - } - - /// - /// Require correct module versions for pipeline execution. - /// - protected bool RequireModules() - { - var result = true; - if (Option.Requires.TryGetValue(ENGINE_MODULE_NAME, out var requiredVersion)) - { - var engineVersion = Engine.GetVersion(); - if (GuardModuleVersion(ENGINE_MODULE_NAME, engineVersion, requiredVersion)) - result = false; - } - for (var i = 0; Source != null && i < Source.Length; i++) - { - if (Source[i].Module != null && Option.Requires.TryGetValue(Source[i].Module.Name, out requiredVersion)) - { - if (GuardModuleVersion(Source[i].Module.Name, Source[i].Module.Version, requiredVersion)) - result = false; - } - } - return result; - } - - /// - /// Require sources for pipeline execution. - /// - protected bool RequireSources() - { - if (Source == null || Source.Length == 0) - { - PrepareWriter().WarnRulePathNotFound(); - return false; - } - return true; - } - - private bool GuardModuleVersion(string moduleName, string moduleVersion, string requiredVersion) - { - if (!TryModuleVersion(moduleVersion, requiredVersion)) - { - var writer = PrepareWriter(); - writer.ErrorRequiredVersionMismatch(moduleName, moduleVersion, requiredVersion); - writer.End(new DefaultPipelineResult(null, BreakLevel.None) { HadErrors = true }); - return true; - } - return false; - } - - private static bool TryModuleVersion(string moduleVersion, string requiredVersion) - { - return SemanticVersion.TryParseVersion(moduleVersion, out var version) && - SemanticVersion.TryParseConstraint(requiredVersion, out var constraint) && - constraint.Equals(version); - } - - protected PipelineContext PrepareContext(BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField) - { - var unresolved = new List(); - if (_Baseline is Configuration.BaselineOption.BaselineRef baselineRef) - unresolved.Add(new BaselineRef(ResolveBaselineGroup(baselineRef.Name), ScopeType.Explicit)); - - return PipelineContext.New( - option: Option, - hostContext: HostContext, - reader: PrepareReader(), - bindTargetName: bindTargetName, - bindTargetType: bindTargetType, - bindField: bindField, - optionBuilder: GetOptionBuilder(), - unresolved: unresolved - ); - } - - protected string[] ResolveBaselineGroup(string[] name) - { - for (var i = 0; name != null && i < name.Length; i++) - name[i] = ResolveBaselineGroup(name[i]); - - return name; - } - - protected string ResolveBaselineGroup(string name) - { - if (name == null || name.Length < 2 || !name.StartsWith("@") || - Option == null || Option.Baseline == null || Option.Baseline.Group == null || - Option.Baseline.Group.Count == 0) - return name; - - var key = name.Substring(1); - if (!Option.Baseline.Group.TryGetValue(key, out var baselines) || baselines.Length == 0) - throw new PipelineConfigurationException("Baseline.Group", string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.PSR0003, key)); - - var writer = PrepareWriter(); - writer.WriteVerbose($"Using baseline group '{key}': {baselines[0]}"); - return baselines[0]; - } - - protected virtual PipelineInputStream PrepareReader() - { - return new PipelineInputStream(null, null, GetInputObjectSourceFilter(), Option); - } - - protected virtual PipelineWriter PrepareWriter() - { - if (_Writer != null) - return _Writer; - - var output = GetOutput(); - _Writer = Option.Output.Format switch - { - OutputFormat.Csv => new CsvOutputWriter(output, Option, ShouldProcess), - OutputFormat.Json => new JsonOutputWriter(output, Option, ShouldProcess), - OutputFormat.NUnit3 => new NUnit3OutputWriter(output, Option, ShouldProcess), - OutputFormat.Yaml => new YamlOutputWriter(output, Option, ShouldProcess), - OutputFormat.Markdown => new MarkdownOutputWriter(output, Option, ShouldProcess), - OutputFormat.Wide => new WideOutputWriter(output, Option, ShouldProcess), - OutputFormat.Sarif => new SarifOutputWriter(Source, output, Option, ShouldProcess), - _ => output, - }; - return _Writer; - } - - protected virtual PipelineWriter GetOutput(bool writeHost = false) - { - // Redirect to file instead - return !string.IsNullOrEmpty(Option.Output.Path) - ? new FileOutputWriter( - inner: _Output, - option: Option, - encoding: Option.Output.GetEncoding(), - path: Option.Output.Path, - shouldProcess: HostContext.ShouldProcess, - writeHost: writeHost - ) - : _Output; - } - - protected static string[] GetCulture(string[] culture) - { - var result = new List(); - var parent = new List(); - var set = new HashSet(); - for (var i = 0; culture != null && i < culture.Length; i++) - { - var c = CultureInfo.CreateSpecificCulture(culture[i]); - if (!set.Contains(c.Name)) - { - result.Add(c.Name); - set.Add(c.Name); - } - for (var p = c.Parent; !string.IsNullOrEmpty(p.Name); p = p.Parent) - { - if (!set.Contains(p.Name)) - { - parent.Add(p.Name); - set.Add(p.Name); - } - } - } - if (parent.Count > 0) - result.AddRange(parent); - - return result.Count == 0 ? null : result.ToArray(); - } - - protected static RepositoryOption GetRepository(RepositoryOption option) - { - var result = new RepositoryOption(option); - if (string.IsNullOrEmpty(result.Url) && GitHelper.TryRepository(out var url)) - result.Url = url; - - if (string.IsNullOrEmpty(result.BaseRef) && GitHelper.TryBaseRef(out var baseRef)) - result.BaseRef = baseRef; - - return result; - } - - /// - /// Coalesce execution options with defaults. - /// - protected static ExecutionOption GetExecutionOption(ExecutionOption option) - { - var result = ExecutionOption.Combine(option, ExecutionOption.Default); - - // Handle when preference is set to none. The default should be used. - result.AliasReference = result.AliasReference == ExecutionActionPreference.None ? ExecutionOption.Default.AliasReference.Value : result.AliasReference; - result.DuplicateResourceId = result.DuplicateResourceId == ExecutionActionPreference.None ? ExecutionOption.Default.DuplicateResourceId.Value : result.DuplicateResourceId; - result.InvariantCulture = result.InvariantCulture == ExecutionActionPreference.None ? ExecutionOption.Default.InvariantCulture.Value : result.InvariantCulture; - result.RuleExcluded = result.RuleExcluded == ExecutionActionPreference.None ? ExecutionOption.Default.RuleExcluded.Value : result.RuleExcluded; - result.RuleInconclusive = result.RuleInconclusive == ExecutionActionPreference.None ? ExecutionOption.Default.RuleInconclusive.Value : result.RuleInconclusive; - result.RuleSuppressed = result.RuleSuppressed == ExecutionActionPreference.None ? ExecutionOption.Default.RuleSuppressed.Value : result.RuleSuppressed; - result.SuppressionGroupExpired = result.SuppressionGroupExpired == ExecutionActionPreference.None ? ExecutionOption.Default.SuppressionGroupExpired.Value : result.SuppressionGroupExpired; - result.UnprocessedObject = result.UnprocessedObject == ExecutionActionPreference.None ? ExecutionOption.Default.UnprocessedObject.Value : result.UnprocessedObject; - return result; - } - - protected PathFilter GetInputObjectSourceFilter() - { - return Option.Input.IgnoreObjectSource.GetValueOrDefault(InputOption.Default.IgnoreObjectSource.Value) ? GetInputFilter() : null; - } - - protected PathFilter GetInputFilter() - { - if (_InputFilter == null) - { - var basePath = Environment.GetWorkingPath(); - var ignoreGitPath = Option.Input.IgnoreGitPath ?? InputOption.Default.IgnoreGitPath.Value; - var ignoreRepositoryCommon = Option.Input.IgnoreRepositoryCommon ?? InputOption.Default.IgnoreRepositoryCommon.Value; - var builder = PathFilterBuilder.Create(basePath, Option.Input.PathIgnore, ignoreGitPath, ignoreRepositoryCommon); - builder.UseGitIgnore(); - - _InputFilter = builder.Build(); - } - return _InputFilter; - } - - private OptionContextBuilder GetOptionBuilder() - { - return new OptionContextBuilder(Option, _Include, _Tag, _Convention); - } - - protected void ConfigureBinding(PSRuleOption option) - { - if (option.Pipeline.BindTargetName != null && option.Pipeline.BindTargetName.Count > 0) - { - // Do not allow custom binding functions to be used with constrained language mode - if (Option.Execution.LanguageMode == LanguageMode.ConstrainedLanguage) - throw new PipelineConfigurationException(optionName: "BindTargetName", message: PSRuleResources.ConstrainedTargetBinding); - - foreach (var action in option.Pipeline.BindTargetName) - BindTargetNameHook = AddBindTargetAction(action, BindTargetNameHook); - } - - if (option.Pipeline.BindTargetType != null && option.Pipeline.BindTargetType.Count > 0) - { - // Do not allow custom binding functions to be used with constrained language mode - if (Option.Execution.LanguageMode == LanguageMode.ConstrainedLanguage) - throw new PipelineConfigurationException(optionName: "BindTargetType", message: PSRuleResources.ConstrainedTargetBinding); - - foreach (var action in option.Pipeline.BindTargetType) - BindTargetTypeHook = AddBindTargetAction(action, BindTargetTypeHook); - } - } - - private static BindTargetMethod AddBindTargetAction(BindTargetFunc action, BindTargetMethod previous) - { - // Nest the previous write action in the new supplied action - // Execution chain will be: action -> previous -> previous..n - return (string[] propertyNames, bool caseSensitive, bool preferTargetInfo, object targetObject, out string path) => - { - return action(propertyNames, caseSensitive, preferTargetInfo, targetObject, previous, out path); - }; - } - - private static BindTargetMethod AddBindTargetAction(BindTargetName action, BindTargetMethod previous) - { - return AddBindTargetAction((string[] propertyNames, bool caseSensitive, bool preferTargetInfo, object targetObject, BindTargetMethod next, out string path) => - { - path = null; - var targetType = action(targetObject); - return string.IsNullOrEmpty(targetType) ? next(propertyNames, caseSensitive, preferTargetInfo, targetObject, out path) : targetType; - }, previous); - } - - protected void AddVisitTargetObjectAction(VisitTargetObjectAction action) - { - // Nest the previous write action in the new supplied action - // Execution chain will be: action -> previous -> previous..n - var previous = VisitTargetObject; - VisitTargetObject = (targetObject) => action(targetObject, previous); - } - - /// - /// Normalizes JSON indent range between minimum 0 and maximum 4. - /// - /// - /// The number of characters to indent. - protected static int NormalizeJsonIndentRange(int? jsonIndent) - { - if (jsonIndent.HasValue) - { - if (jsonIndent < MIN_JSON_INDENT) - return MIN_JSON_INDENT; - - else if (jsonIndent > MAX_JSON_INDENT) - return MAX_JSON_INDENT; - - return jsonIndent.Value; - } - return MIN_JSON_INDENT; - } - - protected bool TryChangedFiles(out string[] files) - { - files = null; - if (!Option.Input.IgnoreUnchangedPath.GetValueOrDefault(InputOption.Default.IgnoreUnchangedPath.Value) || - !GitHelper.TryGetChangedFiles(Option.Repository.BaseRef, "d", null, out files)) - return false; - - for (var i = 0; i < files.Length; i++) - HostContext.Verbose(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.UsingChangedFile, files[i])); - - return true; - } - - protected bool ShouldProcess(string target, string action) - { - return HostContext == null || HostContext.ShouldProcess(target, action); - } - - protected static OutputStyle GetStyle(OutputStyle style) - { - if (style != OutputStyle.Detect) - return style; - - if (Environment.IsAzurePipelines()) - return OutputStyle.AzurePipelines; - - if (Environment.IsGitHubActions()) - return OutputStyle.GitHubActions; - - return Environment.IsVisualStudioCode() ? - OutputStyle.VisualStudioCode : - OutputStyle.Client; - } -} diff --git a/src/PSRule/Pipeline/PipelineBuilderBase.cs b/src/PSRule/Pipeline/PipelineBuilderBase.cs new file mode 100644 index 0000000000..d60d463342 --- /dev/null +++ b/src/PSRule/Pipeline/PipelineBuilderBase.cs @@ -0,0 +1,393 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections; +using System.Globalization; +using PSRule.Configuration; +using PSRule.Data; +using PSRule.Definitions; +using PSRule.Definitions.Baselines; +using PSRule.Options; +using PSRule.Pipeline.Output; +using PSRule.Resources; + +namespace PSRule.Pipeline; + +/// +/// A base instance for a pipeline builder. +/// +internal abstract class PipelineBuilderBase : IPipelineBuilder +{ + private const string ENGINE_MODULE_NAME = "PSRule"; + + protected readonly PSRuleOption Option; + protected readonly Source[] Source; + protected readonly IHostContext HostContext; + + private string[] _Include; + private Hashtable _Tag; + private Configuration.BaselineOption _Baseline; + private string[] _Convention; + private PathFilter _InputFilter; + private PipelineWriter _Writer; + + private readonly HostPipelineWriter _Output; + + private const int MIN_JSON_INDENT = 0; + private const int MAX_JSON_INDENT = 4; + + protected PipelineBuilderBase(Source[] source, IHostContext hostContext) + { + Option = new PSRuleOption(); + Source = source; + _Output = new HostPipelineWriter(hostContext, Option, ShouldProcess); + HostContext = hostContext; + } + + /// + /// Determines if the pipeline is executing in a remote PowerShell session. + /// + public bool InSession => HostContext != null && HostContext.InSession; + + /// + public void Name(string[] name) + { + if (name == null || name.Length == 0) + return; + + _Include = name; + } + + /// + public void Tag(Hashtable tag) + { + if (tag == null || tag.Count == 0) + return; + + _Tag = tag; + } + + /// + public void Convention(string[] convention) + { + if (convention == null || convention.Length == 0) + return; + + _Convention = convention; + } + + /// + public virtual IPipelineBuilder Configure(PSRuleOption option) + { + if (option == null) + return this; + + Option.Baseline = new Options.BaselineOption(option.Baseline); + Option.Binding = new BindingOption(option.Binding); + Option.Convention = new ConventionOption(option.Convention); + Option.Execution = GetExecutionOption(option.Execution); + Option.Input = new InputOption(option.Input); + Option.Input.Format ??= InputOption.Default.Format; + Option.Output = new OutputOption(option.Output); + Option.Output.Outcome ??= OutputOption.Default.Outcome; + Option.Output.Banner ??= OutputOption.Default.Banner; + Option.Output.Style = GetStyle(option.Output.Style ?? OutputOption.Default.Style.Value); + Option.Repository = GetRepository(option.Repository); + return this; + } + + /// + public abstract IPipeline Build(IPipelineWriter writer = null); + + /// + public void Baseline(Configuration.BaselineOption baseline) + { + if (baseline == null) + return; + + _Baseline = baseline; + } + + /// + /// Require correct module versions for pipeline execution. + /// + protected bool RequireModules() + { + var result = true; + if (Option.Requires.TryGetValue(ENGINE_MODULE_NAME, out var requiredVersion)) + { + var engineVersion = Engine.GetVersion(); + if (GuardModuleVersion(ENGINE_MODULE_NAME, engineVersion, requiredVersion)) + result = false; + } + for (var i = 0; Source != null && i < Source.Length; i++) + { + if (Source[i].Module != null && Option.Requires.TryGetValue(Source[i].Module.Name, out requiredVersion)) + { + if (GuardModuleVersion(Source[i].Module.Name, Source[i].Module.Version, requiredVersion)) + result = false; + } + } + return result; + } + + /// + /// Require sources for pipeline execution. + /// + protected bool RequireSources() + { + if (Source == null || Source.Length == 0) + { + PrepareWriter().WarnRulePathNotFound(); + return false; + } + return true; + } + + private bool GuardModuleVersion(string moduleName, string moduleVersion, string requiredVersion) + { + if (!TryModuleVersion(moduleVersion, requiredVersion)) + { + var writer = PrepareWriter(); + writer.ErrorRequiredVersionMismatch(moduleName, moduleVersion, requiredVersion); + writer.End(new DefaultPipelineResult(null, BreakLevel.None) { HadErrors = true }); + return true; + } + return false; + } + + private static bool TryModuleVersion(string moduleVersion, string requiredVersion) + { + return SemanticVersion.TryParseVersion(moduleVersion, out var version) && + SemanticVersion.TryParseConstraint(requiredVersion, out var constraint) && + constraint.Accepts(version); + } + + /// + /// Create a pipeline context. + /// + protected PipelineContext PrepareContext((BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField) binding) + { + var unresolved = new List(); + if (_Baseline is Configuration.BaselineOption.BaselineRef baselineRef) + unresolved.Add(new BaselineRef(ResolveBaselineGroup(baselineRef.Name), ScopeType.Explicit)); + + return PipelineContext.New( + option: Option, + hostContext: HostContext, + reader: PrepareReader(), + bindTargetName: binding.bindTargetName, + bindTargetType: binding.bindTargetType, + bindField: binding.bindField, + optionBuilder: GetOptionBuilder(binding), + unresolved: unresolved + ); + } + + protected string[] ResolveBaselineGroup(string[] name) + { + for (var i = 0; name != null && i < name.Length; i++) + name[i] = ResolveBaselineGroup(name[i]); + + return name; + } + + protected string ResolveBaselineGroup(string name) + { + if (name == null || name.Length < 2 || !name.StartsWith("@") || + Option == null || Option.Baseline == null || Option.Baseline.Group == null || + Option.Baseline.Group.Count == 0) + return name; + + var key = name.Substring(1); + if (!Option.Baseline.Group.TryGetValue(key, out var baselines) || baselines.Length == 0) + throw new PipelineConfigurationException("Baseline.Group", string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.PSR0003, key)); + + var writer = PrepareWriter(); + writer.WriteVerbose($"Using baseline group '{key}': {baselines[0]}"); + return baselines[0]; + } + + protected virtual PipelineInputStream PrepareReader() + { + return new PipelineInputStream(null, GetInputObjectSourceFilter(), Option); + } + + protected virtual PipelineWriter PrepareWriter() + { + if (_Writer != null) + return _Writer; + + var output = GetOutput(); + _Writer = Option.Output.Format switch + { + OutputFormat.Csv => new CsvOutputWriter(output, Option, ShouldProcess), + OutputFormat.Json => new JsonOutputWriter(output, Option, ShouldProcess), + OutputFormat.NUnit3 => new NUnit3OutputWriter(output, Option, ShouldProcess), + OutputFormat.Yaml => new YamlOutputWriter(output, Option, ShouldProcess), + OutputFormat.Markdown => new MarkdownOutputWriter(output, Option, ShouldProcess), + OutputFormat.Wide => new WideOutputWriter(output, Option, ShouldProcess), + OutputFormat.Sarif => new SarifOutputWriter(Source, output, Option, ShouldProcess), + _ => output, + }; + return _Writer; + } + + protected virtual PipelineWriter GetOutput(bool writeHost = false) + { + // Redirect to file instead + return !string.IsNullOrEmpty(Option.Output.Path) + ? new FileOutputWriter( + inner: _Output, + option: Option, + encoding: Option.Output.GetEncoding(), + path: Option.Output.Path, + shouldProcess: HostContext.ShouldProcess, + writeHost: writeHost + ) + : _Output; + } + + protected static string[] GetCulture(string[] culture) + { + var result = new List(); + var parent = new List(); + var set = new HashSet(); + for (var i = 0; culture != null && i < culture.Length; i++) + { + var c = CultureInfo.CreateSpecificCulture(culture[i]); + if (!set.Contains(c.Name)) + { + result.Add(c.Name); + set.Add(c.Name); + } + for (var p = c.Parent; !string.IsNullOrEmpty(p.Name); p = p.Parent) + { + if (!set.Contains(p.Name)) + { + parent.Add(p.Name); + set.Add(p.Name); + } + } + } + if (parent.Count > 0) + result.AddRange(parent); + + return result.Count == 0 ? null : result.ToArray(); + } + + protected static RepositoryOption GetRepository(RepositoryOption option) + { + var result = new RepositoryOption(option); + if (string.IsNullOrEmpty(result.Url) && GitHelper.TryRepository(out var url)) + result.Url = url; + + if (string.IsNullOrEmpty(result.BaseRef) && GitHelper.TryBaseRef(out var baseRef)) + result.BaseRef = baseRef; + + return result; + } + + /// + /// Coalesce execution options with defaults. + /// + protected static ExecutionOption GetExecutionOption(ExecutionOption option) + { + var result = ExecutionOption.Combine(option, ExecutionOption.Default); + + // Handle when preference is set to none. The default should be used. + result.AliasReference = result.AliasReference == ExecutionActionPreference.None ? ExecutionOption.Default.AliasReference.Value : result.AliasReference; + result.DuplicateResourceId = result.DuplicateResourceId == ExecutionActionPreference.None ? ExecutionOption.Default.DuplicateResourceId.Value : result.DuplicateResourceId; + result.InvariantCulture = result.InvariantCulture == ExecutionActionPreference.None ? ExecutionOption.Default.InvariantCulture.Value : result.InvariantCulture; + result.RuleExcluded = result.RuleExcluded == ExecutionActionPreference.None ? ExecutionOption.Default.RuleExcluded.Value : result.RuleExcluded; + result.RuleInconclusive = result.RuleInconclusive == ExecutionActionPreference.None ? ExecutionOption.Default.RuleInconclusive.Value : result.RuleInconclusive; + result.RuleSuppressed = result.RuleSuppressed == ExecutionActionPreference.None ? ExecutionOption.Default.RuleSuppressed.Value : result.RuleSuppressed; + result.SuppressionGroupExpired = result.SuppressionGroupExpired == ExecutionActionPreference.None ? ExecutionOption.Default.SuppressionGroupExpired.Value : result.SuppressionGroupExpired; + result.UnprocessedObject = result.UnprocessedObject == ExecutionActionPreference.None ? ExecutionOption.Default.UnprocessedObject.Value : result.UnprocessedObject; + return result; + } + + protected PathFilter GetInputObjectSourceFilter() + { + return Option.Input.IgnoreObjectSource.GetValueOrDefault(InputOption.Default.IgnoreObjectSource.Value) ? GetInputFilter() : null; + } + + protected PathFilter GetInputFilter() + { + if (_InputFilter == null) + { + var basePath = Environment.GetWorkingPath(); + var ignoreGitPath = Option.Input.IgnoreGitPath ?? InputOption.Default.IgnoreGitPath.Value; + var ignoreRepositoryCommon = Option.Input.IgnoreRepositoryCommon ?? InputOption.Default.IgnoreRepositoryCommon.Value; + var builder = PathFilterBuilder.Create(basePath, Option.Input.PathIgnore, ignoreGitPath, ignoreRepositoryCommon); + builder.UseGitIgnore(); + + _InputFilter = builder.Build(); + } + return _InputFilter; + } + + protected void ConfigureBinding(PSRuleOption option) + { + } + + private OptionContextBuilder GetOptionBuilder((BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField) binding) + { + var builder = new OptionContextBuilder(Option, _Include, _Tag, _Convention, binding.bindTargetName, binding.bindTargetType, binding.bindField); + return builder; + } + + /// + /// Normalizes JSON indent range between minimum 0 and maximum 4. + /// + /// + /// The number of characters to indent. + protected static int NormalizeJsonIndentRange(int? jsonIndent) + { + if (jsonIndent.HasValue) + { + if (jsonIndent < MIN_JSON_INDENT) + return MIN_JSON_INDENT; + + else if (jsonIndent > MAX_JSON_INDENT) + return MAX_JSON_INDENT; + + return jsonIndent.Value; + } + return MIN_JSON_INDENT; + } + + protected bool TryChangedFiles(out string[] files) + { + files = null; + if (!Option.Input.IgnoreUnchangedPath.GetValueOrDefault(InputOption.Default.IgnoreUnchangedPath.Value) || + !GitHelper.TryGetChangedFiles(Option.Repository.BaseRef, "d", null, out files)) + return false; + + for (var i = 0; i < files.Length; i++) + HostContext.Verbose(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.UsingChangedFile, files[i])); + + return true; + } + + protected bool ShouldProcess(string target, string action) + { + return HostContext == null || HostContext.ShouldProcess(target, action); + } + + protected static OutputStyle GetStyle(OutputStyle style) + { + if (style != OutputStyle.Detect) + return style; + + if (Environment.IsAzurePipelines()) + return OutputStyle.AzurePipelines; + + if (Environment.IsGitHubActions()) + return OutputStyle.GitHubActions; + + return Environment.IsVisualStudioCode() ? + OutputStyle.VisualStudioCode : + OutputStyle.Client; + } +} diff --git a/src/PSRule/Pipeline/PipelineContext.cs b/src/PSRule/Pipeline/PipelineContext.cs index 1e46fa7654..918ba29b75 100644 --- a/src/PSRule/Pipeline/PipelineContext.cs +++ b/src/PSRule/Pipeline/PipelineContext.cs @@ -20,6 +20,9 @@ namespace PSRule.Pipeline; +/// +/// Context applicable to the whole pipeline, including during early stage setup. +/// internal sealed class PipelineContext : IDisposable, IBindingContext { private const string ErrorPreference = "ErrorActionPreference"; @@ -44,7 +47,7 @@ internal sealed class PipelineContext : IDisposable, IBindingContext // Track whether Dispose has been called. private bool _Disposed; - internal PSRuleOption Option; + public PSRuleOption Option { get; } internal readonly Dictionary LocalizedDataCache; internal readonly Dictionary ExpressionCache; @@ -53,9 +56,6 @@ internal sealed class PipelineContext : IDisposable, IBindingContext internal readonly List SuppressionGroup; internal readonly IHostContext HostContext; internal readonly PipelineInputStream Reader; - internal readonly BindTargetMethod BindTargetName; - internal readonly BindTargetMethod BindTargetType; - internal readonly BindTargetMethod BindField; internal readonly string RunId; internal readonly Stopwatch RunTime; @@ -70,9 +70,6 @@ private PipelineContext(PSRuleOption option, IHostContext hostContext, PipelineI Option = option; HostContext = hostContext; Reader = reader; - BindTargetName = bindTargetName; - BindTargetType = bindTargetType; - BindField = bindField; _LanguageMode = option.Execution.LanguageMode ?? ExecutionOption.Default.LanguageMode.Value; _PathExpressionCache = new Dictionary(); LocalizedDataCache = new Dictionary(); @@ -114,27 +111,6 @@ public string[] SourceContentCache } } - internal enum ResourceIssueType - { - Unknown - } - - internal sealed class ResourceIssue - { - public ResourceIssue(ResourceKind kind, string id, ResourceIssueType issue) - { - Kind = kind; - Id = id; - Issue = issue; - } - - public ResourceKind Kind { get; } - - public string Id { get; } - - public ResourceIssueType Issue { get; } - } - internal Runspace GetRunspace() { if (_Runspace == null) diff --git a/src/PSRule/Pipeline/PipelineHookActions.cs b/src/PSRule/Pipeline/PipelineHookActions.cs index 82e0e1d608..d3702e11fb 100644 --- a/src/PSRule/Pipeline/PipelineHookActions.cs +++ b/src/PSRule/Pipeline/PipelineHookActions.cs @@ -18,6 +18,9 @@ namespace PSRule.Pipeline; /// internal static class PipelineHookActions { + internal static readonly (BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField) Default = (PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField); + internal static readonly (BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField) Empty = (null, null, null); + private const string Property_TargetName = "TargetName"; private const string Property_Name = "Name"; diff --git a/src/PSRule/Pipeline/PipelineInputStream.cs b/src/PSRule/Pipeline/PipelineInputStream.cs index 9d72cd8244..abac0452d3 100644 --- a/src/PSRule/Pipeline/PipelineInputStream.cs +++ b/src/PSRule/Pipeline/PipelineInputStream.cs @@ -14,17 +14,15 @@ namespace PSRule.Pipeline; /// /// A stream of input objects that will be evaluated. /// -internal sealed class PipelineInputStream +internal sealed class PipelineInputStream : IPipelineReader { - private readonly VisitTargetObject _Input; private readonly InputPathBuilder _InputPath; private readonly PathFilter _InputFilter; private readonly ConcurrentQueue _Queue; private readonly EmitterCollection _EmitterCollection; - public PipelineInputStream(VisitTargetObject input, InputPathBuilder inputPath, PathFilter inputFilter, PSRuleOption option) + public PipelineInputStream(InputPathBuilder inputPath, PathFilter inputFilter, PSRuleOption option) { - _Input = input; _InputPath = inputPath; _InputFilter = inputFilter; _Queue = new ConcurrentQueue(); @@ -35,12 +33,7 @@ public PipelineInputStream(VisitTargetObject input, InputPathBuilder inputPath, public bool IsEmpty => _Queue.IsEmpty; - /// - /// Add a new object into the stream. - /// - /// An object to process. - /// A pre-bound type. - /// Determines if expansion is skipped. + /// public void Enqueue(object sourceObject, string? targetType = null, bool skipExpansion = false) { if (sourceObject == null) @@ -55,11 +48,13 @@ public void Enqueue(object sourceObject, string? targetType = null, bool skipExp _EmitterCollection.Visit(sourceObject); } + /// public bool TryDequeue(out ITargetObject sourceObject) { return _Queue.TryDequeue(out sourceObject); } + /// public void Open() { if (_InputPath == null || _InputPath.Count == 0) @@ -97,11 +92,8 @@ private bool ShouldQueue(TargetObject targetObject) return true; } - /// - /// Add a path to the list of inputs. - /// - /// The path of files to add. - internal void Add(string path) + /// + public void Add(string path) { _InputPath.Add(path); } diff --git a/src/PSRule/Pipeline/PipelineWriterExtensions.cs b/src/PSRule/Pipeline/PipelineWriterExtensions.cs index 0790d862ac..ba8c43add7 100644 --- a/src/PSRule/Pipeline/PipelineWriterExtensions.cs +++ b/src/PSRule/Pipeline/PipelineWriterExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Management.Automation; +using System.Management.Automation.Language; using PSRule.Resources; namespace PSRule.Pipeline; @@ -98,6 +99,21 @@ internal static void WriteError(this IPipelineWriter writer, PipelineException e writer.WriteError(new ErrorRecord(exception, errorId, errorCategory, null)); } + internal static void WriteError(this IPipelineWriter writer, ParseError error) + { + if (writer == null || !writer.ShouldWriteError()) + return; + + var record = new ErrorRecord + ( + exception: new Pipeline.ParseException(message: error.Message, errorId: error.ErrorId), + errorId: error.ErrorId, + errorCategory: ErrorCategory.InvalidOperation, + targetObject: null + ); + writer.WriteError(errorRecord: record); + } + internal static void WriteDebug(this IPipelineWriter writer, string message, params object[] args) { if (writer == null || !writer.ShouldWriteDebug() || string.IsNullOrEmpty(message)) @@ -109,6 +125,14 @@ internal static void WriteDebug(this IPipelineWriter writer, string message, par )); } + internal static void VerboseRuleDiscovery(this IPipelineWriter writer, string path) + { + if (writer == null || !writer.ShouldWriteVerbose() || string.IsNullOrEmpty(path)) + return; + + writer.WriteVerbose($"[PSRule][D] -- Discovering rules in: {path}"); + } + private static string Format(string message, params object[] args) { return args == null || args.Length == 0 ? message : string.Format(Thread.CurrentThread.CurrentCulture, message, args); diff --git a/src/PSRule/Pipeline/ResourceCache.cs b/src/PSRule/Pipeline/ResourceCache.cs new file mode 100644 index 0000000000..9bfecb3541 --- /dev/null +++ b/src/PSRule/Pipeline/ResourceCache.cs @@ -0,0 +1,169 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Definitions; +using PSRule.Definitions.Baselines; +using PSRule.Definitions.ModuleConfigs; +using PSRule.Definitions.Selectors; +using PSRule.Definitions.SuppressionGroups; + +namespace PSRule.Pipeline; + +#nullable enable + +/// +/// Define a cache for resources. +/// +internal sealed class ResourceCache : IResourceCache +{ + private readonly List _TrackedIssues; + private readonly IList _Unresolved; + + internal readonly Dictionary ModuleConfigs; + internal readonly Dictionary Baselines; + internal readonly List Selectors; + internal readonly List SuppressionGroups; + + public ResourceCache(IList unresolved) + { + _TrackedIssues = []; + Selectors = []; + SuppressionGroups = []; + ModuleConfigs = new Dictionary(StringComparer.OrdinalIgnoreCase); + Baselines = new Dictionary(StringComparer.OrdinalIgnoreCase); + _Unresolved = unresolved ?? []; + } + + public IEnumerable Issues => _TrackedIssues; + + public IEnumerable Unresolved => _Unresolved; + + public bool Import(IResource resource) + { + if (resource == null) throw new ArgumentNullException(nameof(resource)); + + if (TrackIssue(resource)) + { + + } + else if (TryBaseline(resource, out var baseline) && TryBaselineRef(resource.Id, out var baselineRef)) + { + RemoveBaselineRef(resource.Id); + //_OptionBuilder.Baseline(baselineRef.Type, baseline.BaselineId, resource.Source.Module, baseline.Spec, baseline.Obsolete); + Baselines.Add(resource.Id.Value, (baseline!, baselineRef!)); + return true; + } + else if (TrySelector(resource, out var selector)) + { + Selectors.Add(selector!); + return true; + } + else if (TryModuleConfig(resource, out var moduleConfig)) + { + if (!string.IsNullOrEmpty(moduleConfig!.Spec?.Rule?.Baseline)) + { + var baselineId = ResourceHelper.GetIdString(moduleConfig.Source.Module, moduleConfig.Spec!.Rule.Baseline); + if (!Baselines.ContainsKey(baselineId)) + _Unresolved.Add(new BaselineRef(baselineId, ScopeType.Baseline)); + } + // _OptionBuilder.ModuleConfig(resource.Source.Module, moduleConfig?.Spec); + ModuleConfigs.Add(resource.Source.Module, moduleConfig); + return true; + } + else if (TrySuppressionGroup(resource, out var suppressionGroup)) + { + if (!suppressionGroup!.Spec.ExpiresOn.HasValue || suppressionGroup.Spec.ExpiresOn.Value > DateTime.UtcNow) + { + SuppressionGroups.Add(suppressionGroup); + return true; + } + } + return false; + } + + /// + /// Check for and track resource issues. + /// + /// If the resource should be ignored then return true, otherwise false is returned. + private bool TrackIssue(IResource resource) + { + if (TrySuppressionGroup(resource, out var suppressionGroup)) + { + if (suppressionGroup!.Spec.ExpiresOn.HasValue && suppressionGroup.Spec.ExpiresOn.Value <= DateTime.UtcNow) + { + _TrackedIssues.Add(new ResourceIssue(resource.Kind, resource.Id, ResourceIssueType.SuppressionGroupExpired)); + return true; + } + } + return false; + } + + private bool TryBaselineRef(ResourceId resourceId, out BaselineRef? baselineRef) + { + baselineRef = null; + var r = _Unresolved.FirstOrDefault(i => ResourceIdEqualityComparer.IdEquals(i.Id, resourceId.Value)); + if (r is not BaselineRef br) + return false; + + baselineRef = br; + return true; + } + + private void RemoveBaselineRef(ResourceId resourceId) + { + foreach (var r in _Unresolved.ToArray()) + { + if (ResourceIdEqualityComparer.IdEquals(r.Id, resourceId.Value)) + _Unresolved.Remove(r); + } + } + + private static bool TryBaseline(IResource resource, out Baseline? baseline) + { + baseline = null; + if (resource.Kind == ResourceKind.Baseline && resource is Baseline result) + { + baseline = result; + return true; + } + return false; + } + + private static bool TryModuleConfig(IResource resource, out ModuleConfigV1? moduleConfig) + { + moduleConfig = null; + if (resource.Kind == ResourceKind.ModuleConfig && + !string.IsNullOrEmpty(resource.Source.Module) && + StringComparer.OrdinalIgnoreCase.Equals(resource.Source.Module, resource.Name) && + resource is ModuleConfigV1 result) + { + moduleConfig = result; + return true; + } + return false; + } + + private static bool TrySelector(IResource resource, out SelectorV1? selector) + { + selector = null; + if (resource.Kind == ResourceKind.Selector && resource is SelectorV1 result) + { + selector = result; + return true; + } + return false; + } + + private static bool TrySuppressionGroup(IResource resource, out SuppressionGroupV1? suppressionGroup) + { + suppressionGroup = null; + if (resource.Kind == ResourceKind.SuppressionGroup && resource is SuppressionGroupV1 result) + { + suppressionGroup = result; + return true; + } + return false; + } +} + +#nullable restore diff --git a/src/PSRule/Pipeline/ResourceCacheBuilder.cs b/src/PSRule/Pipeline/ResourceCacheBuilder.cs new file mode 100644 index 0000000000..de5cf6bf9d --- /dev/null +++ b/src/PSRule/Pipeline/ResourceCacheBuilder.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Definitions; +using PSRule.Host; + +namespace PSRule.Pipeline; + +/// +/// Defines a builder to create a resource cache. +/// +internal sealed class ResourceCacheBuilder(IPipelineWriter writer) +{ + private IEnumerable _Resources; + private readonly IPipelineWriter _Writer = writer; + + public void Import(Source[] sources) + { + _Resources = HostHelper.GetMetaResources(sources, new ResourceCacheDiscoveryContext(_Writer)); + } + + public ResourceCache Build(List unresolved) + { + var cache = new ResourceCache(unresolved); + + foreach (var resource in _Resources) + cache.Import(resource); + + return cache; + } +} diff --git a/src/PSRule/Pipeline/ResourceCacheDiscoveryContext.cs b/src/PSRule/Pipeline/ResourceCacheDiscoveryContext.cs new file mode 100644 index 0000000000..8000bbf92b --- /dev/null +++ b/src/PSRule/Pipeline/ResourceCacheDiscoveryContext.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Definitions; +using PSRule.Runtime; + +namespace PSRule.Pipeline; + +/// +/// Define a context used for early stage resource discovery. +/// +internal sealed class ResourceCacheDiscoveryContext(IPipelineWriter writer) : IResourceDiscoveryContext +{ + public IPipelineWriter Writer { get; } = writer; + + public void EnterLanguageScope(ISourceFile file) + { + + } + + public void ExitLanguageScope(ISourceFile file) + { + + } + + public void PopScope(RunspaceScope scope) + { + + } + + public void PushScope(RunspaceScope scope) + { + + } +} diff --git a/src/PSRule/Pipeline/ResourceIssue.cs b/src/PSRule/Pipeline/ResourceIssue.cs new file mode 100644 index 0000000000..d9ca6c899c --- /dev/null +++ b/src/PSRule/Pipeline/ResourceIssue.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Definitions; + +namespace PSRule.Pipeline; + +internal sealed class ResourceIssue +{ + public ResourceIssue(ResourceKind resourceKind, ResourceId resourceId, ResourceIssueType issue) + { + ResourceKind = resourceKind; + ResourceId = resourceId; + Issue = issue; + } + + public ResourceKind ResourceKind { get; } + + public ResourceId ResourceId { get; } + + public ResourceIssueType Issue { get; } +} diff --git a/src/PSRule/Pipeline/ResourceIssueType.cs b/src/PSRule/Pipeline/ResourceIssueType.cs new file mode 100644 index 0000000000..ba4ee36301 --- /dev/null +++ b/src/PSRule/Pipeline/ResourceIssueType.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace PSRule.Pipeline; + +internal enum ResourceIssueType +{ + Unknown, + + SuppressionGroupExpired +} diff --git a/src/PSRule/Pipeline/RulePipeline.cs b/src/PSRule/Pipeline/RulePipeline.cs index afc93a9312..2c6b1c4f7d 100644 --- a/src/PSRule/Pipeline/RulePipeline.cs +++ b/src/PSRule/Pipeline/RulePipeline.cs @@ -12,16 +12,16 @@ internal abstract class RulePipeline : IPipeline protected readonly PipelineContext Pipeline; protected readonly RunspaceContext Context; protected readonly Source[] Source; - protected readonly PipelineInputStream Reader; + protected readonly IPipelineReader Reader; protected readonly IPipelineWriter Writer; // Track whether Dispose has been called. private bool _Disposed; - protected RulePipeline(PipelineContext context, Source[] source, PipelineInputStream reader, IPipelineWriter writer) + protected RulePipeline(PipelineContext pipelineContext, Source[] source, IPipelineReader reader, IPipelineWriter writer) { - Result = new DefaultPipelineResult(writer, context.Option.Execution.Break.GetValueOrDefault(ExecutionOption.Default.Break.Value)); - Pipeline = context; + Result = new DefaultPipelineResult(writer, pipelineContext.Option.Execution.Break.GetValueOrDefault(ExecutionOption.Default.Break.Value)); + Pipeline = pipelineContext; Context = new RunspaceContext(Pipeline, writer); Source = source; Reader = reader; diff --git a/src/PSRule/Pipeline/Source.cs b/src/PSRule/Pipeline/Source.cs index 5d6b0fd913..ba21e9bf97 100644 --- a/src/PSRule/Pipeline/Source.cs +++ b/src/PSRule/Pipeline/Source.cs @@ -4,7 +4,6 @@ using System.Collections; using System.Management.Automation; using PSRule.Definitions; -using PSRule.Runtime; namespace PSRule.Pipeline; diff --git a/src/PSRule/Pipeline/TargetBinder.cs b/src/PSRule/Pipeline/TargetBinder.cs deleted file mode 100644 index d26ed9bf04..0000000000 --- a/src/PSRule/Pipeline/TargetBinder.cs +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Collections; -using PSRule.Configuration; -using PSRule.Runtime; -using static PSRule.Pipeline.TargetBinder; - -namespace PSRule.Pipeline; - -/// -/// Responsible for handling binding for a given target object. -/// -internal interface ITargetBinder -{ - void Bind(TargetObject targetObject); - - ITargetBindingContext Using(string languageScope); - - ITargetBindingResult Result(string languageScope); -} - -/// -/// A binding context specific to a language scope. -/// -internal interface ITargetBindingContext -{ - string LanguageScope { get; } - - ITargetBindingResult Bind(object o); - - ITargetBindingResult Bind(TargetObject o); -} - -internal interface ITargetBindingResult -{ - /// - /// The bound TargetName of the target object. - /// - string TargetName { get; } - - string TargetNamePath { get; } - - /// - /// The bound TargetType of the target object. - /// - string TargetType { get; } - - string TargetTypePath { get; } - - /// - /// Additional bound fields of the target object. - /// - Hashtable Field { get; } - - bool ShouldFilter { get; } -} - -/// -/// Builds a TargetBinder. -/// -internal sealed class TargetBinderBuilder -{ - private readonly List _BindingContext; - private readonly HashSet _TypeFilter; - private readonly BindTargetMethod _BindTargetName; - private readonly BindTargetMethod _BindTargetType; - private readonly BindTargetMethod _BindField; - - public TargetBinderBuilder(BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField, string[] typeFilter) - { - _BindTargetName = bindTargetName; - _BindTargetType = bindTargetType; - _BindField = bindField; - _BindingContext = new List(); - if (typeFilter != null && typeFilter.Length > 0) - _TypeFilter = new HashSet(typeFilter, StringComparer.OrdinalIgnoreCase); - } - - /// - /// Build a TargetBinder. - /// - public ITargetBinder Build() - { - return new TargetBinder(_BindingContext.ToArray()); - } - - /// - /// Add a target binding context. - /// - public void With(ITargetBindingContext bindingContext) - { - _BindingContext.Add(bindingContext); - } - - /// - /// Add a target binding context from language scope. Current this only use for tests. - /// - internal void With(ILanguageScope languageScope) - { - With(new TargetBindingContext(languageScope.Name, languageScope.Binding, _BindTargetName, _BindTargetType, _BindField, _TypeFilter)); - } -} - -/// -/// Responsible for handling binding for a given target object. -/// -internal sealed class TargetBinder : ITargetBinder -{ - private const string STANDALONE_SCOPE = "."; - - private readonly Dictionary _BindingContext; - private readonly Dictionary _BindingResult; - - internal TargetBinder(ITargetBindingContext[] bindingContext) - { - _BindingContext = new Dictionary(); - _BindingResult = new Dictionary(); - for (var i = 0; bindingContext != null && i < bindingContext.Length; i++) - _BindingContext.Add(bindingContext[i].LanguageScope ?? STANDALONE_SCOPE, bindingContext[i]); - } - - private sealed class ImmutableHashtable : Hashtable - { - private bool _ReadOnly; - - internal ImmutableHashtable() - : base(StringComparer.OrdinalIgnoreCase) { } - - public override bool IsReadOnly => _ReadOnly; - - public override void Add(object key, object value) - { - if (_ReadOnly) - throw new InvalidOperationException(); - - base.Add(key, value); - } - - public override void Clear() - { - if (_ReadOnly) - throw new InvalidOperationException(); - - base.Clear(); - } - - public override void Remove(object key) - { - if (_ReadOnly) - throw new InvalidOperationException(); - - base.Remove(key); - } - - public override object this[object key] - { - get => base[key]; - set - { - if (_ReadOnly) - throw new InvalidOperationException(); - - base[key] = value; - } - } - - internal void Protect() - { - _ReadOnly = true; - } - } - - internal sealed class TargetBindingResult : ITargetBindingResult - { - public TargetBindingResult(string targetName, string targetNamePath, string targetType, string targetTypePath, bool shouldFilter, Hashtable field) - { - TargetName = targetName; - TargetNamePath = targetNamePath; - TargetType = targetType; - TargetTypePath = targetTypePath; - ShouldFilter = shouldFilter; - Field = field; - } - - /// - public string TargetName { get; } - - /// - public string TargetNamePath { get; } - - /// - public string TargetType { get; } - - /// - public string TargetTypePath { get; } - - /// - public bool ShouldFilter { get; } - - /// - public Hashtable Field { get; } - } - - internal sealed class TargetBindingContext : ITargetBindingContext - { - private readonly bool _PreferTargetInfo; - private readonly bool _IgnoreCase; - private readonly bool _UseQualifiedName; - private readonly FieldMap _Field; - private readonly string[] _TargetName; - private readonly string[] _TargetType; - private readonly string _NameSeparator; - private readonly BindTargetMethod _BindTargetName; - private readonly BindTargetMethod _BindTargetType; - private readonly BindTargetMethod _BindField; - private readonly HashSet _TypeFilter; - - public TargetBindingContext(string languageScope, BindingOption bindingOption, BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField, HashSet typeFilter) - { - LanguageScope = languageScope; - _PreferTargetInfo = bindingOption?.PreferTargetInfo ?? BindingOption.Default.PreferTargetInfo.Value; - _IgnoreCase = bindingOption?.IgnoreCase ?? BindingOption.Default.IgnoreCase.Value; - _UseQualifiedName = bindingOption?.UseQualifiedName ?? BindingOption.Default.UseQualifiedName.Value; - _Field = bindingOption?.Field; - _TargetName = bindingOption?.TargetName; - _TargetType = bindingOption?.TargetType; - _NameSeparator = bindingOption?.NameSeparator ?? BindingOption.Default.NameSeparator; - _BindTargetName = bindTargetName; - _BindTargetType = bindTargetType; - _BindField = bindField; - _TypeFilter = typeFilter; - } - - public string LanguageScope { get; } - - public ITargetBindingResult Bind(TargetObject o) - { - var targetNamePath = "."; - var targetName = _PreferTargetInfo && o.TargetName != null ? o.TargetName : _BindTargetName(_TargetName, !_IgnoreCase, _PreferTargetInfo, o.Value, out targetNamePath); - var targetTypePath = "."; - var targetType = _PreferTargetInfo && o.TargetType != null ? o.TargetType : _BindTargetType(_TargetType, !_IgnoreCase, _PreferTargetInfo, o.Value, out targetTypePath); - var shouldFilter = !(_TypeFilter == null || _TypeFilter.Contains(targetType)); - - // Bind custom fields - var field = BindField(_BindField, new[] { _Field }, !_IgnoreCase, o.Value); - return Bind(targetName, targetNamePath, targetType, targetTypePath, field); - } - - public ITargetBindingResult Bind(object o) - { - var targetName = _BindTargetName(_TargetName, !_IgnoreCase, _PreferTargetInfo, o, out var targetNamePath); - var targetType = _BindTargetType(_TargetType, !_IgnoreCase, _PreferTargetInfo, o, out var targetTypePath); - var shouldFilter = !(_TypeFilter == null || _TypeFilter.Contains(targetType)); - - // Bind custom fields - var field = BindField(_BindField, new[] { _Field }, !_IgnoreCase, o); - return Bind(targetName, targetNamePath, targetType, targetTypePath, field); - } - - private ITargetBindingResult Bind(string targetName, string targetNamePath, string targetType, string targetTypePath, Hashtable field) - { - var shouldFilter = !(_TypeFilter == null || _TypeFilter.Contains(targetType)); - - // Use qualified name - if (_UseQualifiedName) - targetName = string.Concat(targetType, _NameSeparator, targetName); - - return new TargetBindingResult - ( - targetName: targetName, - targetNamePath: targetNamePath, - targetType: targetType, - targetTypePath: targetTypePath, - shouldFilter: shouldFilter, - field: field - ); - } - } - - public bool ShouldFilter { get; private set; } - - /// - /// Bind target object based on the supplied baseline. - /// - public void Bind(TargetObject targetObject) - { - foreach (var bindingContext in _BindingContext.Values) - _BindingResult[bindingContext.LanguageScope] = bindingContext.Bind(targetObject); - } - - public ITargetBindingContext Using(string languageScope) - { - return _BindingContext.TryGetValue(languageScope ?? STANDALONE_SCOPE, out var result) ? result : null; - } - - public ITargetBindingResult Result(string languageScope) - { - return _BindingResult.TryGetValue(languageScope ?? STANDALONE_SCOPE, out var result) ? result : null; - } - - /// - /// Bind additional fields. - /// - private static Hashtable BindField(BindTargetMethod bindField, FieldMap[] map, bool caseSensitive, object o) - { - if (map == null || map.Length == 0) - return null; - - var hashtable = new ImmutableHashtable(); - for (var i = 0; i < map.Length; i++) - { - if (map[i] == null || map[i].Count == 0) - continue; - - foreach (var field in map[i]) - { - if (hashtable.ContainsKey(field.Key)) - continue; - - hashtable.Add(field.Key, bindField(field.Value, caseSensitive, false, o, out _)); - } - } - hashtable.Protect(); - return hashtable; - } -} diff --git a/src/PSRule/Properties/AssemblyInfo.cs b/src/PSRule/Properties/AssemblyInfo.cs index 424e35fcb8..e6426a36eb 100644 --- a/src/PSRule/Properties/AssemblyInfo.cs +++ b/src/PSRule/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Runtime.CompilerServices; diff --git a/src/PSRule/Resources/ReasonStrings.Designer.cs b/src/PSRule/Resources/ReasonStrings.Designer.cs index b9dacde3f6..c2f6b1fa09 100644 --- a/src/PSRule/Resources/ReasonStrings.Designer.cs +++ b/src/PSRule/Resources/ReasonStrings.Designer.cs @@ -711,9 +711,9 @@ internal static string Version { /// /// Looks up a localized string similar to The version '{0}' does not match the constraint '{1}'.. /// - internal static string VersionContraint { + internal static string VersionConstraint { get { - return ResourceManager.GetString("VersionContraint", resourceCulture); + return ResourceManager.GetString("VersionConstraint", resourceCulture); } } diff --git a/src/PSRule/Resources/ReasonStrings.resx b/src/PSRule/Resources/ReasonStrings.resx index 2b51ac2acb..4cdaa61039 100644 --- a/src/PSRule/Resources/ReasonStrings.resx +++ b/src/PSRule/Resources/ReasonStrings.resx @@ -307,7 +307,7 @@ The field value '{0}' is not a version string. - + The version '{0}' does not match the constraint '{1}'. diff --git a/src/PSRule/Rules/ErrorInfo.cs b/src/PSRule/Rules/ErrorInfo.cs index 44c456ac5a..844c5b102f 100644 --- a/src/PSRule/Rules/ErrorInfo.cs +++ b/src/PSRule/Rules/ErrorInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/src/PSRule/Rules/PowerShellCondition.cs b/src/PSRule/Rules/PowerShellCondition.cs index bcf96ca91a..612ef55b98 100644 --- a/src/PSRule/Rules/PowerShellCondition.cs +++ b/src/PSRule/Rules/PowerShellCondition.cs @@ -4,7 +4,6 @@ using System.Collections.ObjectModel; using System.Management.Automation; using PSRule.Definitions; -using PSRule.Pipeline; namespace PSRule.Rules; diff --git a/src/PSRule/Rules/RuleBlock.cs b/src/PSRule/Rules/RuleBlock.cs index 0dc24a435d..22cbf088c5 100644 --- a/src/PSRule/Rules/RuleBlock.cs +++ b/src/PSRule/Rules/RuleBlock.cs @@ -6,7 +6,6 @@ using Newtonsoft.Json; using PSRule.Definitions; using PSRule.Definitions.Rules; -using PSRule.Pipeline; using PSRule.Runtime; using YamlDotNet.Serialization; diff --git a/src/PSRule/Runtime/Assert.cs b/src/PSRule/Runtime/Assert.cs index c83bec51a8..d249f99ebe 100644 --- a/src/PSRule/Runtime/Assert.cs +++ b/src/PSRule/Runtime/Assert.cs @@ -146,7 +146,7 @@ public AssertResult AnyOf(params AssertResult[] results) if (results[i].Result) return Pass(); else - result.AddReason(result); + result.AddReason(results[i]); } return result; } @@ -859,7 +859,7 @@ public AssertResult Version(PSObject inputObject, string field, string constrain throw new RuleException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.VersionConstraintInvalid, value)); // Assert - return c != null && !c.Equals(value) ? Fail(Operand.FromPath(field), ReasonStrings.VersionContraint, value, constraint) : Pass(); + return c != null && !c.Accepts(value) ? Fail(Operand.FromPath(field), ReasonStrings.VersionConstraint, value, constraint) : Pass(); } /// @@ -883,7 +883,7 @@ public AssertResult APIVersion(PSObject inputObject, string field, string constr throw new RuleException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.VersionConstraintInvalid, value)); // Assert - return c != null && !c.Equals(value) ? Fail(Operand.FromPath(field), ReasonStrings.VersionContraint, value, constraint) : Pass(); + return c != null && !c.Accepts(value) ? Fail(Operand.FromPath(field), ReasonStrings.VersionConstraint, value, constraint) : Pass(); } /// diff --git a/src/PSRule/Runtime/AssertResult.cs b/src/PSRule/Runtime/AssertResult.cs index 85735a39f7..4b307cecbe 100644 --- a/src/PSRule/Runtime/AssertResult.cs +++ b/src/PSRule/Runtime/AssertResult.cs @@ -19,7 +19,7 @@ internal AssertResult(IOperand operand, bool value, string reason, object[] args Result = value; if (!Result) { - _Reason = new List(); + _Reason = []; AddReason(operand, reason, args); } } @@ -60,7 +60,7 @@ internal void AddReason(AssertResult result) /// /// Add a reason. /// - /// Indentifies the operand that was the reason for the failure. + /// Identifies the operand that was the reason for the failure. /// The text of a reason to add. This text should already be localized for the currently culture. /// Replacement arguments for the format string. internal void AddReason(IOperand operand, string text, params object[] args) diff --git a/src/PSRule/Runtime/Binding/ITargetBinder.cs b/src/PSRule/Runtime/Binding/ITargetBinder.cs new file mode 100644 index 0000000000..51b35974d1 --- /dev/null +++ b/src/PSRule/Runtime/Binding/ITargetBinder.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Pipeline; + +namespace PSRule.Runtime.Binding; + +#nullable enable + +/// +/// Responsible for handling binding for a given target object. +/// +internal interface ITargetBinder +{ + /// + /// Bind to an object. + /// + ITargetBindingResult Bind(TargetObject targetObject); + + /// + /// Bind to an object. + /// + ITargetBindingResult Bind(object targetObject); +} + +#nullable restore diff --git a/src/PSRule/Runtime/Binding/ITargetBindingContext.cs b/src/PSRule/Runtime/Binding/ITargetBindingContext.cs new file mode 100644 index 0000000000..3a60aa0eee --- /dev/null +++ b/src/PSRule/Runtime/Binding/ITargetBindingContext.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Pipeline; + +namespace PSRule.Runtime.Binding; + +/// +/// A binding context specific to a language scope. +/// +internal interface ITargetBindingContext +{ + ITargetBindingResult Bind(object o); + + ITargetBindingResult Bind(TargetObject o); +} diff --git a/src/PSRule/Runtime/Binding/ITargetBindingResult.cs b/src/PSRule/Runtime/Binding/ITargetBindingResult.cs new file mode 100644 index 0000000000..636b40d9e2 --- /dev/null +++ b/src/PSRule/Runtime/Binding/ITargetBindingResult.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections; + +namespace PSRule.Runtime.Binding; + +internal interface ITargetBindingResult +{ + /// + /// The bound TargetName of the target object. + /// + string TargetName { get; } + + string TargetNamePath { get; } + + /// + /// The bound TargetType of the target object. + /// + string TargetType { get; } + + string TargetTypePath { get; } + + /// + /// Additional bound fields of the target object. + /// + Hashtable Field { get; } + + bool ShouldFilter { get; } +} diff --git a/src/PSRule/Runtime/Binding/ImmutableHashtable.cs b/src/PSRule/Runtime/Binding/ImmutableHashtable.cs new file mode 100644 index 0000000000..8e0191bf48 --- /dev/null +++ b/src/PSRule/Runtime/Binding/ImmutableHashtable.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections; + +namespace PSRule.Runtime.Binding; + +internal sealed class ImmutableHashtable : Hashtable +{ + private bool _ReadOnly; + + internal ImmutableHashtable() + : base(StringComparer.OrdinalIgnoreCase) { } + + public override bool IsReadOnly => _ReadOnly; + + public override void Add(object key, object value) + { + if (_ReadOnly) + throw new InvalidOperationException(); + + base.Add(key, value); + } + + public override void Clear() + { + if (_ReadOnly) + throw new InvalidOperationException(); + + base.Clear(); + } + + public override void Remove(object key) + { + if (_ReadOnly) + throw new InvalidOperationException(); + + base.Remove(key); + } + + public override object this[object key] + { + get => base[key]; + set + { + if (_ReadOnly) + throw new InvalidOperationException(); + + base[key] = value; + } + } + + internal void Protect() + { + _ReadOnly = true; + } +} diff --git a/src/PSRule/Runtime/Binding/TargetBinder.cs b/src/PSRule/Runtime/Binding/TargetBinder.cs new file mode 100644 index 0000000000..7d46697477 --- /dev/null +++ b/src/PSRule/Runtime/Binding/TargetBinder.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Pipeline; + +namespace PSRule.Runtime.Binding; + +/// +/// Responsible for handling binding for a given target object. +/// +internal sealed class TargetBinder : ITargetBinder +{ + private readonly ITargetBindingContext _BindingContext; + + internal TargetBinder(ITargetBindingContext bindingContext) + { + _BindingContext = bindingContext; + } + + /// + public ITargetBindingResult Bind(TargetObject targetObject) + { + return _BindingContext.Bind(targetObject); + } + + /// + public ITargetBindingResult Bind(object targetObject) + { + return _BindingContext.Bind(targetObject); + } +} diff --git a/src/PSRule/Runtime/Binding/TargetBinderBuilder.cs b/src/PSRule/Runtime/Binding/TargetBinderBuilder.cs new file mode 100644 index 0000000000..03ae67170f --- /dev/null +++ b/src/PSRule/Runtime/Binding/TargetBinderBuilder.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using PSRule.Configuration; + +namespace PSRule.Runtime.Binding; + +#nullable enable + +/// +/// Builds a TargetBinder. +/// +internal sealed class TargetBinderBuilder +{ + private readonly List _BindingContext; + private readonly HashSet? _TypeFilter; + private readonly BindTargetMethod _BindTargetName; + private readonly BindTargetMethod _BindTargetType; + private readonly BindTargetMethod _BindField; + + public TargetBinderBuilder(BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField, string[]? typeFilter) + { + _BindTargetName = bindTargetName; + _BindTargetType = bindTargetType; + _BindField = bindField; + _BindingContext = []; + if (typeFilter != null && typeFilter.Length > 0) + _TypeFilter = new HashSet(typeFilter, StringComparer.OrdinalIgnoreCase); + } + + /// + /// Build a TargetBinder. + /// + public ITargetBinder Build(BindingOption? bindingOption) + { + return new TargetBinder(new TargetBindingContext(bindingOption, _BindTargetName, _BindTargetType, _BindField, _TypeFilter)); + } +} + +#nullable restore diff --git a/src/PSRule/Runtime/Binding/TargetBindingContext.cs b/src/PSRule/Runtime/Binding/TargetBindingContext.cs new file mode 100644 index 0000000000..03997fffc8 --- /dev/null +++ b/src/PSRule/Runtime/Binding/TargetBindingContext.cs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections; +using PSRule.Configuration; +using PSRule.Pipeline; + +namespace PSRule.Runtime.Binding; + +#nullable enable + +internal sealed class TargetBindingContext : ITargetBindingContext +{ + private readonly bool _PreferTargetInfo; + private readonly bool _IgnoreCase; + private readonly bool _UseQualifiedName; + private readonly FieldMap? _Field; + private readonly string[]? _TargetName; + private readonly string[]? _TargetType; + private readonly string _NameSeparator; + private readonly BindTargetMethod _BindTargetName; + private readonly BindTargetMethod _BindTargetType; + private readonly BindTargetMethod _BindField; + private readonly HashSet? _TypeFilter; + + public TargetBindingContext(BindingOption? bindingOption, BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField, HashSet? typeFilter) + { + _PreferTargetInfo = bindingOption?.PreferTargetInfo ?? BindingOption.Default.PreferTargetInfo!.Value; + _IgnoreCase = bindingOption?.IgnoreCase ?? BindingOption.Default.IgnoreCase!.Value; + _UseQualifiedName = bindingOption?.UseQualifiedName ?? BindingOption.Default.UseQualifiedName!.Value; + _Field = bindingOption?.Field; + _TargetName = bindingOption?.TargetName; + _TargetType = bindingOption?.TargetType; + _NameSeparator = bindingOption?.NameSeparator ?? BindingOption.Default.NameSeparator; + _BindTargetName = bindTargetName; + _BindTargetType = bindTargetType; + _BindField = bindField; + _TypeFilter = typeFilter; + } + + public ITargetBindingResult Bind(TargetObject o) + { + var targetNamePath = "."; + var targetName = _PreferTargetInfo && o.TargetName != null ? o.TargetName : _BindTargetName(_TargetName, !_IgnoreCase, _PreferTargetInfo, o.Value, out targetNamePath); + var targetTypePath = "."; + var targetType = _PreferTargetInfo && o.TargetType != null ? o.TargetType : _BindTargetType(_TargetType, !_IgnoreCase, _PreferTargetInfo, o.Value, out targetTypePath); + var shouldFilter = !(_TypeFilter == null || _TypeFilter.Contains(targetType)); + + // Bind custom fields + var field = BindField(_BindField, [_Field], !_IgnoreCase, o.Value); + return Bind(targetName, targetNamePath, targetType, targetTypePath, field); + } + + public ITargetBindingResult Bind(object o) + { + var targetName = _BindTargetName(_TargetName, !_IgnoreCase, _PreferTargetInfo, o, out var targetNamePath); + var targetType = _BindTargetType(_TargetType, !_IgnoreCase, _PreferTargetInfo, o, out var targetTypePath); + var shouldFilter = !(_TypeFilter == null || _TypeFilter.Contains(targetType)); + + // Bind custom fields + var field = BindField(_BindField, [_Field], !_IgnoreCase, o); + return Bind(targetName, targetNamePath, targetType, targetTypePath, field); + } + + private TargetBindingResult Bind(string targetName, string targetNamePath, string targetType, string targetTypePath, Hashtable? field) + { + var shouldFilter = !(_TypeFilter == null || _TypeFilter.Contains(targetType)); + + // Use qualified name + if (_UseQualifiedName) + targetName = string.Concat(targetType, _NameSeparator, targetName); + + return new TargetBindingResult + ( + targetName: targetName, + targetNamePath: targetNamePath, + targetType: targetType, + targetTypePath: targetTypePath, + shouldFilter: shouldFilter, + field: field + ); + } + + /// + /// Bind additional fields. + /// + private static ImmutableHashtable? BindField(BindTargetMethod bindField, FieldMap?[] map, bool caseSensitive, object o) + { + if (map == null || map.Length == 0) + return null; + + var hashtable = new ImmutableHashtable(); + for (var i = 0; i < map.Length; i++) + { + if (map[i] == null || map[i]!.Count == 0) + continue; + + foreach (var field in map[i]!) + { + if (hashtable.ContainsKey(field.Key)) + continue; + + hashtable.Add(field.Key, bindField(field.Value, caseSensitive, false, o, out _)); + } + } + hashtable.Protect(); + return hashtable; + } +} + +#nullable restore diff --git a/src/PSRule/Runtime/Binding/TargetBindingResult.cs b/src/PSRule/Runtime/Binding/TargetBindingResult.cs new file mode 100644 index 0000000000..15da29b052 --- /dev/null +++ b/src/PSRule/Runtime/Binding/TargetBindingResult.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections; + +namespace PSRule.Runtime.Binding; + +#nullable enable + +internal sealed class TargetBindingResult : ITargetBindingResult +{ + public TargetBindingResult(string targetName, string targetNamePath, string targetType, string targetTypePath, bool shouldFilter, Hashtable? field) + { + TargetName = targetName; + TargetNamePath = targetNamePath; + TargetType = targetType; + TargetTypePath = targetTypePath; + ShouldFilter = shouldFilter; + Field = field; + } + + /// + public string TargetName { get; } + + /// + public string TargetNamePath { get; } + + /// + public string TargetType { get; } + + /// + public string TargetTypePath { get; } + + /// + public bool ShouldFilter { get; } + + /// + public Hashtable? Field { get; } +} + +#nullable restore diff --git a/src/PSRule/Runtime/ILanguageScope.cs b/src/PSRule/Runtime/ILanguageScope.cs index fed742553c..8ad2e13b83 100644 --- a/src/PSRule/Runtime/ILanguageScope.cs +++ b/src/PSRule/Runtime/ILanguageScope.cs @@ -1,14 +1,17 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using PSRule.Configuration; using PSRule.Definitions; using PSRule.Pipeline; +using PSRule.Runtime.Binding; namespace PSRule.Runtime; +#nullable enable + /// /// A named scope for language elements. +/// Any elements in a language scope are not visible to language elements in another scope. /// internal interface ILanguageScope : IDisposable { @@ -17,7 +20,7 @@ internal interface ILanguageScope : IDisposable /// string Name { get; } - BindingOption Binding { get; } + StringComparer GetBindingComparer(); /// /// Get an ordered culture preference list which will be tries for finding help. @@ -29,14 +32,17 @@ internal interface ILanguageScope : IDisposable /// /// Try to get a specific configuration value by name. /// - bool TryConfigurationValue(string key, out object value); + bool TryConfigurationValue(string key, out object? value); + /// + /// Add a filter to the language scope. + /// void WithFilter(IResourceFilter resourceFilter); /// /// Get a filter for a specific resource kind. /// - IResourceFilter GetFilter(ResourceKind kind); + IResourceFilter? GetFilter(ResourceKind kind); /// /// Add a service to the scope. @@ -46,11 +52,21 @@ internal interface ILanguageScope : IDisposable /// /// Get a previously added service. /// - object GetService(string name); + object? GetService(string name); - bool TryGetType(object o, out string type, out string path); + ITargetBindingResult? Bind(TargetObject targetObject); - bool TryGetName(object o, out string name, out string path); + ITargetBindingResult? Bind(object targetObject); - bool TryGetScope(object o, out string[] scope); + /// + /// Try to bind the type of the object. + /// + bool TryGetType(object o, out string? type, out string? path); + + /// + /// Try to bind the name of the object. + /// + bool TryGetName(object o, out string? name, out string? path); } + +#nullable restore diff --git a/src/PSRule/Runtime/LanguageScope.cs b/src/PSRule/Runtime/LanguageScope.cs index 7a5bde8596..0ac67cd9e5 100644 --- a/src/PSRule/Runtime/LanguageScope.cs +++ b/src/PSRule/Runtime/LanguageScope.cs @@ -5,61 +5,65 @@ using PSRule.Configuration; using PSRule.Definitions; using PSRule.Pipeline; +using PSRule.Runtime.Binding; namespace PSRule.Runtime; +#nullable enable + [DebuggerDisplay("{Name}")] internal sealed class LanguageScope : ILanguageScope { - private readonly RunspaceContext _Context; - private IDictionary _Configuration; + private IDictionary? _Configuration; private readonly Dictionary _Service; private readonly Dictionary _Filter; + private ITargetBinder? _TargetBinder; + private StringComparer? _BindingComparer; private bool _Disposed; - public LanguageScope(RunspaceContext context, string name) + public LanguageScope(string name) { - _Context = context; Name = ResourceHelper.NormalizeScope(name); - //_Configuration = new Dictionary(); - _Filter = new Dictionary(); - _Service = new Dictionary(); + _Filter = []; + _Service = []; } /// public string Name { [DebuggerStepThrough] get; } - /// - public BindingOption Binding { [DebuggerStepThrough] get; [DebuggerStepThrough] private set; } - /// public string[] Culture { [DebuggerStepThrough] get; [DebuggerStepThrough] private set; } + public StringComparer GetBindingComparer() => _BindingComparer ?? StringComparer.OrdinalIgnoreCase; + /// public void Configure(Dictionary configuration) { + _Configuration ??= new Dictionary(); _Configuration.AddUnique(configuration); } /// public void Configure(OptionContext context) { - if (context == null) - throw new ArgumentNullException(nameof(context)); + if (context == null) throw new ArgumentNullException(nameof(context)); _Configuration = context.Configuration; WithFilter(context.RuleFilter); WithFilter(context.ConventionFilter); - Binding = context.Binding; + _BindingComparer = context.Binding.GetComparer(); Culture = context.Output.Culture; + + var builder = new TargetBinderBuilder(context.BindTargetName, context.BindTargetType, context.BindField, context.InputTargetType); + _TargetBinder = builder.Build(context.Binding); } /// - public bool TryConfigurationValue(string key, out object value) + public bool TryConfigurationValue(string key, out object? value) { value = null; - return !string.IsNullOrEmpty(key) && _Configuration.TryGetValue(key, out value); + return !string.IsNullOrEmpty(key) && _Configuration != null && _Configuration.TryGetValue(key, out value); } /// @@ -69,7 +73,7 @@ public void WithFilter(IResourceFilter resourceFilter) } /// - public IResourceFilter GetFilter(ResourceKind kind) + public IResourceFilter? GetFilter(ResourceKind kind) { return _Filter.TryGetValue(kind, out var filter) ? filter : null; } @@ -84,61 +88,48 @@ public void AddService(string name, object service) } /// - public object GetService(string name) + public object? GetService(string name) { return _Service.TryGetValue(name, out var service) ? service : null; } - public bool TryGetType(object o, out string type, out string path) + public ITargetBindingResult? Bind(TargetObject targetObject) { - if (_Context != null && _Context.TargetObject.Value == o) - { - var binding = _Context.TargetBinder.Result(Name); - type = binding.TargetType; - path = binding.TargetTypePath; - return true; - } - else if (_Context != null) - { - var binding = _Context.TargetBinder.Using(Name).Bind(o); - type = binding.TargetType; - path = binding.TargetTypePath; - return true; - } - type = null; - path = null; - return false; + return _TargetBinder?.Bind(targetObject); } - public bool TryGetName(object o, out string name, out string path) + public ITargetBindingResult? Bind(object targetObject) { - if (_Context != null && _Context.TargetObject.Value == o) - { - var binding = _Context.TargetBinder.Result(Name); - name = binding.TargetName; - path = binding.TargetNamePath; - return true; - } - else if (_Context != null) + return _TargetBinder?.Bind(targetObject); + } + + /// + public bool TryGetType(object o, out string? type, out string? path) + { + if (_TargetBinder != null) { - var binding = _Context.TargetBinder.Using(Name).Bind(o); - name = binding.TargetName; - path = binding.TargetNamePath; + var result = _TargetBinder.Bind(o); + type = result.TargetType; + path = result.TargetTypePath; return true; } - name = null; + type = null; path = null; return false; } - public bool TryGetScope(object o, out string[] scope) + /// + public bool TryGetName(object o, out string? name, out string? path) { - if (_Context != null && _Context.TargetObject.Value == o) + if (_TargetBinder != null) { - scope = _Context.TargetObject.Scope; + var result = _TargetBinder.Bind(o); + name = result.TargetName; + path = result.TargetNamePath; return true; } - scope = null; + name = null; + path = null; return false; } @@ -170,3 +161,5 @@ public void Dispose() GC.SuppressFinalize(this); } } + +#nullable restore diff --git a/src/PSRule/Runtime/LanguageScopeSet.cs b/src/PSRule/Runtime/LanguageScopeSet.cs index de64c44ee2..89457d3fbc 100644 --- a/src/PSRule/Runtime/LanguageScopeSet.cs +++ b/src/PSRule/Runtime/LanguageScopeSet.cs @@ -10,15 +10,13 @@ namespace PSRule.Runtime; /// internal sealed class LanguageScopeSet : IDisposable { - private readonly RunspaceContext _Context; private readonly Dictionary _Scopes; private ILanguageScope _Current; private bool _Disposed; - public LanguageScopeSet(RunspaceContext context) + public LanguageScopeSet() { - _Context = context; _Scopes = new Dictionary(StringComparer.OrdinalIgnoreCase); Import(null, out _Current); } @@ -93,7 +91,7 @@ internal bool Import(string name, out ILanguageScope scope) if (_Scopes.TryGetValue(GetScopeName(name), out scope)) return false; - scope = new LanguageScope(_Context, name); + scope = new LanguageScope(name); Add(scope); return true; } diff --git a/src/PSRule/Runtime/NameToken.cs b/src/PSRule/Runtime/NameToken.cs index 2e06cc32c0..35acdadc19 100644 --- a/src/PSRule/Runtime/NameToken.cs +++ b/src/PSRule/Runtime/NameToken.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Diagnostics; diff --git a/src/PSRule/Runtime/ObjectPath/IPathExpressionContext.cs b/src/PSRule/Runtime/ObjectPath/IPathExpressionContext.cs index fc754f726a..eb96efb2bf 100644 --- a/src/PSRule/Runtime/ObjectPath/IPathExpressionContext.cs +++ b/src/PSRule/Runtime/ObjectPath/IPathExpressionContext.cs @@ -1,10 +1,10 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Runtime.ObjectPath; /// -/// A context ojbect used using evaluating a path expression. +/// A context object used using evaluating a path expression. /// internal interface IPathExpressionContext { diff --git a/src/PSRule/Runtime/ObjectPath/PathExpressionContext.cs b/src/PSRule/Runtime/ObjectPath/PathExpressionContext.cs index dead15f9df..5a09235e06 100644 --- a/src/PSRule/Runtime/ObjectPath/PathExpressionContext.cs +++ b/src/PSRule/Runtime/ObjectPath/PathExpressionContext.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule.Runtime.ObjectPath; diff --git a/src/PSRule/Runtime/RunspaceContext.cs b/src/PSRule/Runtime/RunspaceContext.cs index 5db8f4641b..b5da036be2 100644 --- a/src/PSRule/Runtime/RunspaceContext.cs +++ b/src/PSRule/Runtime/RunspaceContext.cs @@ -10,6 +10,7 @@ using PSRule.Pipeline; using PSRule.Resources; using PSRule.Rules; +using PSRule.Runtime.Binding; using static PSRule.Pipeline.PipelineContext; namespace PSRule.Runtime; @@ -17,9 +18,9 @@ namespace PSRule.Runtime; #nullable enable /// -/// A context for a PSRule runspace. +/// A context applicable to rule execution. /// -internal sealed class RunspaceContext : IDisposable, ILogger +internal sealed class RunspaceContext : IDisposable, ILogger, IScriptResourceDiscoveryContext { private const string SOURCE_OUTCOME_FAIL = "Rule.Outcome.Fail"; private const string SOURCE_OUTCOME_PASS = "Rule.Outcome.Pass"; @@ -30,7 +31,6 @@ internal sealed class RunspaceContext : IDisposable, ILogger internal static RunspaceContext? CurrentThread; internal readonly PipelineContext Pipeline; - internal readonly IPipelineWriter Writer; // Fields exposed to engine internal RuleRecord? RuleRecord; @@ -92,18 +92,18 @@ internal RunspaceContext(PipelineContext pipeline, IPipelineWriter writer) _RuleTimer = new Stopwatch(); _Reason = []; _Conventions = []; - _LanguageScopes = new LanguageScopeSet(this); + _LanguageScopes = new LanguageScopeSet(); _Scope = new Stack(); } internal bool HadErrors => _RuleErrors > 0; + public IPipelineWriter Writer { get; } + internal IEnumerable? Output { get; private set; } internal TargetObject? TargetObject { get; private set; } - internal ITargetBinder? TargetBinder { get; private set; } - internal SourceScope? Source { get; private set; } internal ILanguageScope LanguageScope @@ -127,12 +127,12 @@ internal bool IsScope(RunspaceScope scope) return scope.HasFlag(current); } - internal void PushScope(RunspaceScope scope) + public void PushScope(RunspaceScope scope) { _Scope.Push(scope); } - internal void PopScope(RunspaceScope scope) + public void PopScope(RunspaceScope scope) { var current = _Scope.Peek(); if (current != scope) @@ -258,14 +258,6 @@ public void ErrorInvalidRuleResult() )); } - public void VerboseRuleDiscovery(string path) - { - if (Writer == null || !Writer.ShouldWriteVerbose() || string.IsNullOrEmpty(path)) - return; - - Writer.WriteVerbose($"[PSRule][D] -- Discovering rules in: {path}"); - } - public void VerboseFoundResource(string name, string moduleName, string scriptName) { if (Writer == null || !Writer.ShouldWriteVerbose()) @@ -320,30 +312,12 @@ public void VerboseConditionResult(int pass, int count, RuleOutcome outcome) Writer.WriteVerbose(string.Concat(GetLogPrefix(), " -- [", pass, "/", count, "] [", outcome, "]")); } - public void WriteError(ErrorRecord record) - { - if (Writer == null || !Writer.ShouldWriteError()) - return; - - Writer.WriteError(errorRecord: record); - } - - public void WriteError(ParseError error) + public ExecutionOption GetExecutionOption() { - if (Writer == null || !Writer.ShouldWriteError()) - return; - - var record = new ErrorRecord - ( - exception: new Pipeline.ParseException(message: error.Message, errorId: error.ErrorId), - errorId: error.ErrorId, - errorCategory: ErrorCategory.InvalidOperation, - targetObject: null - ); - Writer.WriteError(errorRecord: record); + return Pipeline.Option.Execution; } - internal PowerShell GetPowerShell() + public PowerShell GetPowerShell() { var result = PowerShell.Create(); result.Runspace = Pipeline.GetRunspace(); @@ -544,26 +518,24 @@ private string GetLogPrefix() return _LogPrefix ?? string.Empty; } - internal void EnterLanguageScope(ISourceFile file) + public void EnterLanguageScope(ISourceFile file) { // TODO: Look at scope caching, and a scope stack. - if (Source != null && Source.File == file) - return; - if (!file.Exists()) throw new FileNotFoundException(PSRuleResources.ScriptNotFound, file.Path); _LanguageScopes.UseScope(file.Module); - // Change scope - //Pipeline.Baseline.ResetScope(moduleName: source.Module); + if (TargetObject != null && LanguageScope != null) + Binding = LanguageScope.Bind(TargetObject); + Source = new SourceScope(file); } - internal void ExitLanguageScope(ISourceFile file) + public void ExitLanguageScope(ISourceFile file) { - // Look at scope poping and validation. + // Look at scope popping and validation. Source = null; } @@ -575,7 +547,6 @@ internal void EnterTargetObject(TargetObject targetObject) { _ObjectNumber++; TargetObject = targetObject; - TargetBinder?.Bind(TargetObject); if (Pipeline.ContentCache.Count > 0) Pipeline.ContentCache.Clear(); @@ -587,6 +558,7 @@ public void ExitTargetObject() { RunConventionProcess(); TargetObject = null; + Binding = null; } public bool TrySelector(string name) @@ -613,7 +585,7 @@ public bool TrySelector(ResourceId id) /// public RuleRecord EnterRuleBlock(RuleBlock ruleBlock) { - Binding = TargetBinder?.Result(ruleBlock.Info.ModuleName); + EnterLanguageScope(ruleBlock.Source); _RuleErrors = 0; RuleBlock = ruleBlock; @@ -641,7 +613,7 @@ public RuleRecord EnterRuleBlock(RuleBlock ruleBlock) /// /// Exit the rule block scope. /// - public void ExitRuleBlock() + public void ExitRuleBlock(RuleBlock ruleBlock) { // Stop rule execution time _RuleTimer.Stop(); @@ -663,6 +635,8 @@ public void ExitRuleBlock() RuleBlock = null; _RuleErrors = 0; _Reason.Clear(); + + ExitLanguageScope(ruleBlock.Source); } internal void Import(IConvention resource) @@ -673,16 +647,16 @@ internal void Import(IConvention resource) internal void AddService(string id, object service) { ResourceHelper.ParseIdString(LanguageScope.Name, id, out var scopeName, out var name); - if (!StringComparer.OrdinalIgnoreCase.Equals(LanguageScope.Name, scopeName)) + if (!StringComparer.OrdinalIgnoreCase.Equals(LanguageScope.Name, scopeName) || string.IsNullOrEmpty(name)) return; - LanguageScope.AddService(name, service); + LanguageScope.AddService(name!, service); } internal object? GetService(string id) { ResourceHelper.ParseIdString(LanguageScope.Name, id, out var scopeName, out var name); - return !_LanguageScopes.TryScope(scopeName, out var scope) ? null : scope.GetService(name); + return !_LanguageScopes.TryScope(scopeName, out var scope) || string.IsNullOrEmpty(name) ? null : scope.GetService(name!); } private void RunConventionInitialize() @@ -743,7 +717,7 @@ internal void WriteReason(ResultReason reason) public void Init(Source[] source) { InitLanguageScopes(source); - var resources = Host.HostHelper.ImportResource(source, this).OfType(); + var resources = Host.HostHelper.GetMetaResources(source, this); // Process module configurations first foreach (var resource in resources.Where(r => r.Kind == ResourceKind.ModuleConfig).ToArray()) @@ -786,21 +760,6 @@ public void Begin() { Pipeline.Begin(this); - var builder = new TargetBinderBuilder( - Pipeline.BindTargetName, - Pipeline.BindTargetType, - Pipeline.BindField, - Pipeline.Option.Input.TargetType); - - HashSet? _TypeFilter = null; - if (Pipeline.Option.Input.TargetType != null && Pipeline.Option.Input.TargetType.Length > 0) - _TypeFilter = new HashSet(Pipeline.Option.Input.TargetType, StringComparer.OrdinalIgnoreCase); - - foreach (var languageScope in _LanguageScopes.Get()) - { - builder.With(new TargetBinder.TargetBindingContext(languageScope.Name, languageScope.Binding, Pipeline.BindTargetName, Pipeline.BindTargetType, Pipeline.BindField, _TypeFilter)); - } - TargetBinder = builder.Build(); RunConventionInitialize(); } @@ -810,6 +769,20 @@ public void End(IEnumerable output) RunConventionEnd(); } + /// + /// Try to bind the scope of the object. + /// + public bool TryGetScope(object o, out string[]? scope) + { + if (TargetObject != null && TargetObject.Value == o) + { + scope = TargetObject.Scope; + return true; + } + scope = null; + return false; + } + public string? GetLocalizedPath(string file, out string? culture) { culture = null; diff --git a/src/PSRule/packages.lock.json b/src/PSRule/packages.lock.json index 2e8dd6c3af..5d6c23ef19 100644 --- a/src/PSRule/packages.lock.json +++ b/src/PSRule/packages.lock.json @@ -22,44 +22,44 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", "System.Threading.Tasks.Extensions": "4.5.4" } }, "Microsoft.Extensions.Hosting": { "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "ItYHpdqVp5/oFLT5QqbopnkKlyFG9EW/9nhM6/yfObeKt6Su0wkBio6AizgRHGNwhJuAtlE5VIjow5JOTrip6w==", + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "bP9EEkHBEfjgYiG8nUaXqMk/ujwJrffOkNPP7onpRMO8R+OUSESSP4xHkCAXgYZ1COP2Q9lXlU5gkMFh20gRuw==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "8.0.0", "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.2", "Microsoft.Extensions.Configuration.CommandLine": "8.0.0", "Microsoft.Extensions.Configuration.EnvironmentVariables": "8.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "8.0.0", - "Microsoft.Extensions.Configuration.Json": "8.0.0", - "Microsoft.Extensions.Configuration.UserSecrets": "8.0.0", - "Microsoft.Extensions.DependencyInjection": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Diagnostics": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.Configuration.Json": "8.0.1", + "Microsoft.Extensions.Configuration.UserSecrets": "8.0.1", + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics": "8.0.1", "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", "Microsoft.Extensions.FileProviders.Physical": "8.0.0", - "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Configuration": "8.0.0", - "Microsoft.Extensions.Logging.Console": "8.0.0", - "Microsoft.Extensions.Logging.Debug": "8.0.0", - "Microsoft.Extensions.Logging.EventLog": "8.0.0", - "Microsoft.Extensions.Logging.EventSource": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Configuration": "8.0.1", + "Microsoft.Extensions.Logging.Console": "8.0.1", + "Microsoft.Extensions.Logging.Debug": "8.0.1", + "Microsoft.Extensions.Logging.EventLog": "8.0.1", + "Microsoft.Extensions.Logging.EventSource": "8.0.1", + "Microsoft.Extensions.Options": "8.0.2", "System.Threading.Tasks.Extensions": "4.5.4" } }, @@ -134,9 +134,9 @@ }, "System.Text.Json": { "type": "Direct", - "requested": "[8.0.4, )", - "resolved": "8.0.4", - "contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==", + "requested": "[8.0.5, )", + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "8.0.0", "System.Buffers": "4.5.1", @@ -178,8 +178,8 @@ }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "mBMoXLsr5s1y2zOHWmKsE9veDcx8h1x/c3rz4baEdQKTeDcmQAPNbB54Pi/lhFO3K431eEq6PFbMgLaa6PHFfA==", + "resolved": "8.0.2", + "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==", "dependencies": { "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } @@ -204,8 +204,8 @@ }, "Microsoft.Extensions.Configuration.FileExtensions": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "McP+Lz/EKwvtCv48z0YImw+L1gi1gy5rHhNaNIY2CrjloV+XY8gydT8DjMR6zWeL13AFK+DioVpppwAuO1Gi1w==", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", "dependencies": { "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", @@ -216,31 +216,31 @@ }, "Microsoft.Extensions.Configuration.Json": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "C2wqUoh9OmRL1akaCcKSTmRU8z0kckfImG7zLNI8uyi47Lp+zd5LWAD17waPQEqCz3ioWOCrFUo+JJuoeZLOBw==", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", "dependencies": { "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", - "System.Text.Json": "8.0.0" + "System.Text.Json": "8.0.5" } }, "Microsoft.Extensions.Configuration.UserSecrets": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "ihDHu2dJYQird9pl2CbdwuNDfvCZdOS0S7SPlNfhPt0B81UTT+yyZKz2pimFZGUp3AfuBRnqUCxB2SjsZKHVUw==", + "resolved": "8.0.1", + "contentHash": "7tYqdPPpAK+3jO9d5LTuCK2VxrEdf85Ol4trUr6ds4jclBecadWZ/RyPCbNjfbN5iGTfUnD/h65TOQuqQv2c+A==", "dependencies": { "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.Configuration.Json": "8.0.0", + "Microsoft.Extensions.Configuration.Json": "8.0.1", "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", "Microsoft.Extensions.FileProviders.Physical": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==", + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "8.0.0", "System.Threading.Tasks.Extensions": "4.5.4" @@ -248,23 +248,23 @@ }, "Microsoft.Extensions.Diagnostics": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3PZp/YSkIXrF7QK7PfC1bkyRYwqOHpWFad8Qx+4wkuumAeXo1NHaxpS9LboNA9OvNSAu+QOVlXbMyoY+pHSqcw==", + "resolved": "8.0.1", + "contentHash": "doVPCUUCY7c6LhBsEfiy3W1bvS7Mi6LkfQMS8nlC22jZWNxBv8VO8bdfeyvpYFst6Kxqk7HBC6lytmEoBssvSQ==", "dependencies": { "Microsoft.Extensions.Configuration": "8.0.0", - "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" } }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JHYCQG7HmugNYUhOl368g+NMxYE/N/AiclCYRNlgCY9eVyiBkOHMwK4x60RYMxv9EL3+rmj1mqHvdCiPpC+D4Q==", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", "System.Buffers": "4.5.1", - "System.Diagnostics.DiagnosticSource": "8.0.0", + "System.Diagnostics.DiagnosticSource": "8.0.1", "System.Memory": "4.5.5" } }, @@ -293,112 +293,113 @@ }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "AG7HWwVRdCHlaA++1oKDxLsXIBxmDpMPb3VoyOoAghEWnkUvEAdYQUwnV4jJbAaa/nMYNiEh5ByoLauZBEiovg==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", "System.Threading.Tasks.Extensions": "4.5.4" } }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", + "resolved": "8.0.1", + "contentHash": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", - "System.Diagnostics.DiagnosticSource": "8.0.0" + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "System.Diagnostics.DiagnosticSource": "8.0.1" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", + "resolved": "8.0.2", + "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", "System.Buffers": "4.5.1", + "System.Diagnostics.DiagnosticSource": "8.0.1", "System.Memory": "4.5.5" } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "ixXXV0G/12g6MXK65TLngYN9V5hQQRuV+fZi882WIoVJT7h5JvoYoxTEwCgdqwLjSneqh1O+66gM8sMr9z/rsQ==", + "resolved": "8.0.1", + "contentHash": "QWwTrsgOnJMmn+XUslm8D2H1n3PkP/u/v52FODtyBc/k4W9r3i2vcXXeeX/upnzllJYRRbrzVzT0OclfNJtBJA==", "dependencies": { "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.Configuration.Binder": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" } }, "Microsoft.Extensions.Logging.Console": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "e+48o7DztoYog+PY430lPxrM4mm3PbA6qucvQtUDDwVo4MO+ejMw7YGc/o2rnxbxj4isPxdfKFzTxvXMwAz83A==", + "resolved": "8.0.1", + "contentHash": "uzcg/5U2eLyn5LIKlERkdSxw6VPC1yydnOSQiRRWGBGN3kphq3iL4emORzrojScDmxRhv49gp5BI8U3Dz7y4iA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Configuration": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Configuration": "8.0.1", + "Microsoft.Extensions.Options": "8.0.2", "System.Buffers": "4.5.1", - "System.Text.Json": "8.0.0" + "System.Text.Json": "8.0.5" } }, "Microsoft.Extensions.Logging.Debug": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "dt0x21qBdudHLW/bjMJpkixv858RRr8eSomgVbU8qljOyfrfDGi1JQvpF9w8S7ziRPtRKisuWaOwFxJM82GxeA==", + "resolved": "8.0.1", + "contentHash": "B8hqNuYudC2RB+L/DI33uO4rf5by41fZVdcVL2oZj0UyoAZqnwTwYHp1KafoH4nkl1/23piNeybFFASaV2HkFg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging.EventLog": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3X9D3sl7EmOu7vQp5MJrmIJBl5XSdOhZPYXUeFfYa6Nnm9+tok8x3t3IVPLhm7UJtPOU61ohFchw8rNm9tIYOQ==", + "resolved": "8.0.1", + "contentHash": "ZD1m4GXoxcZeDJIq8qePKj+QAWeQNO/OG8skvrOG8RQfxLp9MAKRoliTc27xanoNUzeqvX5HhS/I7c0BvwAYUg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", - "System.Diagnostics.EventLog": "8.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "System.Diagnostics.EventLog": "8.0.1" } }, "Microsoft.Extensions.Logging.EventSource": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "oKcPMrw+luz2DUAKhwFXrmFikZWnyc8l2RKoQwqU3KIZZjcfoJE0zRHAnqATfhRZhtcbjl/QkiY2Xjxp0xu+6w==", + "resolved": "8.0.1", + "contentHash": "YMXMAla6B6sEf/SnfZYTty633Ool3AH7KOw2LOaaEqwSo2piK4f7HMtzyc3CNiipDnq1fsUSuG5Oc7ZzpVy8WQ==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Primitives": "8.0.0", "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Json": "8.0.0" + "System.Text.Json": "8.0.5" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", "Microsoft.Extensions.Primitives": "8.0.0", @@ -601,8 +602,8 @@ }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", + "resolved": "8.0.1", + "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "6.0.0" @@ -610,8 +611,8 @@ }, "System.Diagnostics.EventLog": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A==", + "resolved": "8.0.1", + "contentHash": "n1ZP7NM2Gkn/MgD8+eOT5MulMj6wfeQMNS2Pizvq5GHCZfjlFMXV2irQlQmJhwA2VABC57M0auudO89Iu2uRLg==", "dependencies": { "System.Security.Principal.Windows": "5.0.0" } diff --git a/tests/PSRule.CommandLine.Tests/GlobalUsings.cs b/tests/PSRule.CommandLine.Tests/GlobalUsings.cs index 8c07c6cf4c..5f34b52a07 100644 --- a/tests/PSRule.CommandLine.Tests/GlobalUsings.cs +++ b/tests/PSRule.CommandLine.Tests/GlobalUsings.cs @@ -1,4 +1,3 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -global using Xunit; diff --git a/tests/PSRule.CommandLine.Tests/PSRule.CommandLine.Tests.csproj b/tests/PSRule.CommandLine.Tests/PSRule.CommandLine.Tests.csproj index a19212d69b..5c5609377b 100644 --- a/tests/PSRule.CommandLine.Tests/PSRule.CommandLine.Tests.csproj +++ b/tests/PSRule.CommandLine.Tests/PSRule.CommandLine.Tests.csproj @@ -13,12 +13,16 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/tests/PSRule.Tests/AssertTests.cs b/tests/PSRule.Tests/AssertTests.cs index 16b9abd9a2..2f277ec555 100644 --- a/tests/PSRule.Tests/AssertTests.cs +++ b/tests/PSRule.Tests/AssertTests.cs @@ -104,16 +104,75 @@ public void Assertion() Assert.False(assert.AllOf().Result); } + /// + /// Tests for . + /// + [Fact] + public void AnyOf() + { + SetContext(); + var assert = GetAssertionHelper(); + var actual1 = assert.Create(false, "Test reason 1"); + var actual2 = assert.Create(true, "Test reason 2"); + var actual3 = assert.Fail("Fail reason"); + + // Pass cases with no reasons. + var result = assert.AnyOf(actual2, actual3); + Assert.True(result.Result); + Assert.Empty(result.GetReason()); + Assert.True(assert.AnyOf(actual2).Result); + Assert.True(assert.AnyOf(actual3, actual2, actual1).Result); + + // Check reasons are returned. + result = assert.AnyOf(actual1, actual3); + Assert.False(result.Result); + Assert.Equal("Test reason 1 Fail reason", result.ToString()); + Assert.Equal(["Test reason 1", "Fail reason"], result.GetReason()); + + // Empty fail case. + Assert.False(assert.AnyOf().Result); + } + + /// + /// Tests for . + /// + [Fact] + public void AllOf() + { + SetContext(); + var assert = GetAssertionHelper(); + var actual1 = assert.Create(false, "Test reason 1"); + var actual2 = assert.Create(true, "Test reason 2"); + var actual3 = assert.Fail("Fail reason"); + + // Fail cases. + var result = assert.AllOf(actual2, actual3); + Assert.False(result.Result); + Assert.Equal("Fail reason", result.ToString()); + Assert.Equal("Fail reason", result.GetReason()[0]); + + result = assert.AllOf(actual1, actual2, actual3); + Assert.Equal("Test reason 1 Fail reason", result.ToString()); + Assert.Equal(["Test reason 1", "Fail reason"], result.GetReason()); + + // Pass cases. + Assert.True(assert.AllOf(actual2, actual2).Result); + Assert.True(assert.AllOf(actual2).Result); + + // Empty fail case. + Assert.False(assert.AllOf().Result); + } + [Fact] public void WithinRollupBlock() { SetContext(); var assert = GetAssertionHelper(); - var actual1 = RuleConditionHelper.Create(new object[] { PSObject.AsPSObject(assert.Create(true, "Test reason")), PSObject.AsPSObject(assert.Create(false, "Test reason")) }); + var actual1 = RuleConditionHelper.Create([PSObject.AsPSObject(assert.Create(true, "Test reason")), PSObject.AsPSObject(assert.Create(false, "Test reason"))]); Assert.True(actual1.AnyOf()); Assert.False(actual1.AllOf()); - var actual2 = RuleConditionHelper.Create(new object[] { assert.Create(true, "Test reason"), assert.Create(false, "Test reason") }); + var actual2 = RuleConditionHelper.Create([assert.Create(true, "Test reason"), assert.Create(false, "Test reason")]); Assert.True(actual2.AnyOf()); Assert.False(actual2.AllOf()); } diff --git a/tests/PSRule.Tests/Baseline.Rule.jsonc b/tests/PSRule.Tests/Baseline.Rule.jsonc index fcd78fe933..af3a3401cb 100644 --- a/tests/PSRule.Tests/Baseline.Rule.jsonc +++ b/tests/PSRule.Tests/Baseline.Rule.jsonc @@ -11,23 +11,6 @@ }, "spec": { // Additional comment - "binding": { - "field": { - "kind": [ - "kind" - ], - "uniqueIdentifer": [ - "Id", - "AlternateName" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "kind" - ] - }, "rule": { "include": [ // Additional comment @@ -56,14 +39,6 @@ "name": "TestBaseline2" }, "spec": { - "binding": { - "targetName": [ - "metadata.name" - ], - "targetType": [ - "kind" - ] - }, "rule": { "include": [ "" diff --git a/tests/PSRule.Tests/Baseline.Rule.yaml b/tests/PSRule.Tests/Baseline.Rule.yaml index bff3791677..a31b10d658 100644 --- a/tests/PSRule.Tests/Baseline.Rule.yaml +++ b/tests/PSRule.Tests/Baseline.Rule.yaml @@ -9,17 +9,6 @@ metadata: key: value spec: # Additional comment - binding: - field: - kind: - - kind - uniqueIdentifer: - - Id - - AlternateName - targetName: - - AlternateName - targetType: - - kind rule: include: # Additional comment @@ -38,11 +27,6 @@ kind: Baseline metadata: name: TestBaseline2 spec: - binding: - targetName: - - metadata.name - targetType: - - kind rule: include: - '' diff --git a/tests/PSRule.Tests/BaselineTests.cs b/tests/PSRule.Tests/BaselineTests.cs index 989b739db7..87bbfd2ae2 100644 --- a/tests/PSRule.Tests/BaselineTests.cs +++ b/tests/PSRule.Tests/BaselineTests.cs @@ -59,10 +59,10 @@ public void ReadBaselineYaml() // TestBaseline6 Assert.Equal("TestBaseline6", baseline[5].Name); var labels = baseline[5].Spec.Rule.Labels; - Assert.True(labels.Contains("framework.v1/control", new string[] { "*" })); - Assert.True(labels.Contains("framework.v1/control", new string[] { "c-1" })); - Assert.False(labels.Contains("framework.v1/control", new string[] { "c-3" })); - Assert.False(labels.Contains("framework.v3/control", new string[] { "*" })); + Assert.True(labels.Contains("framework.v1/control", ["*"])); + Assert.True(labels.Contains("framework.v1/control", ["c-1"])); + Assert.False(labels.Contains("framework.v1/control", ["c-3"])); + Assert.False(labels.Contains("framework.v3/control", ["*"])); } [Fact] @@ -100,10 +100,10 @@ public void ReadBaselineJson() // TestBaseline6 Assert.Equal("TestBaseline6", baseline[5].Name); var labels = baseline[5].Spec.Rule.Labels; - Assert.True(labels.Contains("framework.v1/control", new string[] { "*" })); - Assert.True(labels.Contains("framework.v1/control", new string[] { "c-1" })); - Assert.False(labels.Contains("framework.v1/control", new string[] { "c-3" })); - Assert.False(labels.Contains("framework.v3/control", new string[] { "*" })); + Assert.True(labels.Contains("framework.v1/control", ["*"])); + Assert.True(labels.Contains("framework.v1/control", ["c-1"])); + Assert.False(labels.Contains("framework.v1/control", ["c-3"])); + Assert.False(labels.Contains("framework.v3/control", ["*"])); } [Theory] @@ -130,7 +130,7 @@ public void FilterBaseline(string path) var baseline = GetBaselines(GetSource(path)); Assert.NotNull(baseline); - var filter = new BaselineFilter(new string[] { "TestBaseline5" }); + var filter = new BaselineFilter(["TestBaseline5"]); var actual = baseline.FirstOrDefault(b => filter.Match(b)); Assert.Equal("TestBaseline5", actual.Name); @@ -149,11 +149,6 @@ public void BaselineAsYaml() Assert.Equal("Baseline", actual[0]["kind"]); Assert.Equal("TestBaseline1", actual[0]["metadata"]["name"]); Assert.NotNull(actual[0]["spec"]); - Assert.Equal("kind", actual[0]["spec"]["binding"]["field"]["kind"][0]); - Assert.Equal("Id", actual[0]["spec"]["binding"]["field"]["uniqueIdentifer"][0]); - Assert.Equal("AlternateName", actual[0]["spec"]["binding"]["field"]["uniqueIdentifer"][1]); - Assert.Equal("AlternateName", actual[0]["spec"]["binding"]["targetName"][0]); - Assert.Equal("kind", actual[0]["spec"]["binding"]["targetType"][0]); Assert.Equal("WithBaseline", actual[0]["spec"]["rule"]["include"][0]); Assert.Equal("value1", actual[0]["spec"]["configuration"]["key1"]); Assert.Equal("abc", actual[0]["spec"]["configuration"]["key2"][0]["value1"]); @@ -172,11 +167,6 @@ public void BaselineAsJson() Assert.Equal("Baseline", actual[0]["kind"]); Assert.Equal("TestBaseline1", actual[0]["metadata"]["name"]); Assert.NotNull(actual[0]["spec"]); - Assert.Equal("kind", actual[0]["spec"]["binding"]["field"]["kind"][0]); - Assert.Equal("AlternateName", actual[0]["spec"]["binding"]["field"]["uniqueIdentifer"][0]); - Assert.Equal("Id", actual[0]["spec"]["binding"]["field"]["uniqueIdentifer"][1]); - Assert.Equal("AlternateName", actual[0]["spec"]["binding"]["targetName"][0]); - Assert.Equal("kind", actual[0]["spec"]["binding"]["targetType"][0]); Assert.Equal("WithBaseline", actual[0]["spec"]["rule"]["include"][0]); Assert.Equal("value1", actual[0]["spec"]["configuration"]["key1"]); Assert.Equal("abc", actual[0]["spec"]["configuration"]["key2"][0]["value1"]); @@ -211,10 +201,10 @@ private static Source[] GetSourceInModule(string path, string moduleName, Source var file = new SourceFile(GetSourcePath(path), moduleName, type, null); var source = new Source( module: new Source.ModuleInfo(AppDomain.CurrentDomain.BaseDirectory, moduleName, "1.0.0", null, "4de0fd26-6aae-401f-a943-b49f082f141e", "Microsoft", null), - file: new SourceFile[] { file }, + file: [file], dependency: false ); - return new Source[] { source }; + return [source]; } private static string GetSourcePath(string fileName) diff --git a/tests/PSRule.Tests/Binding/TargetBinderTests.cs b/tests/PSRule.Tests/Binding/TargetBinderTests.cs new file mode 100644 index 0000000000..1a0e00bc19 --- /dev/null +++ b/tests/PSRule.Tests/Binding/TargetBinderTests.cs @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Management.Automation; +using Newtonsoft.Json.Linq; +using PSRule.Configuration; +using PSRule.Pipeline; +using PSRule.Runtime.Binding; +using static Xunit.Assert; + +namespace PSRule.Binding; + +#nullable enable + +public sealed class TargetBinderTests +{ + private static readonly BindingOption Module1_Binding = new() + { + TargetName = ["name"], + TargetType = ["type"] + }; + + private static readonly BindingOption Module2_Binding = new() + { + TargetName = ["AlternativeName"], + TargetType = ["type"] + }; + + private static readonly BindingOption Module3_Binding = new() + { + TargetName = ["name"], + TargetType = ["type"], + PreferTargetInfo = true + }; + + + [Fact] + public void BindTargetObject() + { + var targetObject = GetTargetObject(); + + var m0 = GetBinder(null).Bind(targetObject); + Equal("Name1", m0.TargetName); + Equal("System.Management.Automation.PSCustomObject", m0.TargetType); + + var m1 = GetBinder(Module1_Binding).Bind(targetObject); + Equal("Name1", m1.TargetName); + Equal("Type1", m1.TargetType); + + var m2 = GetBinder(Module2_Binding).Bind(targetObject); + Equal("Name2", m2.TargetName); + Equal("Type1", m2.TargetType); + + // With specified type + targetObject = GetTargetObject(targetType: "ManualType"); + + m1 = GetBinder(Module1_Binding).Bind(targetObject); + Equal("Name1", m1.TargetName); + Equal("Type1", m1.TargetType); + + var m3 = GetBinder(Module3_Binding).Bind(targetObject); + Equal("Name1", m3.TargetName); + Equal("ManualType", m3.TargetType); + } + + [Fact] + public void BindJObject() + { + var targetObject = new TargetObject(PSObject.AsPSObject(JToken.Parse("{ \"name\": \"Name1\", \"type\": \"Type1\", \"AlternativeName\": \"Name2\", \"AlternativeType\": \"Type2\" }"))); + + var m0 = GetBinder(null).Bind(targetObject); + Equal("Name1", m0.TargetName); + Equal("System.Management.Automation.PSCustomObject", m0.TargetType); + + var m1 = GetBinder(Module1_Binding).Bind(targetObject); + Equal("Name1", m1.TargetName); + Equal("Type1", m1.TargetType); + + var m2 = GetBinder(Module2_Binding).Bind(targetObject); + Equal("Name2", m2.TargetName); + Equal("Type1", m2.TargetType); + } + + #region Helper methods + + private static TargetObject GetTargetObject(string? targetType = null) + { + var pso = new PSObject(); + pso.Properties.Add(new PSNoteProperty("name", "Name1")); + pso.Properties.Add(new PSNoteProperty("type", "Type1")); + pso.Properties.Add(new PSNoteProperty("AlternativeName", "Name2")); + pso.Properties.Add(new PSNoteProperty("AlternativeType", "Type2")); + return new TargetObject(pso, targetType: targetType); + } + + private static ITargetBinder GetBinder(BindingOption? bindingOption) + { + var builder = new TargetBinderBuilder(PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, null); + return builder.Build(bindingOption); + } + + #endregion Helper methods +} + +#nullable restore diff --git a/tests/PSRule.Tests/DateVersionTests.cs b/tests/PSRule.Tests/DateVersionTests.cs index e23165eb15..3a250cfffa 100644 --- a/tests/PSRule.Tests/DateVersionTests.cs +++ b/tests/PSRule.Tests/DateVersionTests.cs @@ -87,67 +87,67 @@ public void Constraint() Assert.True(DateVersion.TryParseConstraint("@prerelease <=2022-03-01-0", out var actual21)); // Version1 - 2015-10-01 - Assert.True(actual1.Equals(version1)); - Assert.False(actual2.Equals(version1)); - Assert.True(actual3.Equals(version1)); - Assert.True(actual4.Equals(version1)); - Assert.False(actual5.Equals(version1)); - Assert.True(actual7.Equals(version1)); - Assert.True(actual8.Equals(version1)); - Assert.True(actual9.Equals(version1)); - Assert.True(actual10.Equals(version1)); - Assert.True(actual11.Equals(version1)); - Assert.True(actual12.Equals(version1)); - Assert.False(actual14.Equals(version1)); - Assert.True(actual15.Equals(version1)); - Assert.True(actual16.Equals(version1)); - Assert.True(actual17.Equals(version1)); - Assert.True(actual18.Equals(version1)); - Assert.True(actual19.Equals(version1)); - Assert.True(actual20.Equals(version1)); - Assert.True(actual21.Equals(version1)); + Assert.True(actual1.Accepts(version1)); + Assert.False(actual2.Accepts(version1)); + Assert.True(actual3.Accepts(version1)); + Assert.True(actual4.Accepts(version1)); + Assert.False(actual5.Accepts(version1)); + Assert.True(actual7.Accepts(version1)); + Assert.True(actual8.Accepts(version1)); + Assert.True(actual9.Accepts(version1)); + Assert.True(actual10.Accepts(version1)); + Assert.True(actual11.Accepts(version1)); + Assert.True(actual12.Accepts(version1)); + Assert.False(actual14.Accepts(version1)); + Assert.True(actual15.Accepts(version1)); + Assert.True(actual16.Accepts(version1)); + Assert.True(actual17.Accepts(version1)); + Assert.True(actual18.Accepts(version1)); + Assert.True(actual19.Accepts(version1)); + Assert.True(actual20.Accepts(version1)); + Assert.True(actual21.Accepts(version1)); // Version3 - 2015-10-01-alpha.9 - Assert.False(actual1.Equals(version2)); - Assert.False(actual2.Equals(version2)); - Assert.True(actual3.Equals(version2)); - Assert.True(actual4.Equals(version2)); - Assert.True(actual5.Equals(version2)); - Assert.False(actual7.Equals(version2)); - Assert.False(actual8.Equals(version2)); - Assert.False(actual9.Equals(version2)); - Assert.True(actual10.Equals(version2)); - Assert.False(actual11.Equals(version2)); - Assert.False(actual12.Equals(version2)); - Assert.False(actual14.Equals(version2)); - Assert.False(actual15.Equals(version2)); - Assert.False(actual16.Equals(version2)); - Assert.False(actual17.Equals(version2)); - Assert.True(actual18.Equals(version2)); - Assert.True(actual19.Equals(version2)); - Assert.True(actual20.Equals(version2)); - Assert.True(actual21.Equals(version2)); + Assert.False(actual1.Accepts(version2)); + Assert.False(actual2.Accepts(version2)); + Assert.True(actual3.Accepts(version2)); + Assert.True(actual4.Accepts(version2)); + Assert.True(actual5.Accepts(version2)); + Assert.False(actual7.Accepts(version2)); + Assert.False(actual8.Accepts(version2)); + Assert.False(actual9.Accepts(version2)); + Assert.True(actual10.Accepts(version2)); + Assert.False(actual11.Accepts(version2)); + Assert.False(actual12.Accepts(version2)); + Assert.False(actual14.Accepts(version2)); + Assert.False(actual15.Accepts(version2)); + Assert.False(actual16.Accepts(version2)); + Assert.False(actual17.Accepts(version2)); + Assert.True(actual18.Accepts(version2)); + Assert.True(actual19.Accepts(version2)); + Assert.True(actual20.Accepts(version2)); + Assert.True(actual21.Accepts(version2)); // Version4 - 2022-03-01 - Assert.False(actual1.Equals(version3)); - Assert.False(actual2.Equals(version3)); - Assert.True(actual3.Equals(version3)); - Assert.True(actual4.Equals(version3)); - Assert.False(actual5.Equals(version3)); - Assert.False(actual7.Equals(version3)); - Assert.False(actual8.Equals(version3)); - Assert.True(actual9.Equals(version3)); - Assert.True(actual10.Equals(version3)); - Assert.False(actual11.Equals(version3)); - Assert.False(actual12.Equals(version3)); - Assert.False(actual14.Equals(version3)); - Assert.True(actual15.Equals(version3)); - Assert.True(actual16.Equals(version3)); - Assert.True(actual17.Equals(version3)); - Assert.True(actual18.Equals(version3)); - Assert.False(actual19.Equals(version3)); - Assert.True(actual20.Equals(version3)); - Assert.False(actual21.Equals(version3)); + Assert.False(actual1.Accepts(version3)); + Assert.False(actual2.Accepts(version3)); + Assert.True(actual3.Accepts(version3)); + Assert.True(actual4.Accepts(version3)); + Assert.False(actual5.Accepts(version3)); + Assert.False(actual7.Accepts(version3)); + Assert.False(actual8.Accepts(version3)); + Assert.True(actual9.Accepts(version3)); + Assert.True(actual10.Accepts(version3)); + Assert.False(actual11.Accepts(version3)); + Assert.False(actual12.Accepts(version3)); + Assert.False(actual14.Accepts(version3)); + Assert.True(actual15.Accepts(version3)); + Assert.True(actual16.Accepts(version3)); + Assert.True(actual17.Accepts(version3)); + Assert.True(actual18.Accepts(version3)); + Assert.False(actual19.Accepts(version3)); + Assert.True(actual20.Accepts(version3)); + Assert.False(actual21.Accepts(version3)); } /// diff --git a/tests/PSRule.Tests/FromFileBaseline.Rule.ps1 b/tests/PSRule.Tests/FromFileBaseline.Rule.ps1 index 3514c3bb1e..71431b823f 100644 --- a/tests/PSRule.Tests/FromFileBaseline.Rule.ps1 +++ b/tests/PSRule.Tests/FromFileBaseline.Rule.ps1 @@ -7,9 +7,7 @@ # Synopsis: Test for baseline Rule 'WithBaseline' -Tag @{ category = 'group2'; severity = 'high' } { - $PSRule.TargetName -eq 'TestObject1' - $PSRule.TargetType -eq 'TestObjectType' - $PSRule.Field.kind -eq 'TestObjectType' + $Configuration.GetStringOrDefault('key1', $Null) -eq 'value1' } -Labels @{ 'framework.v1/control' = @('c-2') } # Synopsis: Test for baseline diff --git a/tests/PSRule.Tests/LanguageVisitorTests.cs b/tests/PSRule.Tests/LanguageVisitorTests.cs index 1928a5b3e6..751a8707bd 100644 --- a/tests/PSRule.Tests/LanguageVisitorTests.cs +++ b/tests/PSRule.Tests/LanguageVisitorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Management.Automation; diff --git a/tests/PSRule.Tests/MockLanguageScope.cs b/tests/PSRule.Tests/MockLanguageScope.cs index aa775a020d..05c18d73c4 100644 --- a/tests/PSRule.Tests/MockLanguageScope.cs +++ b/tests/PSRule.Tests/MockLanguageScope.cs @@ -7,6 +7,7 @@ using PSRule.Definitions.Rules; using PSRule.Pipeline; using PSRule.Runtime; +using PSRule.Runtime.Binding; namespace PSRule; @@ -21,15 +22,25 @@ public MockLanguageScope(string name) public string Name { get; } - public BindingOption Binding => throw new System.NotImplementedException(); + public BindingOption Binding => throw new NotImplementedException(); - public string[] Culture => throw new System.NotImplementedException(); + public string[] Culture => throw new NotImplementedException(); public void AddService(string name, object service) { } + public ITargetBindingResult Bind(TargetObject targetObject) + { + throw new NotImplementedException(); + } + + public ITargetBindingResult Bind(object targetObject) + { + throw new NotImplementedException(); + } + public void Configure(OptionContext context) { throw new NotImplementedException(); @@ -40,34 +51,34 @@ public void Dispose() } + public StringComparer GetBindingComparer() + { + throw new NotImplementedException(); + } + public IResourceFilter GetFilter(ResourceKind kind) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } public object GetService(string name) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } public bool TryConfigurationValue(string key, out object value) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } public bool TryGetName(object o, out string name, out string path) { - throw new System.NotImplementedException(); - } - - public bool TryGetScope(object o, out string[] scope) - { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } public bool TryGetType(object o, out string type, out string path) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } public void WithFilter(IResourceFilter resourceFilter) diff --git a/tests/PSRule.Tests/ObjectPathTests.cs b/tests/PSRule.Tests/ObjectPathTests.cs index b549c02215..a15ff3bca3 100644 --- a/tests/PSRule.Tests/ObjectPathTests.cs +++ b/tests/PSRule.Tests/ObjectPathTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; diff --git a/tests/PSRule.Tests/OptionContextTests.cs b/tests/PSRule.Tests/OptionContextTests.cs index 7104d4810f..b5082ea804 100644 --- a/tests/PSRule.Tests/OptionContextTests.cs +++ b/tests/PSRule.Tests/OptionContextTests.cs @@ -24,7 +24,7 @@ public void Build() var builder = new OptionContextBuilder(GetOption()); // Check empty scope - var testScope = new LanguageScope(null, "Empty"); + var testScope = new LanguageScope("Empty"); testScope.Configure(builder.Build(testScope.Name)); Assert.Equal(new string[] { "en-ZZ" }, testScope.Culture); } @@ -35,29 +35,29 @@ public void Order() // Create option context var builder = new OptionContextBuilder(GetOption()); - var localScope = new LanguageScope(null, null); + var localScope = new LanguageScope(null); localScope.Configure(builder.Build(null)); var ruleFilter = localScope.GetFilter(ResourceKind.Rule) as RuleFilter; Assert.NotNull(ruleFilter); Assert.True(ruleFilter.IncludeLocal); - // With explict baseline + // With explicit baseline builder = new OptionContextBuilder(GetOption()); - builder.Baseline(ScopeType.Explicit, "BaselineExplicit", null, GetBaseline(ruleInclude: new[] { "abc" }), false); + builder.Baseline(ScopeType.Explicit, "BaselineExplicit", null, GetBaseline(ruleInclude: ["abc"]), false); localScope.Configure(builder.Build(localScope.Name)); ruleFilter = localScope.GetFilter(ResourceKind.Rule) as RuleFilter; Assert.NotNull(ruleFilter); Assert.False(ruleFilter.IncludeLocal); // With include from parameters - builder = new OptionContextBuilder(GetOption(), include: new string[] { "abc" }); + builder = new OptionContextBuilder(GetOption(), include: ["abc"]); localScope.Configure(builder.Build(localScope.Name)); ruleFilter = localScope.GetFilter(ResourceKind.Rule) as RuleFilter; Assert.NotNull(ruleFilter); Assert.False(ruleFilter.IncludeLocal); - builder = new OptionContextBuilder(GetOption(ruleInclude: new[] { "abc" })); + builder = new OptionContextBuilder(GetOption(ruleInclude: ["abc"])); localScope.Configure(builder.Build(localScope.Name)); ruleFilter = localScope.GetFilter(ResourceKind.Rule) as RuleFilter; Assert.NotNull(ruleFilter); @@ -89,7 +89,7 @@ public void Merge_multiple_options_from_file() Assert.Equal("value6", option6); // With module default baseline - builder.Baseline(ScopeType.Module, "BaselineDefault", "Module1", GetBaseline(targetType: new[] { "defaultType" }, ruleInclude: new[] { "defaultRule" }), false); + builder.Baseline(ScopeType.Module, "BaselineDefault", "Module1", GetBaseline(targetType: ["defaultType"], ruleInclude: ["defaultRule"]), false); context = builder.Build(null); Assert.Equal(new[] { "ResourceName", "AlternateName" }, context.Binding.TargetName); @@ -102,18 +102,18 @@ public void Merge_multiple_options_from_file() Assert.Equal(new[] { "ResourceType", "kind" }, context.Binding.TargetType); Assert.Equal(new[] { "rule1", "rule2" }, context.Rule.Include); - // With explict baseline + // With explicit baseline builder.Baseline(ScopeType.Explicit, "BaselineExplicit", "Module1", GetBaseline(), false); context = builder.Build(null); Assert.Equal(new[] { "ResourceName", "AlternateName" }, context.Binding.TargetName); - Assert.Equal(new[] { "typeName" }, context.Binding.TargetType); + Assert.Equal(new[] { "ResourceType", "kind" }, context.Binding.TargetType); Assert.Equal(new[] { "rule1" }, context.Rule.Include); context = builder.Build("Module1"); Assert.Equal(new[] { "ResourceName", "AlternateName" }, context.Binding.TargetName); - Assert.Equal(new[] { "typeName" }, context.Binding.TargetType); + Assert.Equal(new[] { "ResourceType", "kind" }, context.Binding.TargetType); Assert.Equal(new[] { "rule1" }, context.Rule.Include); } @@ -124,7 +124,7 @@ private static PSRuleOption GetOption(string[] culture = null, string[] ruleIncl var option = new PSRuleOption(); // Specify a culture otherwise it varies within CI. - option.Output.Culture = culture ?? new string[] { "en-ZZ" }; + option.Output.Culture = culture ?? ["en-ZZ"]; option.Rule.Include = ruleInclude; @@ -141,14 +141,9 @@ private static PSRuleOption GetOptionFromFile(string file = "PSRule.Tests.yml") private static BaselineSpec GetBaseline(string[] targetType = null, string[] ruleInclude = null) { - targetType ??= new[] { "typeName" }; - ruleInclude ??= new[] { "rule1" }; + ruleInclude ??= ["rule1"]; return new BaselineSpec { - Binding = new BindingOption - { - TargetType = targetType - }, Rule = new RuleOption { Include = ruleInclude diff --git a/tests/PSRule.Tests/Output.Baseline.0.jsonc b/tests/PSRule.Tests/Output.Baseline.0.jsonc index a4a68ac5b0..ed4e03994a 100644 --- a/tests/PSRule.Tests/Output.Baseline.0.jsonc +++ b/tests/PSRule.Tests/Output.Baseline.0.jsonc @@ -1,9 +1,9 @@ [{ // Synopsis: This is an example baseline -"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Module4"},"spec":{"binding":{"field":{"kind":["Id"],"uniqueIdentifer":["AlternateName","Id"]},"targetName":["AlternateName"],"targetType":["Kind"]},"configuration":{"ruleConfig1":"Test"},"rule":{"include":["M4.Rule1"]}}},{ +"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Module4"},"spec":{"configuration":{"ruleConfig1":"Test"},"rule":{"include":["M4.Rule1"]}}},{ // Synopsis: This is an example baseline -"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Module4a"},"spec":{"binding":{"field":{"kind":["Id"],"uniqueIdentifer":["AlternateName","Id"]},"targetName":["AlternateName"],"targetType":["Kind"]},"configuration":{"ruleConfig1":"Test2"},"rule":{"include":["M4.Rule1"]}}},{ +"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Module4a"},"spec":{"configuration":{"ruleConfig1":"Test2"},"rule":{"include":["M4.Rule1"]}}},{ // Synopsis: This is an example baseline -"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Baseline2"},"spec":{"binding":{"targetName":["AlternateName"],"targetType":["Kind"]},"configuration":{"ruleConfig2":"Test3"},"rule":{"include":["M4.Rule1"]}}},{ +"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Baseline2"},"spec":{"configuration":{"ruleConfig2":"Test3"},"rule":{"include":["M4.Rule1"]}}},{ // Synopsis: This is an example baseline -"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Baseline3"},"spec":{"binding":{"field":{"alternativeType":["AlternateName"]},"targetName":["AlternateName"],"targetType":["Kind"]},"configuration":{"ruleConfig2":"Test3"},"rule":{"include":["M4.Rule1"]}}}] \ No newline at end of file +"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Baseline3"},"spec":{"configuration":{"ruleConfig2":"Test3"},"rule":{"include":["M4.Rule1"]}}}] \ No newline at end of file diff --git a/tests/PSRule.Tests/Output.Baseline.2.jsonc b/tests/PSRule.Tests/Output.Baseline.2.jsonc index 41450f3c1f..8d499d6867 100644 --- a/tests/PSRule.Tests/Output.Baseline.2.jsonc +++ b/tests/PSRule.Tests/Output.Baseline.2.jsonc @@ -7,23 +7,6 @@ "name": "Module4" }, "spec": { - "binding": { - "field": { - "kind": [ - "Id" - ], - "uniqueIdentifer": [ - "AlternateName", - "Id" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig1": "Test" }, @@ -42,23 +25,6 @@ "name": "Module4a" }, "spec": { - "binding": { - "field": { - "kind": [ - "Id" - ], - "uniqueIdentifer": [ - "AlternateName", - "Id" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig1": "Test2" }, @@ -77,14 +43,6 @@ "name": "Baseline2" }, "spec": { - "binding": { - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig2": "Test3" }, @@ -103,19 +61,6 @@ "name": "Baseline3" }, "spec": { - "binding": { - "field": { - "alternativeType": [ - "AlternateName" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig2": "Test3" }, diff --git a/tests/PSRule.Tests/Output.Baseline.4.jsonc b/tests/PSRule.Tests/Output.Baseline.4.jsonc index 0207ba3335..d55ff94152 100644 --- a/tests/PSRule.Tests/Output.Baseline.4.jsonc +++ b/tests/PSRule.Tests/Output.Baseline.4.jsonc @@ -7,23 +7,6 @@ "name": "Module4" }, "spec": { - "binding": { - "field": { - "kind": [ - "Id" - ], - "uniqueIdentifer": [ - "AlternateName", - "Id" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig1": "Test" }, @@ -42,23 +25,6 @@ "name": "Module4a" }, "spec": { - "binding": { - "field": { - "kind": [ - "Id" - ], - "uniqueIdentifer": [ - "AlternateName", - "Id" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig1": "Test2" }, @@ -77,14 +43,6 @@ "name": "Baseline2" }, "spec": { - "binding": { - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig2": "Test3" }, @@ -103,19 +61,6 @@ "name": "Baseline3" }, "spec": { - "binding": { - "field": { - "alternativeType": [ - "AlternateName" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig2": "Test3" }, diff --git a/tests/PSRule.Tests/Output.Baseline.yaml b/tests/PSRule.Tests/Output.Baseline.yaml index 83ffdc7b77..0274a0383a 100644 --- a/tests/PSRule.Tests/Output.Baseline.yaml +++ b/tests/PSRule.Tests/Output.Baseline.yaml @@ -4,17 +4,6 @@ kind: Baseline metadata: name: Module4 spec: - binding: - field: - kind: - - Id - uniqueIdentifer: - - AlternateName - - Id - targetName: - - AlternateName - targetType: - - Kind configuration: ruleConfig1: Test rule: @@ -27,17 +16,6 @@ kind: Baseline metadata: name: Module4a spec: - binding: - field: - kind: - - Id - uniqueIdentifer: - - AlternateName - - Id - targetName: - - AlternateName - targetType: - - Kind configuration: ruleConfig1: Test2 rule: @@ -50,11 +28,6 @@ kind: Baseline metadata: name: Baseline2 spec: - binding: - targetName: - - AlternateName - targetType: - - Kind configuration: ruleConfig2: Test3 rule: @@ -67,14 +40,6 @@ kind: Baseline metadata: name: Baseline3 spec: - binding: - field: - alternativeType: - - AlternateName - targetName: - - AlternateName - targetType: - - Kind configuration: ruleConfig2: Test3 rule: diff --git a/tests/PSRule.Tests/PSRule.Baseline.Tests.ps1 b/tests/PSRule.Tests/PSRule.Baseline.Tests.ps1 index 83a4d7d342..ffeb6984c3 100644 --- a/tests/PSRule.Tests/PSRule.Baseline.Tests.ps1 +++ b/tests/PSRule.Tests/PSRule.Baseline.Tests.ps1 @@ -47,17 +47,6 @@ kind: Baseline metadata: name: Module4 spec: - binding: - field: - kind: - - Id - uniqueIdentifer: - - AlternateName - - Id - targetName: - - AlternateName - targetType: - - Kind configuration: ruleConfig1: Test rule: @@ -71,17 +60,6 @@ kind: Baseline metadata: name: Module4a spec: - binding: - field: - kind: - - Id - uniqueIdentifer: - - AlternateName - - Id - targetName: - - AlternateName - targetType: - - Kind configuration: ruleConfig1: Test2 rule: @@ -95,11 +73,6 @@ kind: Baseline metadata: name: Baseline2 spec: - binding: - targetName: - - AlternateName - targetType: - - Kind configuration: ruleConfig2: Test3 rule: @@ -113,14 +86,6 @@ kind: Baseline metadata: name: Baseline3 spec: - binding: - field: - alternativeType: - - AlternateName - targetName: - - AlternateName - targetType: - - Kind configuration: ruleConfig2: Test3 rule: @@ -139,23 +104,6 @@ spec: "name": "Module4" }, "spec": { - "binding": { - "field": { - "kind": [ - "Id" - ], - "uniqueIdentifer": [ - "AlternateName", - "Id" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig1": "Test" }, @@ -178,14 +126,6 @@ spec: "name": "Baseline2" }, "spec": { - "binding": { - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig2": "Test3" }, @@ -208,19 +148,6 @@ spec: "name": "Baseline3" }, "spec": { - "binding": { - "field": { - "alternativeType": [ - "AlternateName" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig2": "Test3" }, @@ -244,23 +171,6 @@ $baselineTwoSpaceJsonTestCases = @( "name": "Module4" }, "spec": { - "binding": { - "field": { - "kind": [ - "Id" - ], - "uniqueIdentifer": [ - "AlternateName", - "Id" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig1": "Test" }, @@ -283,14 +193,6 @@ $baselineTwoSpaceJsonTestCases = @( "name": "Baseline2" }, "spec": { - "binding": { - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig2": "Test3" }, @@ -313,19 +215,6 @@ $baselineTwoSpaceJsonTestCases = @( "name": "Baseline3" }, "spec": { - "binding": { - "field": { - "alternativeType": [ - "AlternateName" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig2": "Test3" }, @@ -342,17 +231,17 @@ $baselineZeroSpaceJsonTestCases = @( @{Baseline = 'Module4'; ExpectedJson = @" [{ // Synopsis: This is an example baseline -"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Module4"},"spec":{"binding":{"field":{"kind":["Id"],"uniqueIdentifer":["AlternateName","Id"]},"targetName":["AlternateName"],"targetType":["Kind"]},"configuration":{"ruleConfig1":"Test"},"rule":{"include":["M4.Rule1"]}}}] +"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Module4"},"spec":{"configuration":{"ruleConfig1":"Test"},"rule":{"include":["M4.Rule1"]}}}] "@} @{Baseline = 'Baseline2'; ExpectedJson = @" [{ // Synopsis: This is an example baseline -"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Baseline2"},"spec":{"binding":{"targetName":["AlternateName"],"targetType":["Kind"]},"configuration":{"ruleConfig2":"Test3"},"rule":{"include":["M4.Rule1"]}}}] +"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Baseline2"},"spec":{"configuration":{"ruleConfig2":"Test3"},"rule":{"include":["M4.Rule1"]}}}] "@} @{Baseline = 'Baseline3'; ExpectedJson = @" [{ // Synopsis: This is an example baseline -"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Baseline3"},"spec":{"binding":{"field":{"alternativeType":["AlternateName"]},"targetName":["AlternateName"],"targetType":["Kind"]},"configuration":{"ruleConfig2":"Test3"},"rule":{"include":["M4.Rule1"]}}}] +"apiVersion":"github.com/microsoft/PSRule/v1","kind":"Baseline","metadata":{"name":"Baseline3"},"spec":{"configuration":{"ruleConfig2":"Test3"},"rule":{"include":["M4.Rule1"]}}}] "@} ) #endregion @@ -698,89 +587,98 @@ Describe 'Baseline' -Tag 'Baseline' { } It 'With -Baseline' { - $result = @($testObject | Invoke-PSRule -Path $ruleFilePath,$baselineFilePath -Baseline 'TestBaseline1'); + $options = @{ + 'Binding.TargetName' = 'AlternateName' + 'Binding.TargetType' = 'kind' + } + + $result = @($testObject | Invoke-PSRule -Option $options -Path $ruleFilePath,$baselineFilePath -Baseline 'TestBaseline1'); $result | Should -Not -BeNullOrEmpty; $result.Length | Should -Be 1; $result[0].RuleName | Should -Be 'WithBaseline'; $result[0].Outcome | Should -Be 'Pass'; $result[0].TargetName | Should -Be 'TestObject1'; $result[0].TargetType | Should -Be 'TestObjectType'; - $result[0].Field.kind | Should -Be 'TestObjectType'; # Use a baseline group by name - $result = @($testObject | Invoke-PSRule -Path $ruleFilePath,$baselineFilePath -Baseline '@latest' -Option @{ + $options = @{ + 'Binding.TargetName' = 'AlternateName' + 'Binding.TargetType' = 'kind' 'Baseline.Group' = @{ latest = 'TestBaseline1' } - }); + } + $result = @($testObject | Invoke-PSRule -Option $options -Path $ruleFilePath,$baselineFilePath -Baseline '@latest'); $result | Should -Not -BeNullOrEmpty; $result.Length | Should -Be 1; $result[0].RuleName | Should -Be 'WithBaseline'; $result[0].Outcome | Should -Be 'Pass'; $result[0].TargetName | Should -Be 'TestObject1'; $result[0].TargetType | Should -Be 'TestObjectType'; - $result[0].Field.kind | Should -Be 'TestObjectType'; } It 'With -Module' { $Null = Import-Module (Join-Path $here -ChildPath 'TestModule4') -Force; + $options = @{ + 'Binding.TargetName' = 'AlternateName' + 'Binding.TargetType' = 'kind' + } + # Module - $result = @($testObject | Invoke-PSRule -Module TestModule4); + $result = @($testObject | Invoke-PSRule -Option $options -Module TestModule4); $result | Should -Not -BeNullOrEmpty; $result.Length | Should -Be 1; $result[0].RuleName | Should -Be 'M4.Rule1'; $result[0].Outcome | Should -Be 'Pass'; # Module + Workspace - $option = @{ + $options = @{ 'Configuration.ruleConfig1' = 'Test2' 'Rule.Include' = @('M4.Rule1', 'M4.Rule2') + 'Binding.TargetName' = 'AlternateName' + 'Binding.TargetType' = 'kind' 'Binding.Field' = @{ kind = 'Kind' } } - $result = @($testObject | Invoke-PSRule -Module TestModule4 -Option $option); + $result = @($testObject | Invoke-PSRule -Module TestModule4 -Option $options); $result | Should -Not -BeNullOrEmpty; $result.Length | Should -Be 2; $result[0].RuleName | Should -Be 'M4.Rule1'; $result[0].Outcome | Should -Be 'Fail'; $result[0].TargetName | Should -Be 'TestObject1'; $result[0].TargetType | Should -Be 'TestObjectType'; - $result[0].Field.kind | Should -Be 'TestObjectType'; - $result[0].Field.uniqueIdentifer | Should -Be '1'; - $result[0].Field.AlternativeType | Should -Be 'TestObjectType'; $result[1].RuleName | Should -Be 'M4.Rule2'; $result[1].Outcome | Should -Be 'Pass'; $result[1].TargetName | Should -Be 'TestObject1'; $result[1].TargetType | Should -Be 'TestObjectType'; - $result[1].Field.AlternativeType | Should -Be 'TestObjectType'; # Module + Workspace + Parameter - $option = @{ + $options = @{ 'Configuration.ruleConfig1' = 'Test2' 'Rule.Include' = @('M4.Rule1', 'M4.Rule2') + 'Binding.TargetName' = 'AlternateName' + 'Binding.TargetType' = 'kind' } - $result = @($testObject | Invoke-PSRule -Module TestModule4 -Option $option -Name 'M4.Rule2'); + $result = @($testObject | Invoke-PSRule -Module TestModule4 -Option $options -Name 'M4.Rule2'); $result | Should -Not -BeNullOrEmpty; $result.Length | Should -Be 1; $result[0].RuleName | Should -Be 'M4.Rule2'; $result[0].Outcome | Should -Be 'Pass'; - $result[0].Field.kind | Should -Be '1'; - $result[0].Field.uniqueIdentifer | Should -Be '1'; # Module + Workspace + Parameter + Explicit - $option = @{ + $options = @{ 'Configuration.ruleConfig1' = 'Test2' 'Rule.Include' = @('M4.Rule1', 'M4.Rule2') + 'Binding.TargetName' = 'AlternateName' + 'Binding.TargetType' = 'kind' } - $result = @($testObject | Invoke-PSRule -Module TestModule4 -Option $option -Name 'M4.Rule2', 'M4.Rule3' -Baseline 'Baseline2'); + $result = @($testObject | Invoke-PSRule -Module TestModule4 -Option $options -Name 'M4.Rule2', 'M4.Rule3' -Baseline 'Baseline2'); $result | Should -Not -BeNullOrEmpty; $result.Length | Should -Be 2; $result[0].RuleName | Should -Be 'M4.Rule2'; $result[0].Outcome | Should -Be 'Pass'; - $result[0].Field.AlternativeType | Should -Be 'TestObjectType'; $result[1].RuleName | Should -Be 'M4.Rule3'; $result[1].Outcome | Should -Be 'Pass'; - $result[1].Field.AlternativeType | Should -Be 'TestObjectType'; # Module Config + Module + Workspace + Parameter + Explicit $result = @($testObject | Invoke-PSRule -Module TestModule4 -Name 'M4.Rule4' -Baseline 'Baseline3'); @@ -788,7 +686,6 @@ Describe 'Baseline' -Tag 'Baseline' { $result.Length | Should -Be 1; $result[0].RuleName | Should -Be 'M4.Rule4'; $result[0].Outcome | Should -Be 'Pass'; - $result[0].Field.AlternativeType | Should -Be 'TestObject1'; # Explict with default $result = @($testObject | Invoke-PSRule -Module TestModule4 -Path $ruleFilePath -Baseline 'Module4'); @@ -798,6 +695,8 @@ Describe 'Baseline' -Tag 'Baseline' { # Explict with local scope $result = @($testObject | Invoke-PSRule -Module TestModule4 -Path $ruleFilePath -Baseline 'Module4' -Option @{ + 'Binding.TargetName' = 'AlternateName' + 'Binding.TargetType' = 'kind' 'Rule.IncludeLocal' = $True }); $result | Should -Not -BeNullOrEmpty; diff --git a/tests/PSRule.Tests/PSRule.Common.Tests.ps1 b/tests/PSRule.Tests/PSRule.Common.Tests.ps1 index be92f0d5ab..e9e726987e 100644 --- a/tests/PSRule.Tests/PSRule.Common.Tests.ps1 +++ b/tests/PSRule.Tests/PSRule.Common.Tests.ps1 @@ -1030,15 +1030,6 @@ Describe 'Invoke-PSRule' -Tag 'Invoke-PSRule','Common' { $option = @{ 'execution.mode' = 'ConstrainedLanguage' }; { $Null = $testObject | Invoke-PSRule -Path $ruleFilePath -Name 'ConstrainedTest2' -Option $option -ErrorAction Stop } | Should -Throw 'Cannot invoke method. Method invocation is supported only on core types in this language mode.'; { $Null = $testObject | Invoke-PSRule -Path $ruleFilePath -Name 'ConstrainedTest3' -Option $option -ErrorAction Stop } | Should -Throw 'Cannot invoke method. Method invocation is supported only on core types in this language mode.'; - - $bindFn = { - param ($TargetObject) - $Null = [Console]::WriteLine('Should fail'); - return 'BadName'; - } - - $option = New-PSRuleOption -Option @{ 'execution.mode' = 'ConstrainedLanguage' } -BindTargetName $bindFn; - { $Null = $testObject | Invoke-PSRule -Path $ruleFilePath -Name 'ConstrainedTest1' -Option $option -ErrorAction Stop } | Should -Throw 'Exception calling "Invoke" with "3" argument(s): "Binding functions are not supported in this language mode."'; } } @@ -1545,15 +1536,6 @@ Describe 'Test-PSRuleTarget' -Tag 'Test-PSRuleTarget','Common' { $option = @{ 'execution.mode' = 'ConstrainedLanguage' }; { $Null = $testObject | Test-PSRuleTarget -Path $ruleFilePath -Name 'ConstrainedTest2' -Option $option -ErrorAction Stop } | Should -Throw 'Cannot invoke method. Method invocation is supported only on core types in this language mode.'; { $Null = $testObject | Test-PSRuleTarget -Path $ruleFilePath -Name 'ConstrainedTest3' -Option $option -ErrorAction Stop } | Should -Throw 'Cannot invoke method. Method invocation is supported only on core types in this language mode.'; - - $bindFn = { - param ($TargetObject) - $Null = [Console]::WriteLine('Should fail'); - return 'BadName'; - } - - $option = New-PSRuleOption -Option @{ 'execution.mode' = 'ConstrainedLanguage' } -BindTargetName $bindFn; - { $Null = $testObject | Test-PSRuleTarget -Path $ruleFilePath -Name 'ConstrainedTest1' -Option $option -ErrorAction Stop } | Should -Throw 'Exception calling "Test" with "3" argument(s): "Binding functions are not supported in this language mode."'; } } } @@ -1803,15 +1785,6 @@ Describe 'Assert-PSRule' -Tag 'Assert-PSRule','Common' { $option = @{ 'execution.mode' = 'ConstrainedLanguage' }; { $Null = $testObject | Assert-PSRule -Path $ruleFilePath -Name 'ConstrainedTest2' -Option $option -ErrorAction Stop 6>&1 } | Should -Throw 'Cannot invoke method. Method invocation is supported only on core types in this language mode.'; { $Null = $testObject | Assert-PSRule -Path $ruleFilePath -Name 'ConstrainedTest3' -Option $option -ErrorAction Stop 6>&1 } | Should -Throw 'Cannot invoke method. Method invocation is supported only on core types in this language mode.'; - - $bindFn = { - param ($TargetObject) - $Null = [Console]::WriteLine('Should fail'); - return 'BadName'; - } - - $option = New-PSRuleOption -Option @{ 'execution.mode' = 'ConstrainedLanguage' } -BindTargetName $bindFn; - { $Null = $testObject | Assert-PSRule -Path $ruleFilePath -Name 'ConstrainedTest1' -Option $option -ErrorAction Stop 6>&1 } | Should -Throw 'Exception calling "Assert" with "3" argument(s): "Binding functions are not supported in this language mode."'; } } } @@ -2814,12 +2787,6 @@ Describe 'Binding' -Tag Common, Binding { [PSCustomObject]@{ TargetName = 'TargetName' } - [PSCustomObject]@{ - OtherName = 'OtherName' - resourceName = 'ResourceName' - AlternateName = 'AlternateName' - TargetName = 'TargetName' - } [PSCustomObject]@{ Metadata = @{ Name = 'MetadataName' @@ -2827,26 +2794,16 @@ Describe 'Binding' -Tag Common, Binding { } ) - $bindFn = { - param ($TargetObject) - $otherName = $TargetObject.PSObject.Properties['OtherName']; - if ($otherName -eq $Null) { - return $Null - } - return $otherName.Value; - } - - $option = New-PSRuleOption -Option @{ 'Binding.TargetName' = 'ResourceName', 'AlternateName', 'Metadata.Name'; 'Binding.IgnoreCase' = $True } -BindTargetName $bindFn; + $option = New-PSRuleOption -Option @{ 'Binding.TargetName' = 'ResourceName', 'AlternateName', 'Metadata.Name'; 'Binding.IgnoreCase' = $True }; $result = $testObject | Invoke-PSRule -Option $option -Path $ruleFilePath -Name 'FromFile1'; $result | Should -Not -BeNullOrEmpty; - $result.Count | Should -Be 5; + $result.Count | Should -Be 4; $result[0].TargetName | Should -Be 'ResourceName'; $result[1].TargetName | Should -Be 'AlternateName'; $result[2].TargetName | Should -Be 'TargetName'; - $result[3].TargetName | Should -Be 'OtherName'; - $result[4].TargetName | Should -Be 'MetadataName'; + $result[3].TargetName | Should -Be 'MetadataName'; - $option = New-PSRuleOption -Option @{ 'Binding.TargetName' = 'ResourceName', 'AlternateName'; 'Binding.IgnoreCase' = $False } -BindTargetName $bindFn; + $option = New-PSRuleOption -Option @{ 'Binding.TargetName' = 'ResourceName', 'AlternateName'; 'Binding.IgnoreCase' = $False }; $result = $testObject[0..1] | Invoke-PSRule -Option $option -Path $ruleFilePath -Name 'FromFile1'; $result | Should -Not -BeNullOrEmpty; $result.Count | Should -Be 2; @@ -2919,25 +2876,6 @@ Describe 'Binding' -Tag Common, Binding { $result | Should -Not -BeNullOrEmpty; $result.TargetType | Should -Be 'TestType'; } - - It 'Binds to custom type by script' { - $bindFn = { - param ($TargetObject) - - $otherType = $TargetObject.PSObject.Properties['OtherType']; - - if ($otherType -eq $Null) { - return $Null - } - - return $otherType.Value; - } - - $option = New-PSRuleOption -Option @{ 'Binding.TargetType' = 'kind' } -BindTargetType $bindFn; - $result = $testObject | Invoke-PSRule -Option $option -Path $ruleFilePath -Name 'FromFile1'; - $result | Should -Not -BeNullOrEmpty; - $result.TargetType | Should -Be 'OtherType'; - } } } diff --git a/tests/PSRule.Tests/PSRule.Tests.csproj b/tests/PSRule.Tests/PSRule.Tests.csproj index b6b7b41a81..d4bebd70ee 100644 --- a/tests/PSRule.Tests/PSRule.Tests.csproj +++ b/tests/PSRule.Tests/PSRule.Tests.csproj @@ -10,10 +10,10 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/PSRule.Tests/Pipeline/ResourceCacheTests.cs b/tests/PSRule.Tests/Pipeline/ResourceCacheTests.cs new file mode 100644 index 0000000000..00f7ef745d --- /dev/null +++ b/tests/PSRule.Tests/Pipeline/ResourceCacheTests.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using PSRule.Definitions; +using PSRule.Definitions.Selectors; + +namespace PSRule.Pipeline; + +/// +/// Units tests for . +/// +public sealed class ResourceCacheTests +{ + [Fact] + public void Import_WhenNullResource_ShouldReturnException() + { + var cache = new ResourceCache([]); + + Assert.Throws(() => cache.Import(null)); + } + + [Fact] + public void Import_WhenValidSelector_ShouldReturnTrue() + { + var cache = new ResourceCache([]); + var selector = new SelectorV1("", new SourceFile("", default, SourceType.Yaml, ""), new ResourceMetadata { Name = "test" }, default, default, new SelectorV1Spec()); + + Assert.True(cache.Import(selector)); + Assert.Single(cache.Selectors); + } +} diff --git a/tests/PSRule.Tests/PipelineTests.cs b/tests/PSRule.Tests/PipelineTests.cs index ffa0e34b2f..1fcf5c6c5f 100644 --- a/tests/PSRule.Tests/PipelineTests.cs +++ b/tests/PSRule.Tests/PipelineTests.cs @@ -180,7 +180,7 @@ public void PipelineWithInvariantCulture() Environment.UseCurrentCulture(CultureInfo.InvariantCulture); var context = PipelineContext.New(GetOption(), null, null, null, null, null, new OptionContextBuilder(), null); var writer = new TestWriter(GetOption()); - var pipeline = new GetRulePipeline(context, GetSource(), new PipelineInputStream(null, null, null, null), writer, false); + var pipeline = new GetRulePipeline(context, GetSource(), new PipelineInputStream(null, null, null), writer, false); try { pipeline.Begin(); @@ -202,7 +202,7 @@ public void PipelineWithInvariantCultureDisabled() option.Execution.InvariantCulture = ExecutionActionPreference.Ignore; var context = PipelineContext.New(option, null, null, null, null, null, new OptionContextBuilder(), null); var writer = new TestWriter(option); - var pipeline = new GetRulePipeline(context, GetSource(), new PipelineInputStream(null, null, null, null), writer, false); + var pipeline = new GetRulePipeline(context, GetSource(), new PipelineInputStream(null, null, null), writer, false); try { pipeline.Begin(); diff --git a/tests/PSRule.Tests/ResourceHelperTests.cs b/tests/PSRule.Tests/ResourceHelperTests.cs index 750aa1e56c..d23097f97a 100644 --- a/tests/PSRule.Tests/ResourceHelperTests.cs +++ b/tests/PSRule.Tests/ResourceHelperTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Definitions; diff --git a/tests/PSRule.Tests/RulesTests.cs b/tests/PSRule.Tests/RulesTests.cs index d8ac191bd8..0e28560832 100644 --- a/tests/PSRule.Tests/RulesTests.cs +++ b/tests/PSRule.Tests/RulesTests.cs @@ -60,7 +60,7 @@ public void ReadYamlRule() [Fact] public void ReadYamlSubSelectorRule() { - var context = new RunspaceContext(PipelineContext.New(GetOption(), null, null, PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, new OptionContextBuilder(), null), new TestWriter(GetOption())); + var context = new RunspaceContext(PipelineContext.New(GetOption(), null, null, PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, GetOptionBuilder(), null), new TestWriter(GetOption())); context.Init(GetSource("FromFileSubSelector.Rule.yaml")); context.Begin(); @@ -132,7 +132,7 @@ public void ReadYamlSubSelectorRule() [Fact] public void EvaluateYamlRule() { - var context = new RunspaceContext(PipelineContext.New(GetOption(), null, null, PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, new OptionContextBuilder(), null), new TestWriter(GetOption())); + var context = new RunspaceContext(PipelineContext.New(GetOption(), null, null, PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, GetOptionBuilder(), null), new TestWriter(GetOption())); context.Init(GetSource()); context.Begin(); ImportSelectors(context); @@ -195,7 +195,7 @@ public void EvaluateYamlRule() [Fact] public void RuleWithObjectPath() { - var context = new RunspaceContext(PipelineContext.New(GetOption(), null, null, PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, new OptionContextBuilder(), null), new TestWriter(GetOption())); + var context = new RunspaceContext(PipelineContext.New(GetOption(), null, null, PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, GetOptionBuilder(), null), new TestWriter(GetOption())); context.Init(GetSource()); context.Begin(); ImportSelectors(context); @@ -263,7 +263,7 @@ public void ReadJsonRule() [Fact] public void ReadJsonSubSelectorRule() { - var context = new RunspaceContext(PipelineContext.New(GetOption(), null, null, PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, new OptionContextBuilder(), null), new TestWriter(GetOption())); + var context = new RunspaceContext(PipelineContext.New(GetOption(), null, null, PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, GetOptionBuilder(), null), new TestWriter(GetOption())); context.Init(GetSource("FromFileSubSelector.Rule.jsonc")); context.Begin(); @@ -341,6 +341,11 @@ private static PSRuleOption GetOption() return new PSRuleOption(); } + private static OptionContextBuilder GetOptionBuilder() + { + return new OptionContextBuilder(option: GetOption(), bindTargetName: PipelineHookActions.BindTargetName, bindTargetType: PipelineHookActions.BindTargetType, bindField: PipelineHookActions.BindField); + } + private static Source[] GetSource(string path = "FromFile.Rule.yaml") { var builder = new SourcePipelineBuilder(null, null); diff --git a/tests/PSRule.Tests/SelectorTests.cs b/tests/PSRule.Tests/SelectorTests.cs index 2ea819a585..108c3679b3 100644 --- a/tests/PSRule.Tests/SelectorTests.cs +++ b/tests/PSRule.Tests/SelectorTests.cs @@ -14,13 +14,6 @@ namespace PSRule; -internal enum TestEnumValue -{ - None = 0, - - All = 1 -} - public sealed class SelectorTests { private const string SelectorYamlFileName = "Selectors.Rule.yaml"; @@ -39,7 +32,7 @@ public void ReadSelector(string type, string path) context.Begin(); var selector = HostHelper.GetSelectorForTests(GetSource(path), context).ToArray(); Assert.NotNull(selector); - Assert.Equal(102, selector.Length); + Assert.Equal(104, selector.Length); var actual = selector[0]; var visitor = new SelectorVisitor(context, actual.Id, actual.Source, actual.Spec.If); @@ -967,6 +960,7 @@ public void ContainsExpression(string type, string path) ); context.EnterTargetObject(new TargetObject(actual1)); + context.EnterLanguageScope(withName.Source); Assert.True(withName.Match(actual1)); context.EnterTargetObject(new TargetObject(actual2)); @@ -1572,6 +1566,15 @@ public void Version(string type, string path) Assert.True(version.Match(actual5)); Assert.False(version.Match(actual6)); Assert.False(version.Match(actual7)); + + version = GetSelectorVisitor($"{type}VersionAnyStableVersion", GetSource(path), out _); + Assert.True(version.Match(actual1)); + Assert.True(version.Match(actual2)); + Assert.True(version.Match(actual3)); + Assert.True(version.Match(actual4)); + Assert.False(version.Match(actual5)); + Assert.False(version.Match(actual6)); + Assert.False(version.Match(actual7)); } [Theory] @@ -1613,6 +1616,15 @@ public void APIVersion(string type, string path) Assert.True(version.Match(actual5)); Assert.False(version.Match(actual6)); Assert.False(version.Match(actual7)); + + version = GetSelectorVisitor($"{type}APIVersionAnyStableVersion", GetSource(path), out _); + Assert.True(version.Match(actual1)); + Assert.True(version.Match(actual2)); + Assert.True(version.Match(actual3)); + Assert.False(version.Match(actual4)); + Assert.False(version.Match(actual5)); + Assert.False(version.Match(actual6)); + Assert.False(version.Match(actual7)); } [Theory] @@ -1873,7 +1885,7 @@ private static PSObject GetObject(params (string name, object value)[] propertie private static SelectorVisitor GetSelectorVisitor(string name, Source[] source, out RunspaceContext context) { - var builder = new OptionContextBuilder(GetOption()); + var builder = new OptionContextBuilder(GetOption(), bindTargetName: PipelineHookActions.BindTargetName, bindTargetType: PipelineHookActions.BindTargetType, bindField: PipelineHookActions.BindField); context = new RunspaceContext(PipelineContext.New(GetOption(), null, null, PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, builder, null), null); context.Init(source); context.Begin(); diff --git a/tests/PSRule.Tests/Selectors.Rule.jsonc b/tests/PSRule.Tests/Selectors.Rule.jsonc index 342d69d78d..9a0ccefdb8 100644 --- a/tests/PSRule.Tests/Selectors.Rule.jsonc +++ b/tests/PSRule.Tests/Selectors.Rule.jsonc @@ -1353,6 +1353,21 @@ "metadata": { "name": "JsonVersionAnyVersion" }, + "spec": { + "if": { + "field": "version", + "version": "", + "includePrerelease": true + } + } + }, + { + // Synopsis: Test any valid stable version + "apiVersion": "github.com/microsoft/PSRule/v1", + "kind": "Selector", + "metadata": { + "name": "JsonVersionAnyStableVersion" + }, "spec": { "if": { "field": "version", @@ -1898,6 +1913,21 @@ "metadata": { "name": "JsonAPIVersionAnyVersion" }, + "spec": { + "if": { + "field": "dateVersion", + "apiVersion": "", + "includePrerelease": true + } + } + }, + { + // Synopsis: Test comparison with apiVersion. + "apiVersion": "github.com/microsoft/PSRule/v1", + "kind": "Selector", + "metadata": { + "name": "JsonAPIVersionAnyStableVersion" + }, "spec": { "if": { "field": "dateVersion", diff --git a/tests/PSRule.Tests/Selectors.Rule.yaml b/tests/PSRule.Tests/Selectors.Rule.yaml index 4bbf6a30b1..04111bf97e 100644 --- a/tests/PSRule.Tests/Selectors.Rule.yaml +++ b/tests/PSRule.Tests/Selectors.Rule.yaml @@ -935,6 +935,18 @@ apiVersion: github.com/microsoft/PSRule/v1 kind: Selector metadata: name: YamlVersionAnyVersion +spec: + if: + field: 'version' + version: '' + includePrerelease: true + +--- +# Synopsis: Test any valid stable version +apiVersion: github.com/microsoft/PSRule/v1 +kind: Selector +metadata: + name: YamlVersionAnyStableVersion spec: if: field: 'version' @@ -1359,6 +1371,18 @@ apiVersion: github.com/microsoft/PSRule/v1 kind: Selector metadata: name: YamlAPIVersionAnyVersion +spec: + if: + field: dateVersion + apiVersion: '' + includePrerelease: true + +--- +# Synopsis: Test comparison with apiVersion. +apiVersion: github.com/microsoft/PSRule/v1 +kind: Selector +metadata: + name: YamlAPIVersionAnyStableVersion spec: if: field: dateVersion diff --git a/tests/PSRule.Tests/SemanticBreakTests.cs b/tests/PSRule.Tests/SemanticBreakTests.cs index f600ba272e..62a1f8a7ce 100644 --- a/tests/PSRule.Tests/SemanticBreakTests.cs +++ b/tests/PSRule.Tests/SemanticBreakTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace PSRule; diff --git a/tests/PSRule.Tests/TargetBinderTests.cs b/tests/PSRule.Tests/TargetBinderTests.cs deleted file mode 100644 index 4391329647..0000000000 --- a/tests/PSRule.Tests/TargetBinderTests.cs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Management.Automation; -using Newtonsoft.Json.Linq; -using PSRule.Configuration; -using PSRule.Definitions.Baselines; -using PSRule.Definitions.ModuleConfigs; -using PSRule.Pipeline; - -namespace PSRule; - -public sealed class TargetBinderTests -{ - [Fact] - public void BindTargetObject() - { - var binder = GetBinder(); - var targetObject = GetTargetObject(); - binder.Bind(targetObject); - - var m1 = binder.Result("Module1"); - Assert.Equal("Name1", m1.TargetName); - Assert.Equal("Type1", m1.TargetType); - - var m2 = binder.Result("Module2"); - Assert.Equal("Name2", m2.TargetName); - Assert.Equal("Type1", m2.TargetType); - - var m0 = binder.Result("."); - Assert.Equal("Name1", m0.TargetName); - Assert.Equal("System.Management.Automation.PSCustomObject", m0.TargetType); - - // With specified type - targetObject = GetTargetObject(targetType: "ManualType"); - binder.Bind(targetObject); - - m1 = binder.Result("Module1"); - Assert.Equal("Name1", m1.TargetName); - Assert.Equal("Type1", m1.TargetType); - - var m3 = binder.Result("Module3"); - Assert.Equal("Name1", m3.TargetName); - Assert.Equal("ManualType", m3.TargetType); - } - - [Fact] - public void BindJObject() - { - var binder = GetBinder(); - var targetObject = new TargetObject(PSObject.AsPSObject(JToken.Parse("{ \"name\": \"Name1\", \"type\": \"Type1\", \"AlternativeName\": \"Name2\", \"AlternativeType\": \"Type2\" }"))); - binder.Bind(targetObject); - - var m1 = binder.Result("Module1"); - Assert.Equal("Name1", m1.TargetName); - Assert.Equal("Type1", m1.TargetType); - - var m2 = binder.Result("Module2"); - Assert.Equal("Name2", m2.TargetName); - Assert.Equal("Type1", m2.TargetType); - - var m0 = binder.Result("."); - Assert.Equal("Name1", m0.TargetName); - Assert.Equal("System.Management.Automation.PSCustomObject", m0.TargetType); - } - - #region Helper methods - - private static TargetObject GetTargetObject(string targetType = null) - { - var pso = new PSObject(); - pso.Properties.Add(new PSNoteProperty("name", "Name1")); - pso.Properties.Add(new PSNoteProperty("type", "Type1")); - pso.Properties.Add(new PSNoteProperty("AlternativeName", "Name2")); - pso.Properties.Add(new PSNoteProperty("AlternativeType", "Type2")); - return new TargetObject(pso, targetType: targetType); - } - - private static ITargetBinder GetBinder() - { - var builder = new TargetBinderBuilder(PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, null); - var option = new OptionContextBuilder(); - - option.ModuleConfig("Module1", new ModuleConfigV1Spec - { - Binding = new BindingOption - { - TargetName = new[] { "name" }, - TargetType = new[] { "type" } - } - }); - - option.ModuleConfig("Module2", new ModuleConfigV1Spec - { - Binding = new BindingOption - { - TargetName = new[] { "AlternativeName" }, - TargetType = new[] { "type" } - } - }); - - option.ModuleConfig("Module3", new ModuleConfigV1Spec - { - Binding = new BindingOption - { - TargetName = new[] { "name" }, - TargetType = new[] { "type" }, - PreferTargetInfo = true - } - }); - - var scopes = new Runtime.LanguageScopeSet(null); - - scopes.Import("Module1", out var module1); - module1.Configure(option.Build(module1.Name)); - builder.With(module1); - - scopes.Import("Module2", out var module2); - module2.Configure(option.Build(module2.Name)); - builder.With(module2); - - scopes.Import("Module3", out var module3); - module3.Configure(option.Build(module3.Name)); - builder.With(module3); - - scopes.Import(".", out var local); - local.Configure(option.Build(local.Name)); - builder.With(local); - return builder.Build(); - } - - private static IBaselineV1Spec GetOption(string[] targetName, string[] targetType, bool preferTargetInfo = false) - { - var result = new BaselineOption.BaselineInline(); - result.Binding.TargetName = targetName; - result.Binding.TargetType = targetType; - result.Binding.PreferTargetInfo = preferTargetInfo; - return result; - } - - #endregion Helper methods -} diff --git a/tests/PSRule.Tests/TestEnumValue.cs b/tests/PSRule.Tests/TestEnumValue.cs new file mode 100644 index 0000000000..fcea60d404 --- /dev/null +++ b/tests/PSRule.Tests/TestEnumValue.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace PSRule; + +internal enum TestEnumValue +{ + None = 0, + + All = 1 +} diff --git a/tests/PSRule.Tests/TestModule4/rules/Baseline.Rule.yaml b/tests/PSRule.Tests/TestModule4/rules/Baseline.Rule.yaml index 7f335a0f6c..a86e34ee34 100644 --- a/tests/PSRule.Tests/TestModule4/rules/Baseline.Rule.yaml +++ b/tests/PSRule.Tests/TestModule4/rules/Baseline.Rule.yaml @@ -8,17 +8,6 @@ kind: Baseline metadata: name: Module4 spec: - binding: - field: - kind: - - Id - uniqueIdentifer: - - Id - - AlternateName - targetName: - - AlternateName - targetType: - - Kind rule: include: - 'M4.Rule1' @@ -32,17 +21,6 @@ kind: Baseline metadata: name: Module4a spec: - binding: - field: - kind: - - Id - uniqueIdentifer: - - Id - - AlternateName - targetName: - - AlternateName - targetType: - - Kind rule: include: - 'M4.Rule1' @@ -56,11 +34,6 @@ kind: Baseline metadata: name: Baseline2 spec: - binding: - targetName: - - AlternateName - targetType: - - Kind rule: include: - 'M4.Rule1' @@ -74,13 +47,6 @@ kind: Baseline metadata: name: Baseline3 spec: - binding: - targetName: - - AlternateName - targetType: - - Kind - field: - AlternativeType: [ 'AlternateName' ] rule: include: - 'M4.Rule1' diff --git a/tests/PSRule.Tests/TestModule4/rules/Module4.Rule.ps1 b/tests/PSRule.Tests/TestModule4/rules/Module4.Rule.ps1 index 1371d38673..a09a0e372a 100644 --- a/tests/PSRule.Tests/TestModule4/rules/Module4.Rule.ps1 +++ b/tests/PSRule.Tests/TestModule4/rules/Module4.Rule.ps1 @@ -7,8 +7,6 @@ # Synopsis: Test rule in TestModule4 Rule 'M4.Rule1' { - $PSRule.TargetName -eq 'TestObject1' - $PSRule.TargetType -eq 'TestObjectType' $Configuration.ruleConfig1 -eq 'Test' } diff --git a/tests/PSRule.Tests/TestModule5/rules/Baseline.Rule.jsonc b/tests/PSRule.Tests/TestModule5/rules/Baseline.Rule.jsonc index 597be63f73..c357ffb765 100644 --- a/tests/PSRule.Tests/TestModule5/rules/Baseline.Rule.jsonc +++ b/tests/PSRule.Tests/TestModule5/rules/Baseline.Rule.jsonc @@ -10,23 +10,6 @@ "name": "Module4" }, "spec": { - "binding": { - "field": { - "kind": [ - "Id" - ], - "uniqueIdentifer": [ - "AlternateName", - "Id" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig1": "Test" }, @@ -45,23 +28,6 @@ "name": "Module4a" }, "spec": { - "binding": { - "field": { - "kind": [ - "Id" - ], - "uniqueIdentifer": [ - "AlternateName", - "Id" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig1": "Test2" }, @@ -80,14 +46,6 @@ "name": "Baseline2" }, "spec": { - "binding": { - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig2": "Test3" }, @@ -106,19 +64,6 @@ "name": "Baseline3" }, "spec": { - "binding": { - "field": { - "AlternativeType": [ - "AlternateName" - ] - }, - "targetName": [ - "AlternateName" - ], - "targetType": [ - "Kind" - ] - }, "configuration": { "ruleConfig2": "Test3" }, diff --git a/tests/PSRule.Tests/TestResourceName.cs b/tests/PSRule.Tests/TestResourceName.cs index c03babcca6..b8415e9c51 100644 --- a/tests/PSRule.Tests/TestResourceName.cs +++ b/tests/PSRule.Tests/TestResourceName.cs @@ -3,7 +3,6 @@ using System; using PSRule.Definitions; -using PSRule.Pipeline; namespace PSRule; diff --git a/tests/PSRule.Tool.Tests/PSRule.Tool.Tests.csproj b/tests/PSRule.Tool.Tests/PSRule.Tool.Tests.csproj index 9534987a3d..8717a2a0c7 100644 --- a/tests/PSRule.Tool.Tests/PSRule.Tool.Tests.csproj +++ b/tests/PSRule.Tool.Tests/PSRule.Tool.Tests.csproj @@ -10,8 +10,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/PSRule.Types.Tests/Data/ModuleConstraintTests.cs b/tests/PSRule.Types.Tests/Data/ModuleConstraintTests.cs new file mode 100644 index 0000000000..0e03ee97b6 --- /dev/null +++ b/tests/PSRule.Types.Tests/Data/ModuleConstraintTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace PSRule.Data; + +/// +/// Tests for . +/// +public sealed class ModuleConstraintTests +{ + [Theory] + [InlineData("1.0.0")] + [InlineData("0.1.0+build.1")] + public void Any_WhenIncludePrereleaseIsFalse_ShouldAcceptStableVersions(string version) + { + var constraint = ModuleConstraint.Any("test", includePrerelease: false); + Assert.True(SemanticVersion.TryParseVersion(version, out var actualVersion)); + Assert.True(constraint.Accepts(actualVersion)); + } + + [Theory] + [InlineData("1.0.0-preview")] + [InlineData("0.1.0-alpha.1+build.1")] + public void Any_WhenIncludePrereleaseIsFalse_ShouldNotAcceptPrereleaseVersions(string version) + { + var constraint = ModuleConstraint.Any("test", includePrerelease: false); + Assert.True(SemanticVersion.TryParseVersion(version, out var actualVersion)); + Assert.False(constraint.Accepts(actualVersion)); + } + + [Theory] + [InlineData("1.0.0")] + [InlineData("0.1.0+build.1")] + [InlineData("1.0.0-preview")] + [InlineData("0.1.0-alpha.1+build.1")] + public void Any_WhenIncludePrereleaseIsTrue_ShouldAcceptStableOrPrereleaseVersions(string version) + { + var constraint = ModuleConstraint.Any("test", includePrerelease: true); + Assert.True(SemanticVersion.TryParseVersion(version, out var actualVersion)); + Assert.True(constraint.Accepts(actualVersion)); + } +} diff --git a/tests/PSRule.Tests/SemanticVersionTests.cs b/tests/PSRule.Types.Tests/Data/SemanticVersionTests.cs similarity index 52% rename from tests/PSRule.Tests/SemanticVersionTests.cs rename to tests/PSRule.Types.Tests/Data/SemanticVersionTests.cs index 3c6b65889e..ad93401731 100644 --- a/tests/PSRule.Tests/SemanticVersionTests.cs +++ b/tests/PSRule.Types.Tests/Data/SemanticVersionTests.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using PSRule.Data; - -namespace PSRule; +namespace PSRule.Data; /// /// Tests for semantic version comparison. @@ -17,20 +15,20 @@ public sealed class SemanticVersionTests public void Version() { Assert.True(SemanticVersion.TryParseVersion("1.2.3-alpha.3+7223b39", out var actual1)); - Assert.Equal(1, actual1.Major); + Assert.Equal(1, actual1!.Major); Assert.Equal(2, actual1.Minor); Assert.Equal(3, actual1.Patch); Assert.Equal("alpha.3", actual1.Prerelease.Value); Assert.Equal("7223b39", actual1.Build); Assert.True(SemanticVersion.TryParseVersion("v1.2.3-alpha.3", out var actual2)); - Assert.Equal(1, actual2.Major); + Assert.Equal(1, actual2!.Major); Assert.Equal(2, actual2.Minor); Assert.Equal(3, actual2.Patch); Assert.Equal("alpha.3", actual2.Prerelease.Value); Assert.True(SemanticVersion.TryParseVersion("v1.2.3+7223b39", out var actual3)); - Assert.Equal(1, actual3.Major); + Assert.Equal(1, actual3!.Major); Assert.Equal(2, actual3.Minor); Assert.Equal(3, actual3.Patch); Assert.Equal("7223b39", actual3.Build); @@ -47,11 +45,11 @@ public void VersionOrder() Assert.True(SemanticVersion.TryParseVersion("10.0.0", out var actual3)); Assert.True(SemanticVersion.TryParseVersion("1.0.2", out var actual4)); - Assert.True(actual1.CompareTo(actual1) == 0); + Assert.Equal(0, actual1!.CompareTo(actual1)); Assert.True(actual1.CompareTo(actual2) < 0); Assert.True(actual1.CompareTo(actual3) < 0); Assert.True(actual1.CompareTo(actual4) < 0); - Assert.True(actual2.CompareTo(actual2) == 0); + Assert.Equal(0, actual2!.CompareTo(actual2)); Assert.True(actual2.CompareTo(actual1) > 0); Assert.True(actual2.CompareTo(actual3) < 0); Assert.True(actual2.CompareTo(actual4) > 0); @@ -97,96 +95,96 @@ public void Constraint() Assert.True(SemanticVersion.TryParseConstraint("@prerelease <=3.4.5-0", out var actual21)); // Version1 - 1.2.3 - Assert.True(actual1.Equals(version1)); - Assert.False(actual2.Equals(version1)); - Assert.True(actual3.Equals(version1)); - Assert.True(actual4.Equals(version1)); - Assert.False(actual5.Equals(version1)); - Assert.True(actual6.Equals(version1)); - Assert.True(actual7.Equals(version1)); - Assert.True(actual8.Equals(version1)); - Assert.True(actual9.Equals(version1)); - Assert.True(actual10.Equals(version1)); - Assert.True(actual11.Equals(version1)); - Assert.True(actual12.Equals(version1)); - Assert.True(actual13.Equals(version1)); - Assert.False(actual14.Equals(version1)); - Assert.True(actual15.Equals(version1)); - Assert.True(actual16.Equals(version1)); - Assert.True(actual17.Equals(version1)); - Assert.True(actual18.Equals(version1)); - Assert.True(actual19.Equals(version1)); - Assert.True(actual20.Equals(version1)); - Assert.True(actual21.Equals(version1)); + Assert.True(actual1.Accepts(version1)); + Assert.False(actual2.Accepts(version1)); + Assert.True(actual3.Accepts(version1)); + Assert.True(actual4.Accepts(version1)); + Assert.False(actual5.Accepts(version1)); + Assert.True(actual6.Accepts(version1)); + Assert.True(actual7.Accepts(version1)); + Assert.True(actual8.Accepts(version1)); + Assert.True(actual9.Accepts(version1)); + Assert.True(actual10.Accepts(version1)); + Assert.True(actual11.Accepts(version1)); + Assert.True(actual12.Accepts(version1)); + Assert.True(actual13.Accepts(version1)); + Assert.False(actual14.Accepts(version1)); + Assert.True(actual15.Accepts(version1)); + Assert.True(actual16.Accepts(version1)); + Assert.True(actual17.Accepts(version1)); + Assert.True(actual18.Accepts(version1)); + Assert.True(actual19.Accepts(version1)); + Assert.True(actual20.Accepts(version1)); + Assert.True(actual21.Accepts(version1)); // Version2 - 1.2.3-alpha.3+7223b39 - Assert.False(actual1.Equals(version2)); - Assert.True(actual2.Equals(version2)); - Assert.False(actual3.Equals(version2)); - Assert.True(actual4.Equals(version2)); - Assert.True(actual5.Equals(version2)); - Assert.True(actual6.Equals(version2)); - Assert.False(actual7.Equals(version2)); - Assert.False(actual8.Equals(version2)); - Assert.False(actual9.Equals(version2)); - Assert.True(actual10.Equals(version2)); - Assert.False(actual11.Equals(version2)); - Assert.False(actual12.Equals(version2)); - Assert.False(actual13.Equals(version2)); - Assert.False(actual14.Equals(version2)); - Assert.False(actual15.Equals(version2)); - Assert.False(actual16.Equals(version2)); - Assert.False(actual17.Equals(version2)); - Assert.False(actual18.Equals(version2)); - Assert.True(actual19.Equals(version2)); - Assert.False(actual20.Equals(version2)); - Assert.True(actual21.Equals(version2)); + Assert.False(actual1.Accepts(version2)); + Assert.True(actual2.Accepts(version2)); + Assert.False(actual3.Accepts(version2)); + Assert.True(actual4.Accepts(version2)); + Assert.True(actual5.Accepts(version2)); + Assert.True(actual6.Accepts(version2)); + Assert.False(actual7.Accepts(version2)); + Assert.False(actual8.Accepts(version2)); + Assert.False(actual9.Accepts(version2)); + Assert.True(actual10.Accepts(version2)); + Assert.False(actual11.Accepts(version2)); + Assert.False(actual12.Accepts(version2)); + Assert.False(actual13.Accepts(version2)); + Assert.False(actual14.Accepts(version2)); + Assert.False(actual15.Accepts(version2)); + Assert.False(actual16.Accepts(version2)); + Assert.False(actual17.Accepts(version2)); + Assert.False(actual18.Accepts(version2)); + Assert.True(actual19.Accepts(version2)); + Assert.False(actual20.Accepts(version2)); + Assert.True(actual21.Accepts(version2)); // Version3 - 3.4.5-alpha.9 - Assert.False(actual1.Equals(version3)); - Assert.False(actual2.Equals(version3)); - Assert.False(actual3.Equals(version3)); - Assert.False(actual4.Equals(version3)); - Assert.False(actual5.Equals(version3)); - Assert.False(actual6.Equals(version3)); - Assert.False(actual7.Equals(version3)); - Assert.False(actual8.Equals(version3)); - Assert.False(actual9.Equals(version3)); - Assert.False(actual10.Equals(version3)); - Assert.False(actual11.Equals(version3)); - Assert.False(actual12.Equals(version3)); - Assert.False(actual13.Equals(version3)); - Assert.False(actual14.Equals(version3)); - Assert.False(actual15.Equals(version3)); - Assert.True(actual16.Equals(version3)); - Assert.False(actual17.Equals(version3)); - Assert.True(actual18.Equals(version3)); - Assert.False(actual19.Equals(version3)); - Assert.True(actual20.Equals(version3)); - Assert.False(actual21.Equals(version3)); + Assert.False(actual1.Accepts(version3)); + Assert.False(actual2.Accepts(version3)); + Assert.False(actual3.Accepts(version3)); + Assert.False(actual4.Accepts(version3)); + Assert.False(actual5.Accepts(version3)); + Assert.False(actual6.Accepts(version3)); + Assert.False(actual7.Accepts(version3)); + Assert.False(actual8.Accepts(version3)); + Assert.False(actual9.Accepts(version3)); + Assert.False(actual10.Accepts(version3)); + Assert.False(actual11.Accepts(version3)); + Assert.False(actual12.Accepts(version3)); + Assert.False(actual13.Accepts(version3)); + Assert.False(actual14.Accepts(version3)); + Assert.False(actual15.Accepts(version3)); + Assert.True(actual16.Accepts(version3)); + Assert.False(actual17.Accepts(version3)); + Assert.True(actual18.Accepts(version3)); + Assert.False(actual19.Accepts(version3)); + Assert.True(actual20.Accepts(version3)); + Assert.False(actual21.Accepts(version3)); // Version4 - 3.4.5 - Assert.False(actual1.Equals(version4)); - Assert.False(actual2.Equals(version4)); - Assert.True(actual3.Equals(version4)); - Assert.True(actual4.Equals(version4)); - Assert.False(actual5.Equals(version4)); - Assert.False(actual6.Equals(version4)); - Assert.True(actual7.Equals(version4)); - Assert.False(actual8.Equals(version4)); - Assert.True(actual9.Equals(version4)); - Assert.True(actual10.Equals(version4)); - Assert.False(actual11.Equals(version4)); - Assert.False(actual12.Equals(version4)); - Assert.False(actual13.Equals(version4)); - Assert.False(actual14.Equals(version4)); - Assert.True(actual15.Equals(version4)); - Assert.True(actual16.Equals(version4)); - Assert.True(actual17.Equals(version4)); - Assert.True(actual18.Equals(version4)); - Assert.False(actual19.Equals(version4)); - Assert.True(actual20.Equals(version4)); - Assert.False(actual21.Equals(version4)); + Assert.False(actual1.Accepts(version4)); + Assert.False(actual2.Accepts(version4)); + Assert.True(actual3.Accepts(version4)); + Assert.True(actual4.Accepts(version4)); + Assert.False(actual5.Accepts(version4)); + Assert.False(actual6.Accepts(version4)); + Assert.True(actual7.Accepts(version4)); + Assert.False(actual8.Accepts(version4)); + Assert.True(actual9.Accepts(version4)); + Assert.True(actual10.Accepts(version4)); + Assert.False(actual11.Accepts(version4)); + Assert.False(actual12.Accepts(version4)); + Assert.False(actual13.Accepts(version4)); + Assert.False(actual14.Accepts(version4)); + Assert.True(actual15.Accepts(version4)); + Assert.True(actual16.Accepts(version4)); + Assert.True(actual17.Accepts(version4)); + Assert.True(actual18.Accepts(version4)); + Assert.False(actual19.Accepts(version4)); + Assert.True(actual20.Accepts(version4)); + Assert.False(actual21.Accepts(version4)); } /// @@ -204,7 +202,7 @@ public void Prerelease() var actual7 = new SemanticVersion.PR("beta.11"); var actual8 = new SemanticVersion.PR("rc.1"); - Assert.True(actual1.CompareTo(actual1) == 0); + Assert.Equal(0, actual1.CompareTo(actual1)); Assert.True(actual1.CompareTo(actual2) > 0); Assert.True(actual1.CompareTo(actual6) > 0); Assert.True(actual2.CompareTo(actual3) < 0); @@ -216,4 +214,26 @@ public void Prerelease() Assert.True(actual8.CompareTo(actual1) < 0); Assert.True(actual8.CompareTo(actual2) > 0); } + + [Theory] + [InlineData("1.2.3")] + [InlineData("1.2.3-alpha.3+7223b39")] + [InlineData("3.4.5-alpha.9")] + [InlineData("3.4.5+7223b39")] + public void ToString_WhenValid_ShouldReturnString(string version) + { + Assert.True(SemanticVersion.TryParseVersion(version, out var actual)); + Assert.Equal(version, actual!.ToString()); + } + + [Theory] + [InlineData("1.2.3")] + [InlineData("1.2.3-alpha.3+7223b39")] + [InlineData("3.4.5-alpha.9")] + [InlineData("3.4.5+7223b39")] + public void ToShortString_WhenValid_ShouldReturnString(string version) + { + Assert.True(SemanticVersion.TryParseVersion(version, out var actual)); + Assert.Equal(string.Join(".", actual!.Major, actual.Minor, actual.Patch), actual!.ToShortString()); + } } diff --git a/tests/PSRule.Types.Tests/GlobalUsings.cs b/tests/PSRule.Types.Tests/GlobalUsings.cs new file mode 100644 index 0000000000..8c07c6cf4c --- /dev/null +++ b/tests/PSRule.Types.Tests/GlobalUsings.cs @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +global using Xunit; diff --git a/tests/PSRule.Types.Tests/PSRule.Types.Tests.csproj b/tests/PSRule.Types.Tests/PSRule.Types.Tests.csproj new file mode 100644 index 0000000000..cc9b7b3a98 --- /dev/null +++ b/tests/PSRule.Types.Tests/PSRule.Types.Tests.csproj @@ -0,0 +1,28 @@ + + + + net8.0 + PSRule + {8860178f-4b4a-4e28-8cc3-85dfe2a2fe4b} + 12.0 + enable + enable + + false + true + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + +