Skip to content

Commit

Permalink
Add support for declaring required capabilities microsoft#2707 (micro…
Browse files Browse the repository at this point in the history
  • Loading branch information
BernieWhite authored Jan 8, 2025
1 parent 778f4b6 commit 46c1a14
Show file tree
Hide file tree
Showing 58 changed files with 1,348 additions and 234 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ The following conceptual topics exist in the `PSRule` module:
- [Binding.TargetName](https://aka.ms/ps-rule/options#bindingtargetname)
- [Binding.TargetType](https://aka.ms/ps-rule/options#bindingtargettype)
- [Binding.UseQualifiedName](https://aka.ms/ps-rule/options#bindingusequalifiedname)
- [Capabilities](https://aka.ms/ps-rule/options#capabilities)
- [Configuration](https://aka.ms/ps-rule/options#configuration)
- [Convention.Include](https://aka.ms/ps-rule/options#conventioninclude)
- [Execution.AliasReference](https://aka.ms/ps-rule/options#executionaliasreference)
Expand Down
5 changes: 5 additions & 0 deletions docs/CHANGELOG-v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ What's changed since pre-release v3.0.0-B0351:
- A precondition `type` property has been added to selectors and suppression groups.
- This simplifies type conditions that are common used in selectors and suppression groups.
- To use this feature, set the `apiVersion` to `github.com/microsoft/PSRule/2025-01-01`.
- Add support for declaring required capabilities in workspaces and modules by @BernieWhite.
[#2707](https://github.com/microsoft/PSRule/issues/2707)
- A module or workspace can declare required capabilities that must be supported by the runtime.
- When a capability is not supported or disabled, the runtime will fail with a specific error.
- This provides a way to ensure that rules execute consistently across environments.
- General improvements:
- Added support for registering custom emitters by @BernieWhite.
[#2681](https://github.com/microsoft/PSRule/issues/2681)
Expand Down
43 changes: 43 additions & 0 deletions docs/concepts/PSRule/en-US/about_PSRule_Options.md
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,49 @@ variables:
value: '2'
```

### Capabilities

<!-- module:version 3.0.0 -->

Specifies a list of capabilities required by the configuration.
When set, PSRule will validate that the capabilities are available before executing the configuration.
If the capabilities are not available, PSRule will report an error.

Capabilities may not be available if the PSRule version you are using does not support the feature.
Additionally, some capabilities may be disabled by the environment or configuration.

This option can be specified using:

```powershell
# PowerShell: Using the Capabilities hashtable key
$option = New-PSRuleOption -Option @{ 'Capabilities' = 'api-2025-01-01' };
```

```yaml
# YAML: Using the capabilities property
capabilities:
- 'api-2025-01-01'
- 'api-v1
```

```bash
# Bash: Using environment variable
export PSRULE_CAPABILITIES='api-2025-01-01;api-v1'
```

```yaml
# GitHub Actions: Using environment variable
env:
PSRULE_CAPABILITIES: 'api-2025-01-01;api-v1'
```

```yaml
# Azure Pipelines: Using environment variable
variables:
- name: PSRULE_CAPABILITIES
value: 'api-2025-01-01;api-v1'
```

### Convention.Include

Specifies conventions to execute when the pipeline run.
Expand Down
56 changes: 56 additions & 0 deletions docs/concepts/capabilities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
description: Capabilities are a way to declare or require a minimum set of features or functionality.
module: 3.0.0
---

# Capabilities

Capabilities are a way to declare or require a minimum set of features or functionality.
As PSRule evolves, new features or behaviors are added that may not be supported by previous versions.
By using capabilities, workspace and module authors can declare capabilities that are required.
Declaring capabilities provides a way to ensure consistent behavior across different configurations and versions of PSRule.

Each capability is a unique identifier that represents a feature or behavior of the PSRule engine.

!!! Note
Capabilities are a new feature in PSRule v3 and are not supported in previous versions.
Earlier versions of PSRule will not check for missing capabilities before executing rules or resources.

## Supported capabilities

The following capabilities are currently supported:

Identifier | Description
---------- | -----------
`api-v1` | YAML or JSON resource types that use the API version `github.com/microsoft/PSRule/v1`. This capability is always enabled in PSRule v3.
`api-2025-01-01` | YAML or JSON resource types that use the API version `github.com/microsoft/PSRule/2025-01-01`. This capability is always enabled in PSRule v3.
`powershell-language` | Determines if the workspace or module uses language features such as rules and conventions that use PowerShell script. This capability is enabled unless the `Execution.RestrictScriptSource` option restricts the use of PowerShell script.

## Declaring capabilities

Declaring capabilities is optional.
When you do not declare capabilities, an unrecognized or disabled features is ignored.
However, please note the execution behavior may not be as expected if rules or resources depend on these features.

For example, if a you define a rule in PowerShell but PowerShell script is disabled, the rule will not be executed.

Workspace capabilities are declared in a `ps-rule.yaml` file using the `capabilities` key.

```yaml title="ps-rule.yaml"
capabilities:
- api-v1
- powershell-language
```
Module capabilities are declared in the `ModuleConfig` resource for the module using the `capabilities` key.

```yaml title="ModuleConfig.Rule.yaml"
apiVersion: github.com/microsoft/PSRule/2025-01-01
kind: ModuleConfig
metadata:
name: MyModule
spec:
capabilities:
- api-v1
- powershell-language
```
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ nav:
- Using PSRule from a Container: scenarios/containers/container-execution.md
- Concepts:
- Baselines: concepts/baselines.md
- Capabilities: concepts/capabilities.md
- Emitters: concepts/emitters.md
- Functions: expressions/functions.md
- Grouping rules: concepts/grouping-rules.md
Expand Down
7 changes: 7 additions & 0 deletions ps-rule-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ run:
category: CI repository scan
description: An analysis scan that checks repository files aligned to expected format requirements.

requires:
PSRule.Rules.MSFT.OSS: '>=1.1.0'

capabilities:
- api-2025-01-01
- api-v1

output:
culture:
- en-US
Expand Down
89 changes: 88 additions & 1 deletion schemas/PSRule-language.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@
},
"spec": {
"type": "object",
"$ref": "#/definitions/moduleConfigSpec-v1"
"$ref": "#/definitions/moduleConfigSpec-v2"
}
},
"required": [
Expand Down Expand Up @@ -649,6 +649,93 @@
},
"additionalProperties": false
},
"moduleConfigSpec-v2": {
"type": "object",
"title": "Spec",
"description": "A specification for a module configuration.",
"properties": {
"binding": {
"$ref": "#/definitions/binding-option"
},
"capabilities": {
"type": "array",
"title": "Required capabilities",
"description": "Specifies a list of capabilities required by the configuration.",
"markdownDescription": "Specifies a list of capabilities required by the configuration. [See help](https://microsoft.github.io/PSRule/v3/concepts/PSRule/en-US/about_PSRule_Options/#capabilities)",
"items": {
"type": "string",
"enum": [
"api-v1",
"api-2025-01-01",
"powershell-language"
]
},
"uniqueItems": true
},
"configuration": {
"$ref": "#/definitions/configuration"
},
"convention": {
"$ref": "#/definitions/convention-option"
},
"output": {
"type": "object",
"title": "Output options",
"description": "Options that affect how output is generated.",
"properties": {
"culture": {
"type": "array",
"title": "Culture",
"description": "One or more cultures to use for generating output. When multiple cultures are specified, the first matching culture will be used. By default, the current PowerShell culture is used.",
"markdownDescription": "One or more cultures to use for generating output. When multiple cultures are specified, the first matching culture will be used. By default, the current PowerShell culture is used. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Options/#outputculture)",
"items": {
"type": "string",
"description": "A culture for generating output.",
"minLength": 2
},
"uniqueItems": true,
"defaultSnippets": [
{
"label": "en-AU",
"bodyText": [
"en-AU"
]
},
{
"label": "en-US",
"bodyText": [
"en-US"
]
},
{
"label": "en-GB",
"bodyText": [
"en-GB"
]
}
]
}
},
"additionalProperties": false
},
"rule": {
"type": "object",
"title": "Rule",
"description": "A filter for included or excluded rules.",
"properties": {
"baseline": {
"type": "string",
"title": "Baseline",
"description": "The name of a baseline to use.",
"markdownDescription": "The name of a baseline to use. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Options/#rulebaseline)",
"$ref": "#/definitions/resourceNameReference"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"resourceName": {
"type": "string",
"minLength": 3,
Expand Down
19 changes: 19 additions & 0 deletions schemas/PSRule-options.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,10 @@
}
]
},
"capabilities": {
"type": "array",
"$ref": "#/definitions/capabilities"
},
"configuration": {
"type": "object",
"oneOf": [
Expand Down Expand Up @@ -1203,6 +1207,21 @@
}
]
},
"capabilities": {
"type": "array",
"title": "Required capabilities",
"description": "Specifies a list of capabilities required by the configuration.",
"markdownDescription": "Specifies a list of capabilities required by the configuration. [See help](https://microsoft.github.io/PSRule/v3/concepts/PSRule/en-US/about_PSRule_Options/#capabilities)",
"items": {
"type": "string",
"enum": [
"api-v1",
"api-2025-01-01",
"powershell-language"
]
},
"uniqueItems": true
},
"rule-option": {
"type": "object",
"title": "Rule options",
Expand Down
46 changes: 46 additions & 0 deletions src/PSRule.Types/Converters/Json/CapabilityOptionJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Newtonsoft.Json;
using PSRule.Options;

namespace PSRule.Converters.Json;

/// <summary>
/// A JSON converter for <see cref="CapabilityOption"/>.
/// </summary>
public sealed class CapabilityOptionJsonConverter : JsonConverter<CapabilityOption>
{
/// <inheritdoc/>
public override CapabilityOption? ReadJson(JsonReader reader, Type objectType, CapabilityOption? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TryConsume(JsonToken.StartArray))
{
var items = new List<string>();
while (reader.TryConsume(JsonToken.String, out var value) && value is string s)
{
items.Add(s);
}

return new CapabilityOption
{
Items = [.. items]
};
}
return null;
}

/// <inheritdoc/>
public override void WriteJson(JsonWriter writer, CapabilityOption? value, JsonSerializer serializer)
{
if (value == null || value.Items == null)
return;

writer.WriteStartArray();
foreach (var item in value.Items)
{
writer.WriteValue(item);
}
writer.WriteEndArray();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
using System.Diagnostics;
using Newtonsoft.Json;
using PSRule.Definitions;
using PSRule.Pipeline;
using PSRule.Resources;

namespace PSRule;
namespace PSRule.Converters.Json;

internal static class JsonReaderExtensions
{
Expand All @@ -23,10 +22,10 @@ public static bool TryLineInfo(this JsonReader reader, out int lineNumber, out i
return true;
}

public static bool GetSourceExtent(this JsonReader reader, ISourceFile file, out ISourceExtent extent)
public static bool GetSourceExtent(this JsonReader reader, ISourceFile file, out ISourceExtent? extent)
{
extent = null;
if (file == null || !TryLineInfo(reader, out var lineNumber, out var linePosition))
if (file == null || !reader.TryLineInfo(out var lineNumber, out var linePosition))
return false;

extent = new SourceExtent(file, lineNumber, linePosition);
Expand All @@ -44,7 +43,7 @@ public static bool TryConsume(this JsonReader reader, JsonToken token)
}

[DebuggerStepThrough]
public static bool TryConsume(this JsonReader reader, JsonToken token, out object value)
public static bool TryConsume(this JsonReader reader, JsonToken token, out object? value)
{
value = null;
if (reader.TokenType != token)
Expand All @@ -59,7 +58,7 @@ public static bool TryConsume(this JsonReader reader, JsonToken token, out objec
public static void Consume(this JsonReader reader, JsonToken token)
{
if (reader.TokenType != token)
throw new PipelineSerializationException(PSRuleResources.ReadJsonFailedExpectedToken, Enum.GetName(typeof(JsonToken), reader.TokenType), reader.Path);
throw new PipelineSerializationException(Messages.ReadJsonFailedExpectedToken, Enum.GetName(typeof(JsonToken), reader.TokenType), reader.Path);

reader.Read();
}
Expand Down
Loading

0 comments on commit 46c1a14

Please sign in to comment.