Fix CLI module versions and version pre-release handling #2557 (#2558)

This commit is contained in:
Bernie White 2024-09-24 03:19:15 +10:00 коммит произвёл GitHub
Родитель 6536998cee
Коммит 0570e0e02b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
27 изменённых файлов: 677 добавлений и 279 удалений

Просмотреть файл

@ -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}

Просмотреть файл

@ -27,6 +27,17 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers
## Unreleased
What's changed since pre-release v3.0.0-B0267:
- 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:

Просмотреть файл

@ -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')
}

Просмотреть файл

@ -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

Просмотреть файл

@ -98,7 +98,7 @@ public sealed class ModuleCommand
// 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.");
@ -224,13 +224,13 @@ public sealed class ModuleCommand
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: false);
// 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;
@ -316,7 +316,7 @@ public sealed class ModuleCommand
foreach (var kv in file.Modules)
{
// 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: false);
// Find the ideal version.
var idealVersion = await FindVersionAsync(kv.Key, moduleConstraint, null, null, cancellationToken);
@ -408,7 +408,7 @@ public sealed class ModuleCommand
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 +452,8 @@ public sealed class ModuleCommand
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;

Просмотреть файл

@ -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": "../../"
}
}
}
}

Просмотреть файл

@ -5,17 +5,6 @@ using System.Diagnostics;
namespace PSRule.Data;
/// <summary>
/// An date version constraint.
/// </summary>
public interface IDateVersionConstraint
{
/// <summary>
/// Determines if the date version meets the requirments of the constraint.
/// </summary>
bool Equals(DateVersion.Version version);
}
/// <summary>
/// A helper for comparing date version strings.
/// An date version is represented as YYYY-MM-DD-prerelease.
@ -75,18 +64,37 @@ public static class DateVersion
public sealed class VersionConstraint : IDateVersionConstraint
{
private List<ConstraintExpression>? _Constraints;
private readonly string _Value;
private readonly bool _IncludePrerelease;
/// <summary>
/// A version constraint that accepts any version including pre-releases.
/// </summary>
public static readonly VersionConstraint Any = new(string.Empty, includePrerelease: true);
/// <summary>
/// A version constraint that accepts any stable version.
/// </summary>
public static readonly VersionConstraint AnyStable = new(string.Empty, includePrerelease: false);
internal VersionConstraint(string value, bool includePrerelease)
{
_Value = value;
_IncludePrerelease = includePrerelease;
}
/// <inheritdoc/>
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 class DateVersion
return TryParseConstraint(value, out constraint);
}
public bool Equals(Version version)
/// <inheritdoc/>
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 @@ public static class DateVersion
/// <summary>
/// An date version.
/// </summary>
[DebuggerDisplay("{_VersionString}")]
public sealed class Version : IComparable<Version>, IEquatable<Version>
{
private readonly string _VersionString;
/// <summary>
/// The year part of the version.
/// </summary>
@ -305,12 +317,19 @@ public static class DateVersion
Month = month;
Day = day;
Prerelease = prerelease;
_VersionString = GetVersionString();
}
/// <summary>
/// Determines if the version is stable or a pre-release.
/// </summary>
public bool Stable => Prerelease == null || Prerelease.Stable;
/// <inheritdoc/>
public override string ToString()
{
return string.Concat(Year, DASH, Month, DASH, Day);
return _VersionString;
}
/// <inheritdoc/>
@ -338,7 +357,7 @@ public static class DateVersion
/// </summary>
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 static class DateVersion
/// </summary>
public int CompareTo(Version other)
{
if (other == null)
if (other is null)
return 1;
if (Year != other.Year)
@ -369,13 +388,34 @@ public static class DateVersion
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 @@ public static class DateVersion
/// </summary>
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;

Просмотреть файл

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace PSRule.Data;
/// <summary>
/// An date version constraint.
/// </summary>
public interface IDateVersionConstraint
{
/// <summary>
/// Determines if the date version meets the requirments of the constraint.
/// </summary>
bool Accepts(DateVersion.Version? version);
}

Просмотреть файл

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace PSRule.Data;
/// <summary>
/// A semantic version constraint.
/// </summary>
public interface ISemanticVersionConstraint
{
/// <summary>
/// Determines if the semantic version meets the requirments of the constraint.
/// </summary>
bool Accepts(SemanticVersion.Version? version);
}

Просмотреть файл

@ -1,31 +1,44 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Diagnostics;
namespace PSRule.Data;
/// <summary>
/// A version constraint for a PSRule module.
/// </summary>
public sealed class ModuleConstraint
/// <param name="module">The name of the module.</param>
/// <param name="constraint">The version constraint of the module.</param>
/// <exception cref="ArgumentNullException">Both <paramref name="module"/> and <paramref name="constraint"/> must not be null or empty.</exception>
[DebuggerDisplay("{Module}")]
public sealed class ModuleConstraint(string module, ISemanticVersionConstraint constraint) : ISemanticVersionConstraint
{
/// <summary>
/// Create a version constraint for a PSRule module.
/// </summary>
/// <param name="module">The name of the module.</param>
/// <param name="constraint">The version constraint of the module.</param>
public ModuleConstraint(string module, ISemanticVersionConstraint constraint)
{
Module = module;
Constraint = constraint;
}
/// <summary>
/// The name of the module.
/// </summary>
public string Module { get; }
public string Module { get; } = !string.IsNullOrEmpty(module) ? module : throw new ArgumentNullException(nameof(module));
/// <summary>
/// The version constraint of the module.
/// </summary>
public ISemanticVersionConstraint Constraint { get; }
public ISemanticVersionConstraint Constraint { get; } = constraint ?? throw new ArgumentNullException(nameof(constraint));
/// <inheritdoc/>
public bool Accepts(SemanticVersion.Version? version) => Constraint.Accepts(version);
/// <summary>
/// Get a constraint that accepts any version of the specified module.
/// </summary>
/// <param name="module"></param>
/// <param name="includePrerelease">Determines if pre-releases are accepted or only stable versions.</param>
/// <returns>A <see cref="ModuleConstraint"/>.</returns>
public static ModuleConstraint Any(string module, bool includePrerelease = false)
{
return new ModuleConstraint
(
module,
includePrerelease ? SemanticVersion.VersionConstraint.Any : SemanticVersion.VersionConstraint.AnyStable
);
}
}

Просмотреть файл

@ -5,17 +5,6 @@ using System.Diagnostics;
namespace PSRule.Data;
/// <summary>
/// A semantic version constraint.
/// </summary>
public interface ISemanticVersionConstraint
{
/// <summary>
/// Determines if the semantic version meets the requirments of the constraint.
/// </summary>
bool Equals(SemanticVersion.Version version);
}
/// <summary>
/// A helper for comparing semantic version strings.
/// </summary>
@ -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 static class SemanticVersion
{
private List<ConstraintExpression>? _Constraints;
private readonly string _Value;
private readonly bool _IncludePrerelease;
internal VersionConstraint(string value)
/// <summary>
/// A version constraint that accepts any version including pre-releases.
/// </summary>
public static readonly VersionConstraint Any = new(string.Empty, includePrerelease: true);
/// <summary>
/// A version constraint that accepts any stable version.
/// </summary>
public static readonly VersionConstraint AnyStable = new(string.Empty, includePrerelease: false);
internal VersionConstraint(string value, bool includePrerelease)
{
_Value = value;
_IncludePrerelease = includePrerelease;
}
/// <inheritdoc/>
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,20 +135,6 @@ public static class SemanticVersion
return false;
}
internal void Join(int major, int minor, int patch, PR prid, ComparisonOperator flag, JoinOperator join, bool includePrerelease)
{
_Constraints ??= new List<ConstraintExpression>();
_Constraints.Add(new ConstraintExpression(
major,
minor,
patch,
prid,
flag,
join == JoinOperator.None ? JoinOperator.Or : join,
includePrerelease
));
}
/// <inheritdoc/>
public override string ToString()
{
@ -158,6 +146,20 @@ public static class SemanticVersion
{
return _Value.GetHashCode();
}
internal void Join(int major, int minor, int patch, PR prid, ComparisonOperator flag, JoinOperator join, bool includePrerelease)
{
_Constraints ??= [];
_Constraints.Add(new ConstraintExpression(
major,
minor,
patch,
prid,
flag,
join == JoinOperator.None ? JoinOperator.Or : join,
includePrerelease
));
}
}
[DebuggerDisplay("{_Major}.{_Minor}.{_Patch}")]
@ -190,17 +192,18 @@ public static class SemanticVersion
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)
/// <inheritdoc/>
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,11 @@ public static class SemanticVersion
/// <summary>
/// A semantic version.
/// </summary>
[DebuggerDisplay("{_VersionString}")]
public sealed class Version : IComparable<Version>, IEquatable<Version>
{
private readonly string _VersionString;
/// <summary>
/// The major part of the version.
/// </summary>
@ -374,8 +380,15 @@ public static class SemanticVersion
Patch = patch;
Prerelease = prerelease;
Build = build;
_VersionString = GetVersionString();
}
/// <summary>
/// Determines if the version is stable or a pre-release.
/// </summary>
public bool Stable => Prerelease == null || Prerelease.Stable;
/// <summary>
/// Try to parse a semantic version from a string.
/// </summary>
@ -387,7 +400,7 @@ public static class SemanticVersion
/// <inheritdoc/>
public override string ToString()
{
return string.Concat(Major, '.', Minor, '.', Patch);
return _VersionString;
}
/// <inheritdoc/>
@ -432,7 +445,7 @@ public static class SemanticVersion
/// </summary>
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 +465,7 @@ public static class SemanticVersion
/// </summary>
public int CompareTo(Version? other)
{
if (other == null)
if (other is null)
return 1;
if (Major != other.Major)
@ -461,7 +474,37 @@ public static class SemanticVersion
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;
}
private string GetVersionString()
{
var count = 5 + (Prerelease != null && !Prerelease.Stable ? 2 : 0) + (Build != null && Build.Length > 0 ? 2 : 0);
var parts = new object[count];
parts[0] = Major;
parts[1] = DOT;
parts[2] = Minor;
parts[3] = DOT;
parts[4] = Patch;
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 +512,10 @@ public static class SemanticVersion
/// A semantic version pre-release identifier.
/// </summary>
[DebuggerDisplay("{Value}")]
public sealed class PR
public sealed class PR : IComparable<PR>, IEquatable<PR>
{
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 +544,16 @@ public static class SemanticVersion
/// <summary>
/// Compare the pre-release identifer to another pre-release identifier.
/// </summary>
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 +586,21 @@ public static class SemanticVersion
return left.Length > right.Length ? 1 : -1;
}
/// <inheritdoc/>
public bool Equals(PR? other)
{
if (other is null)
return Stable;
return Stable && other.Stable ||
Value.Equals(other.Value);
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is PR prerelease && Value.Equals(prerelease.Value);
return obj is PR other && Equals(other);
}
/// <inheritdoc/>
@ -560,6 +614,18 @@ public static class SemanticVersion
{
return Value.ToString();
}
/// <summary>
/// Compare two <see cref="PR"/> instances.
/// </summary>
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 +718,7 @@ public static class SemanticVersion
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 +832,7 @@ public static class SemanticVersion
[DebuggerStepThrough()]
private static bool IsSeparator(char c)
{
return c == SEPARATOR;
return c == DOT;
}
[DebuggerStepThrough()]
@ -782,7 +848,7 @@ public static class SemanticVersion
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 +885,7 @@ public static class SemanticVersion
/// </summary>
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;

Просмотреть файл

@ -1128,8 +1128,8 @@ internal sealed class LanguageExpressions
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 sealed class LanguageExpressions
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();
}

Просмотреть файл

@ -160,7 +160,7 @@ internal abstract class PipelineBuilderBase : IPipelineBuilder
{
return SemanticVersion.TryParseVersion(moduleVersion, out var version) &&
SemanticVersion.TryParseConstraint(requiredVersion, out var constraint) &&
constraint.Equals(version);
constraint.Accepts(version);
}
/// <summary>

4
src/PSRule/Resources/ReasonStrings.Designer.cs сгенерированный
Просмотреть файл

@ -711,9 +711,9 @@ namespace PSRule.Resources {
/// <summary>
/// Looks up a localized string similar to The version &apos;{0}&apos; does not match the constraint &apos;{1}&apos;..
/// </summary>
internal static string VersionContraint {
internal static string VersionConstraint {
get {
return ResourceManager.GetString("VersionContraint", resourceCulture);
return ResourceManager.GetString("VersionConstraint", resourceCulture);
}
}

Просмотреть файл

@ -307,7 +307,7 @@
<data name="Version" xml:space="preserve">
<value>The field value '{0}' is not a version string.</value>
</data>
<data name="VersionContraint" xml:space="preserve">
<data name="VersionConstraint" xml:space="preserve">
<value>The version '{0}' does not match the constraint '{1}'.</value>
</data>
<data name="Within" xml:space="preserve">

Просмотреть файл

@ -859,7 +859,7 @@ public sealed class Assert
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();
}
/// <summary>
@ -883,7 +883,7 @@ public sealed class Assert
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();
}
/// <summary>

Просмотреть файл

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using PSRule.Data;
namespace PSRule.CommandLine;
/// <summary>
/// Tests for <see cref="ModuleConstraint"/>.
/// </summary>
public sealed class ModuleConstraintTests
{
}

Просмотреть файл

@ -21,4 +21,8 @@
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\PSRule.CommandLine\PSRule.CommandLine.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -87,67 +87,67 @@ public sealed class DateVersionTests
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));
}
/// <summary>

Просмотреть файл

@ -14,13 +14,6 @@ using PSRule.Runtime;
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 sealed class SelectorTests
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);
@ -1573,6 +1566,15 @@ public sealed class SelectorTests
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]
@ -1614,6 +1616,15 @@ public sealed class SelectorTests
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]

Просмотреть файл

@ -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",

Просмотреть файл

@ -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

Просмотреть файл

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.PowerShell;
using PSRule.Data;
namespace PSRule;
@ -97,96 +98,96 @@ public sealed class SemanticVersionTests
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));
}
/// <summary>
@ -216,4 +217,15 @@ public sealed class SemanticVersionTests
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());
}
}

Просмотреть файл

@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace PSRule;
internal enum TestEnumValue
{
None = 0,
All = 1
}

Просмотреть файл

@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace PSRule.Data;
/// <summary>
/// Tests for <see cref="ModuleConstraint"/>.
/// </summary>
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));
}
}

Просмотреть файл

@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
global using Xunit;

Просмотреть файл

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>PSRule</RootNamespace>
<ProjectGuid>{8860178f-4b4a-4e28-8cc3-85dfe2a2fe4b}</ProjectGuid>
<LangVersion>12.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit" Version="2.9.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\PSRule.Types\PSRule.Types.csproj" />
</ItemGroup>
</Project>