Skip to content

Commit

Permalink
Added badges API for rule results microsoft#623
Browse files Browse the repository at this point in the history
  • Loading branch information
BernieWhite committed Aug 25, 2021
1 parent a47278e commit 6446fd7
Show file tree
Hide file tree
Showing 27 changed files with 1,178 additions and 37 deletions.
46 changes: 29 additions & 17 deletions PSRule.sln
@@ -1,32 +1,44 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.106
# Visual Studio Version 16
VisualStudioVersion = 16.0.31613.86
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule", "src\PSRule\PSRule.csproj", "{ACAFD790-980B-4B64-912F-9BAD91DFF749}"
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", "{B2764210-8B4B-4554-A000-5F3E3D9ECDD5}"
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", "{FAD69F5B-461E-44FF-8438-1F7BF0710660}"
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", "{4921E88E-968B-4677-95DE-B1D6C5AC620E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSRule.BuildTool", "src\PSRule.BuildTool\PSRule.BuildTool.csproj", "{20DDCC65-8A9A-4BDC-91EC-C3BE6F32E52E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ACAFD790-980B-4B64-912F-9BAD91DFF749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ACAFD790-980B-4B64-912F-9BAD91DFF749}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACAFD790-980B-4B64-912F-9BAD91DFF749}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACAFD790-980B-4B64-912F-9BAD91DFF749}.Release|Any CPU.Build.0 = Release|Any CPU
{B2764210-8B4B-4554-A000-5F3E3D9ECDD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2764210-8B4B-4554-A000-5F3E3D9ECDD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2764210-8B4B-4554-A000-5F3E3D9ECDD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2764210-8B4B-4554-A000-5F3E3D9ECDD5}.Release|Any CPU.Build.0 = Release|Any CPU
{FAD69F5B-461E-44FF-8438-1F7BF0710660}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FAD69F5B-461E-44FF-8438-1F7BF0710660}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FAD69F5B-461E-44FF-8438-1F7BF0710660}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FAD69F5B-461E-44FF-8438-1F7BF0710660}.Release|Any CPU.Build.0 = Release|Any CPU
{0130215D-58EB-4887-B6FA-31ED02500569}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0130215D-58EB-4887-B6FA-31ED02500569}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0130215D-58EB-4887-B6FA-31ED02500569}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0130215D-58EB-4887-B6FA-31ED02500569}.Release|Any CPU.Build.0 = Release|Any CPU
{3EC0912F-BFC7-4B53-A1A1-0BA993C6282E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3EC0912F-BFC7-4B53-A1A1-0BA993C6282E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3EC0912F-BFC7-4B53-A1A1-0BA993C6282E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3EC0912F-BFC7-4B53-A1A1-0BA993C6282E}.Release|Any CPU.Build.0 = Release|Any CPU
{D3488CE2-779F-4474-B38A-F894A4B689F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D3488CE2-779F-4474-B38A-F894A4B689F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D3488CE2-779F-4474-B38A-F894A4B689F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D3488CE2-779F-4474-B38A-F894A4B689F7}.Release|Any CPU.Build.0 = Release|Any CPU
{4921E88E-968B-4677-95DE-B1D6C5AC620E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4921E88E-968B-4677-95DE-B1D6C5AC620E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4921E88E-968B-4677-95DE-B1D6C5AC620E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4921E88E-968B-4677-95DE-B1D6C5AC620E}.Release|Any CPU.Build.0 = Release|Any CPU
{20DDCC65-8A9A-4BDC-91EC-C3BE6F32E52E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20DDCC65-8A9A-4BDC-91EC-C3BE6F32E52E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20DDCC65-8A9A-4BDC-91EC-C3BE6F32E52E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20DDCC65-8A9A-4BDC-91EC-C3BE6F32E52E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -294,6 +294,7 @@ The following conceptual topics exist in the `PSRule` module:
- [StartsWith](docs/concepts/PSRule/en-US/about_PSRule_Assert.md#startswith)
- [Version](docs/concepts/PSRule/en-US/about_PSRule_Assert.md#version)
- [WithinPath](docs/concepts/PSRule/en-US/about_PSRule_Assert.md#withinpath)
- [Badges](docs/concepts/PSRule/en-US/about_PSRule_Badges.md)
- [Baselines](docs/concepts/PSRule/en-US/about_PSRule_Baseline.md)
- [Baseline specs](docs/concepts/PSRule/en-US/about_PSRule_Baseline.md#baseline-specs)
- [Baseline scopes](docs/concepts/PSRule/en-US/about_PSRule_Baseline.md#baseline-scopes)
Expand Down
8 changes: 8 additions & 0 deletions docs/CHANGELOG-v1.md
Expand Up @@ -10,6 +10,13 @@ See [upgrade notes][upgrade-notes] for helpful information when upgrading from p

## Unreleased

What's changed since pre-release v1.7.0-B2108021:

- Engine features:
- Added support for generating badges from rule results. [#623](https://github.com/microsoft/PSRule/issues/623)
- Standard or custom badges can be generated using a convention and the badge API.
- See [about_PSRule_Badges] for details.

## v1.7.0-B2108021 (pre-release)

What's changed since pre-release v1.7.0-B2108016:
Expand Down Expand Up @@ -500,3 +507,4 @@ What's changed since v0.22.0:
[about_PSRule_Conventions]: concepts/PSRule/en-US/about_PSRule_Conventions.md
[about_PSRule_Selectors]: concepts/PSRule/en-US/about_PSRule_Selectors.md
[about_PSRule_Rules]: concepts/PSRule/en-US/about_PSRule_Rules.md
[about_PSRule_Badges]: concepts/PSRule/en-US/about_PSRule_Badges.md
4 changes: 4 additions & 0 deletions docs/commands/PSRule/en-US/toc.yml
Expand Up @@ -39,6 +39,10 @@
href: ../../../concepts/PSRule/en-US/about_PSRule_Baseline.md
topicHref: ../../../concepts/PSRule/en-US/about_PSRule_Baseline.md

- name: Badges
href: ../../../concepts/PSRule/en-US/about_PSRule_Badges.md
topicHref: ../../../concepts/PSRule/en-US/about_PSRule_Badges.md

- name: Conventions
href: ../../../concepts/PSRule/en-US/about_PSRule_Conventions.md
topicHref: ../../../concepts/PSRule/en-US/about_PSRule_Conventions.md
Expand Down
92 changes: 92 additions & 0 deletions docs/concepts/PSRule/en-US/about_PSRule_Badges.md
@@ -0,0 +1,92 @@
# PSRule_Badges

## about_PSRule_Badges

## SHORT DESCRIPTION

Describes using the badge API with PSRule.

## LONG DESCRIPTION

PSRule executes rules to validate an object from input.
When processing input it may be necessary to perform custom actions before or after rules execute.
Conventions provide an extensibility point that can be shipped with or external to standard rules.
The badge API can be used to create badges within a convention.

### Using the API

PSRule provides the `$PSRule` built-in variable that exposes the badge API.
By using the `$PSRule.Badges.Create` method you can create a standard or custom badge.

The create method provides the following overloads:

```csharp
// Create a badge for the worst case of an analyzed object.
IBadge Create(InvokeResult result);

// Create a badge for the worst case of all analyzed objects.
IBadge Create(IEnumerable<InvokeResult> result);

// Create a custom badge.
IBadge Create(string title, BadgeType type, string label);
```

A badge once created can be read as a string or written to disk with the following methods:

```csharp
// Get the badge as SVG text content.
string ToSvg();

// Write the SVG badge content directly to disk.
void ToFile(string path);
```

### Defining conventions

To define a convention, add a `Export-PSRuleConvention` block within a `.Rule.ps1` file.
The `.Rule.ps1` must be in an included path or module with `-Path` or `-Module`.

The `Export-PSRuleConvention` block works similar to the `Rule` block.
Each convention must have a unique name.
Currently the badge API support creating badges in the `-End` block.

For example:

```powershell
# Synopsis: A convention that generates a badge for an aggregate result.
Export-PSRuleConvention 'Local.Aggregate' -End {
$PSRule.Badges.Create($PSRule.Output).ToFile('out/badges/aggregate.svg');
}
```

```powershell
# Synopsis: A convention that generates a custom badge.
Export-PSRuleConvention 'Local.CustomBadge' -End {
$PSRule.Badges.Create('PSRule', [PSRule.Badges.BadgeType]::Success, 'OK').ToFile('out/badges/custom.svg');
}
```

### Using conventions

A convention can be included by using the `-Convention` parameter when executing a PSRule cmdlet.
Alternatively, conventions can be included with options.
To use a convention specify the name of the convention by name.
For example:

```powershell
Invoke-PSRule -Convention 'Local.Aggregate';
```

## NOTE

An online version of this document is available at https://microsoft.github.io/PSRule/concepts/PSRule/en-US/about_PSRule_Badges.md.

## SEE ALSO

- [Invoke-PSRule](https://microsoft.github.io/PSRule/commands/PSRule/en-US/Invoke-PSRule.html)

## KEYWORDS

- Badges
- Conventions
- PSRule
77 changes: 77 additions & 0 deletions src/PSRule.Badges/BadgeResources.cs
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Newtonsoft.Json;
using System;
using System.IO;
using System.Reflection;

namespace PSRule.Badges
{
/// <summary>
/// A helper class for working with badge resources.
/// </summary>
internal static class BadgeResources
{
private const string DEFAULT_CULTURE_RESOURCE = "PSRule.Badges.Resources.en.json";

private static char[] _Char;
private static double[] _Width;

/// <summary>
/// Load pre-calculated widths for characters.
/// </summary>
private static void Load()
{
if (_Char != null && _Width != null)
return;

var assembly = Assembly.GetExecutingAssembly();
using (var stream = assembly.GetManifestResourceStream(DEFAULT_CULTURE_RESOURCE))
{
using (var reader = new StreamReader(stream))
{
var json = reader.ReadToEnd();
var d = JsonConvert.DeserializeObject<object[][]>(json);
_Char = new char[d.Length];
_Width = new double[d.Length];
for (var i = 0; i < d.Length; i++)
{
_Char[i] = Convert.ToChar(d[i][0]);
_Width[i] = Convert.ToDouble(d[i][1]);
}
}
}
}

/// <summary>
/// Get the width in pixels for a character.
/// </summary>
private static double GetWidth(char c)
{
Load();
return Find(c);
}

/// <summary>
/// Find the width in pixels for a character.
/// </summary>
private static double Find(char c)
{
var index = Array.BinarySearch(_Char, c);
if (index >= 0)
return _Width[index];

return 0d;
}

public static double Measure(string s)
{
var length = 0d;
for (var i = 0; i < s.Length; i++)
length += GetWidth(s[i]);

return length;
}
}
}
46 changes: 46 additions & 0 deletions src/PSRule.Badges/PSRule.Badges.csproj
@@ -0,0 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<NeutralLanguage>en-US</NeutralLanguage>
<Authors>Bernie White</Authors>
<AssemblyTitle>PSRule.Badges</AssemblyTitle>
<RepositoryUrl>https://github.com/Microsoft/PSRule</RepositoryUrl>
<PackageLicenseExpression>https://github.com/Microsoft/PSRule/blob/main/LICENSE</PackageLicenseExpression>
<Version>0.0.1</Version>
<Copyright>Copyright (c) Microsoft Corporation. Licensed under the MIT License.</Copyright>
<Description>Contains resources for PSRule to work with badges.

This project uses GitHub Issues to track bugs and feature requests. See GitHub project for more information.</Description>
</PropertyGroup>

<PropertyGroup Condition="'$(OS)' == 'Windows_NT'">
<DefineConstants>Windows</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<None Remove="Resources\en.json" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Resources\en.json" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>

</Project>
7 changes: 7 additions & 0 deletions src/PSRule.Badges/Properties/AssemblyInfo.cs
@@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("PSRule")]
[assembly: InternalsVisibleTo("PSRule.Tests")]
1 change: 1 addition & 0 deletions src/PSRule.Badges/Resources/en.json
@@ -0,0 +1 @@
[[" ",3.3378601083100534E-07],["!",4.458007431030273],["/",5.145507431030273],["0",7.202636337280273],["1",7.202636337280273],["2",7.202636337280273],["3",7.202636337280273],["4",7.202636337280273],["5",7.202636337280273],["6",7.202636337280273],["7",7.202636337280273],["8",7.202636337280273],["9",7.202636337280273],["A",7.745116806030273],["B",7.766601181030273],["C",7.911620712280273],["D",8.728026962280273],["E",7.165038681030273],["F",6.509765243530273],["G",8.787108993530273],["H",8.513183212280273],["I",4.769530868530273],["J",5.150878524780273],["K",7.852538681030273],["L",6.305663681030273],["M",9.549804306030273],["N",8.475585556030273],["O",8.916015243530273],["P",6.832030868530273],["Q",8.916015243530273],["R",7.879394149780273],["S",7.745116806030273],["T",6.982421493530273],["U",8.292968368530273],["V",7.745116806030273],["W",11.204101181030273],["X",7.761230087280273],["Y",6.971679306030273],["Z",7.761230087280273],["a",6.805175399780273],["b",7.057616806030273],["c",5.902831649780273],["d",7.057616806030273],["e",6.751464462280273],["f",3.9853511810302735],["g",7.057616806030273],["h",7.170409774780273],["i",3.1098628997802735],["j",3.8994136810302735],["k",6.703124618530273],["l",3.1098628997802735],["m",11.021483993530273],["n",7.170409774780273],["o",6.874999618530273],["p",7.057616806030273],["q",7.057616806030273],["r",4.833983993530273],["s",5.902831649780273],["t",4.463378524780273],["u",7.170409774780273],["v",6.703124618530273],["w",9.270507431030273],["x",6.703124618530273],["y",6.703124618530273],["z",5.951171493530273]]

0 comments on commit 6446fd7

Please sign in to comment.