Updates for module add upgrade and restore CLI commands #2550 #2551 (#2559)

This commit is contained in:
Bernie White 2024-09-25 03:12:51 +10:00 коммит произвёл GitHub
Родитель 56a21369f9
Коммит 6c21b047ab
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
15 изменённых файлов: 217 добавлений и 100 удалений

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

@ -29,6 +29,15 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers
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)

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

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

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

@ -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": [

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

@ -17,7 +17,6 @@ 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 sealed class ModuleCommand
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 sealed class ModuleCommand
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);
@ -109,7 +110,7 @@ public sealed class ModuleCommand
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 sealed class ModuleCommand
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 sealed class ModuleCommand
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,13 +217,13 @@ public sealed class ModuleCommand
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 : ModuleConstraint.Any(module, includePrerelease: false);
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;
@ -250,7 +249,8 @@ public sealed class ModuleCommand
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 sealed class ModuleCommand
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 sealed class ModuleCommand
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<string>(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 : ModuleConstraint.Any(kv.Key, includePrerelease: false);
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 sealed class ModuleCommand
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;
}
@ -461,24 +463,26 @@ public sealed class ModuleCommand
return result;
}
private static async Task InstallVersionAsync([DisallowNull] ClientContext context, [DisallowNull] string name, [DisallowNull] string version, CancellationToken cancellationToken)
private static async Task<SemanticVersion.Version?> 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 @@ public sealed class ModuleCommand
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 @@ public sealed class ModuleCommand
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<FindPackageByIdResource> GetSourceRepositoryAsync()
@ -509,9 +522,9 @@ public sealed class ModuleCommand
return await repository.GetResourceAsync<FindPackageByIdResource>();
}
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 @@ public sealed class ModuleCommand
string.Equals(name, "_rels/.rels", StringComparison.OrdinalIgnoreCase);
}
private static PowerShell CreatePowerShell()
{
return PowerShell.Create();
}
#endregion Helper methods
}

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

@ -4,32 +4,37 @@
namespace PSRule.CommandLine.Models;
/// <summary>
///
/// Options for the module command.
/// </summary>
public sealed class ModuleOptions
{
/// <summary>
///
/// A specific path to use for the operation.
/// </summary>
public string[]? Path { get; set; }
/// <summary>
///
/// The name of any specified modules.
/// </summary>
public string[]? Module { get; set; }
/// <summary>
///
/// Determines if the module is overridden if it already exists.
/// </summary>
public bool Force { get; set; }
/// <summary>
///
/// The target module version.
/// </summary>
public string? Version { get; set; }
/// <summary>
///
/// Determines if verification that the module exists is skipped.
/// </summary>
public bool SkipVerification { get; set; }
/// <summary>
/// Accept pre-release versions in addition to stable module versions.
/// </summary>
public bool Prerelease { get; set; }
}

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

@ -29,6 +29,7 @@ internal sealed class ClientBuilder
private readonly Option<string> _Module_Add_Version;
private readonly Option<bool> _Module_Add_Force;
private readonly Option<bool> _Module_Add_SkipVerification;
private readonly Option<bool> _Module_Prerelease;
private readonly Option<string[]> _Global_Path;
private readonly Option<DirectoryInfo> _Run_OutputPath;
private readonly Option<string> _Run_OutputFormat;
@ -43,70 +44,74 @@ internal sealed class ClientBuilder
// Global options.
_Global_Option = new Option<string>(
new string[] { "--option" },
["--option"],
getDefaultValue: () => "ps-rule.yaml",
description: CmdStrings.Global_Option_Description
);
_Global_Verbose = new Option<bool>(
new string[] { "--verbose" },
["--verbose"],
description: CmdStrings.Global_Verbose_Description
);
_Global_Debug = new Option<bool>(
new string[] { "--debug" },
["--debug"],
description: CmdStrings.Global_Debug_Description
);
_Global_Path = new Option<string[]>(
new string[] { "-p", "--path" },
["-p", "--path"],
description: CmdStrings.Global_Path_Description
);
// Options for the run command.
_Run_OutputPath = new Option<DirectoryInfo>(
new string[] { "--output-path" },
["--output-path"],
description: CmdStrings.Run_OutputPath_Description
);
_Run_OutputFormat = new Option<string>(
new string[] { "-o", "--output" },
["-o", "--output"],
description: CmdStrings.Run_OutputFormat_Description
);
_Run_InputPath = new Option<string[]>(
new string[] { "-f", "--input-path" },
["-f", "--input-path"],
description: CmdStrings.Run_InputPath_Description
);
_Run_Module = new Option<string[]>(
new string[] { "-m", "--module" },
["-m", "--module"],
description: CmdStrings.Run_Module_Description
);
_Run_Baseline = new Option<string>(
new string[] { "--baseline" },
["--baseline"],
description: CmdStrings.Run_Baseline_Description
);
_Run_Outcome = new Option<string[]>(
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<bool>(
new string[] { ARG_FORCE },
[ARG_FORCE],
description: CmdStrings.Module_Init_Force_Description
);
_Module_Add_Version = new Option<string>
(
new string[] { "--version" },
["--version"],
description: CmdStrings.Module_Add_Version_Description
);
_Module_Add_Force = new Option<bool>(
new string[] { ARG_FORCE },
[ARG_FORCE],
description: CmdStrings.Module_Add_Force_Description
);
_Module_Add_SkipVerification = new Option<bool>(
new string[] { "--skip-verification" },
["--skip-verification"],
description: CmdStrings.Module_Add_SkipVerification_Description
);
_Module_Prerelease = new Option<bool>(
["--prerelease"],
description: CmdStrings.Module_Prerelease_Description
);
_Module_Restore_Force = new Option<bool>(
new string[] { ARG_FORCE },
[ARG_FORCE],
description: CmdStrings.Module_Restore_Force_Description
);
@ -165,12 +170,12 @@ internal sealed class ClientBuilder
{
var cmd = new Command("module", CmdStrings.Module_Description);
var moduleArg = new Argument<string[]>
var requiredModuleArg = new Argument<string[]>
(
"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 @@ internal sealed class ClientBuilder
"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 @@ internal sealed class ClientBuilder
"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 @@ internal sealed class ClientBuilder
"upgrade",
CmdStrings.Module_Upgrade_Description
);
var optionalModuleArg = new Argument<string[]>
(
"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);

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

@ -178,11 +178,20 @@ namespace PSRule.Tool.Resources {
}
/// <summary>
/// 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..
/// </summary>
internal static string Module_Module_Description {
internal static string Module_OptionalModule_Description {
get {
return ResourceManager.GetString("Module_Module_Description", resourceCulture);
return ResourceManager.GetString("Module_OptionalModule_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Accept pre-release versions in addition to stable module versions..
/// </summary>
internal static string Module_Prerelease_Description {
get {
return ResourceManager.GetString("Module_Prerelease_Description", resourceCulture);
}
}
@ -195,6 +204,15 @@ namespace PSRule.Tool.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to The name of one or more modules..
/// </summary>
internal static string Module_RequiredModule_Description {
get {
return ResourceManager.GetString("Module_RequiredModule_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Restore from the module lock file and configured options..
/// </summary>

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

@ -141,9 +141,12 @@
<data name="Module_Description" xml:space="preserve">
<value>Manage or restore modules tracked by the module lock file and configured options.</value>
</data>
<data name="Module_Module_Description" xml:space="preserve">
<data name="Module_RequiredModule_Description" xml:space="preserve">
<value>The name of one or more modules.</value>
</data>
<data name="Module_OptionalModule_Description" xml:space="preserve">
<value>Optionally specifies one or more modules to apply to.</value>
</data>
<data name="Module_Remove_Description" xml:space="preserve">
<value>Remove one or more modules from the lock file.</value>
</data>
@ -192,4 +195,7 @@
<data name="Run_OutputPath_Description" xml:space="preserve">
<value>Specifies a path to write results to.</value>
</data>
</root>
<data name="Module_Prerelease_Description" xml:space="preserve">
<value>Accept pre-release versions in addition to stable module versions.</value>
</data>
</root>

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

@ -346,7 +346,8 @@ public static class SemanticVersion
[DebuggerDisplay("{_VersionString}")]
public sealed class Version : IComparable<Version>, IEquatable<Version>
{
private readonly string _VersionString;
private string? _VersionString;
private string? _ShortVersionString;
/// <summary>
/// The major part of the version.
@ -380,8 +381,6 @@ public static class SemanticVersion
Patch = patch;
Prerelease = prerelease;
Build = build;
_VersionString = GetVersionString();
}
/// <summary>
@ -397,10 +396,20 @@ public static class SemanticVersion
return TryParseVersion(value, out version);
}
/// <inheritdoc/>
/// <summary>
/// Get the version as a string.
/// </summary>
public override string ToString()
{
return _VersionString;
return _VersionString ??= GetVersionString(simple: false);
}
/// <summary>
/// Get the version as a string returning only the major.minor.patch part of the version.
/// </summary>
public string ToShortString()
{
return _ShortVersionString ??= GetVersionString(simple: true);
}
/// <inheritdoc/>
@ -480,10 +489,14 @@ public static class SemanticVersion
return Prerelease != other.Prerelease ? PR.Compare(Prerelease, other.Prerelease) : 0;
}
private string GetVersionString()
/// <summary>
/// Returns a version string.
/// </summary>
/// <param name="simple">When <c>true</c>, only return the major.minor.patch version.</param>
private string GetVersionString(bool simple = false)
{
var count = 5 + (Prerelease != null && !Prerelease.Stable ? 2 : 0) + (Build != null && Build.Length > 0 ? 2 : 0);
var parts = new object[count];
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;
@ -491,19 +504,21 @@ public static class SemanticVersion
parts[3] = DOT;
parts[4] = Patch;
var next = 5;
if (Prerelease != null && !Prerelease.Stable)
if (size > 5)
{
parts[next++] = DASH;
parts[next++] = Prerelease.Value;
}
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;
if (Build != null && Build.Length > 0)
{
parts[next++] = PLUS;
parts[next++] = Build;
}
}
return string.Concat(parts);
}
}

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

@ -6,3 +6,4 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.PSRule.Core")]
[assembly: InternalsVisibleTo("Microsoft.PSRule.Tool")]
[assembly: InternalsVisibleTo("PSRule.Tests")]
[assembly: InternalsVisibleTo("PSRule.Types.Tests")]

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

@ -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
/// </summary>
[JsonProperty("version", NullValueHandling = NullValueHandling.Include)]
public SemanticVersion.Version Version { get; set; }
/// <summary>
/// Accept pre-release versions in addition to stable module versions.
/// </summary>
[DefaultValue(null), JsonProperty("includePrerelease", NullValueHandling = NullValueHandling.Ignore)]
public bool? IncludePrerelease { get; set; }
}

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

@ -1,13 +0,0 @@
// 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
{
}

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

@ -14,7 +14,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit" Version="2.9.0" />
<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>

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

@ -11,7 +11,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit" Version="2.9.0" />
<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>

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

@ -1,10 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.PowerShell;
using PSRule.Data;
namespace PSRule;
namespace PSRule.Data;
/// <summary>
/// Tests for semantic version comparison.
@ -18,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);
@ -48,11 +45,11 @@ public sealed class SemanticVersionTests
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);
@ -205,7 +202,7 @@ public sealed class SemanticVersionTests
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);
@ -226,6 +223,17 @@ public sealed class SemanticVersionTests
public void ToString_WhenValid_ShouldReturnString(string version)
{
Assert.True(SemanticVersion.TryParseVersion(version, out var actual));
Assert.Equal(version, actual.ToString());
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());
}
}