diff --git a/docs/changelog.md b/docs/changelog.md
index 76fe4ff8f2..1bba3466a0 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -42,6 +42,8 @@ What's changed since pre-release v3.0.0-B0453:
- Bug fixes:
- Fixed path handling issue string is missing the terminator with single quote in source paths by @juan-carlos-diaz.
[#2885](https://github.com/microsoft/PSRule/issues/2885)
+ - Fixed module path not found with pre-release by @BernieWhite.
+ [#2889](https://github.com/microsoft/PSRule/issues/2889)
## v3.0.0-B0453 (pre-release)
diff --git a/src/PSRule/Pipeline/SourcePipelineBuilder.cs b/src/PSRule/Pipeline/SourcePipelineBuilder.cs
index 32279dfe1b..67bc8aa858 100644
--- a/src/PSRule/Pipeline/SourcePipelineBuilder.cs
+++ b/src/PSRule/Pipeline/SourcePipelineBuilder.cs
@@ -162,7 +162,7 @@ public void ModuleByName(string name, string? version = null)
Source(new Source(info, files, dependency: false));
- // Import dependencies
+ // TODO: Import dependencies
//for (var i = 0; module.RequiredModules != null && i < module.RequiredModules.Count; i++)
// Module(module.RequiredModules[i], dependency: true);
}
@@ -182,15 +182,17 @@ private bool TryPackagedModuleFromCache(string name, string? version, out string
if (_CachePath == null)
return false;
- Log($"[PSRule][S] -- Searching for module in: {_CachePath}");
- if (!string.IsNullOrEmpty(version))
+ Log($"[PSRule][S] -- Searching for module in cache: {_CachePath}");
+ if (!string.IsNullOrEmpty(version) && version != null)
{
- path = Environment.GetRootedBasePath(Path.Combine(_CachePath, "Modules", name, version));
+ path = Environment.GetRootedBasePath(Path.Combine(_CachePath, "Modules", name, GetVersionPath(version)));
+ Log($"[PSRule][S] -- Search for module by version in: {path}");
if (File.Exists(Path.Combine(path, GetManifestName(name))))
return true;
}
path = Environment.GetRootedBasePath(Path.Combine(_CachePath, "Modules", name));
+ Log($"[PSRule][S] -- Search for module as version-less in: {path}");
return File.Exists(Path.Combine(path, GetManifestName(name)));
}
@@ -211,9 +213,9 @@ private bool TryInstalledModule(string name, string? version, out string? path)
var searchPath = Environment.GetRootedBasePath(Path.Combine(searchPaths[i], name));
// Try a specific version.
- if (!string.IsNullOrEmpty(version))
+ if (!string.IsNullOrEmpty(version) && version != null)
{
- var versionPath = Path.Combine(searchPath, version);
+ var versionPath = Path.Combine(searchPath, GetVersionPath(version));
var manifestPath = Path.Combine(versionPath, GetManifestName(name));
if (File.Exists(manifestPath))
{
@@ -247,6 +249,20 @@ private bool TryInstalledModule(string name, string? version, out string? path)
return sorted.Length > 0;
}
+ ///
+ /// Get the path for version lookups which ignore pre-release version and build segments.
+ /// If version is pre-release, use major.minor.patch.
+ ///
+ /// Return a version string just containing major.minor.patch.
+ private static string GetVersionPath(string version)
+ {
+ if (version.Contains('-') || version.Contains('+'))
+ {
+ version = version.Split('-', '+')[0];
+ }
+ return version;
+ }
+
private static string[] SortModulePath(IEnumerable values)
{
var results = values.ToArray();
diff --git a/tests/PSRule.Tests/PSRule.Tests.csproj b/tests/PSRule.Tests/PSRule.Tests.csproj
index 84fccd5eed..d277066453 100644
--- a/tests/PSRule.Tests/PSRule.Tests.csproj
+++ b/tests/PSRule.Tests/PSRule.Tests.csproj
@@ -183,12 +183,31 @@
PreserveNewest
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/PSRule.Tests/SourcePipelineBuilderTests.cs b/tests/PSRule.Tests/SourcePipelineBuilderTests.cs
index 5e7214da0b..ad1a8f8c05 100644
--- a/tests/PSRule.Tests/SourcePipelineBuilderTests.cs
+++ b/tests/PSRule.Tests/SourcePipelineBuilderTests.cs
@@ -11,101 +11,127 @@ namespace PSRule;
///
public sealed class SourcePipelineBuilderTests : BaseTests
{
- [Fact]
- public void Add_single_file()
+ [Theory]
+ [InlineData("FromFile.Rule.ps1")]
+ [InlineData("John's Documents/FromFileBaseline.Rule.ps1")]
+ public void Directory_WithSingleFile_ShouldFindCount(string path)
{
var builder = new SourcePipelineBuilder(null, null);
- builder.Directory(GetSourcePath("FromFile.Rule.ps1"));
+ builder.Directory(GetSourcePath(path));
var sources = builder.Build();
Assert.Single(sources);
Assert.Single(sources[0].File);
+ Assert.Equal(GetSourcePath(path), sources[0].File[0].Path);
}
- [Fact]
- public void Add_directory()
+ [Theory]
+ [InlineData("FromFile.Rule.yaml", 1)]
+ public void Directory_WithSingleFileAndDisablePowerShell_ShouldFindCount(string path, int count)
{
- var builder = new SourcePipelineBuilder(null, null);
- builder.Directory(GetSourcePath(""));
+ var option = GetOption();
+ option.Execution.RestrictScriptSource = RestrictScriptSource.DisablePowerShell;
+ var builder = new SourcePipelineBuilder(null, option);
+ builder.Directory(GetSourcePath(path));
var sources = builder.Build();
Assert.Single(sources);
- Assert.Equal(26, sources[0].File.Length);
+ Assert.Equal(count, sources[0].File.Length);
}
- [Fact]
- public void Add_script_file_with_disable_powershell()
+ [Theory]
+ [InlineData("FromFile.Rule.ps1")]
+ public void Directory_WithScriptFileAndDisablePowerShell_ShouldNotFindAny(string path)
{
var option = GetOption();
option.Execution.RestrictScriptSource = RestrictScriptSource.DisablePowerShell;
var builder = new SourcePipelineBuilder(null, option);
- builder.Directory(GetSourcePath("FromFile.Rule.ps1"));
+ builder.Directory(GetSourcePath(path));
var sources = builder.Build();
Assert.Empty(sources);
}
- [Fact]
- public void Add_script_file_with_module_only()
+ [Theory]
+ [InlineData("FromFile.Rule.yaml", 1)]
+ public void Directory_WithSingleFileAndModuleOnly_ShouldFindCount(string path, int count)
{
var option = GetOption();
option.Execution.RestrictScriptSource = RestrictScriptSource.ModuleOnly;
var builder = new SourcePipelineBuilder(null, option);
- builder.Directory(GetSourcePath("FromFile.Rule.ps1"));
+ builder.Directory(GetSourcePath(path));
var sources = builder.Build();
- Assert.Empty(sources);
+ Assert.Single(sources);
+ Assert.Equal(count, sources[0].File.Length);
}
- [Fact]
- public void Add_yaml_file_with_disable_powershell()
+ [Theory]
+ [InlineData("FromFile.Rule.ps1")]
+ public void Directory_WithScriptFileAndModuleOnly_ShouldNotFindAny(string path)
{
var option = GetOption();
- option.Execution.RestrictScriptSource = RestrictScriptSource.DisablePowerShell;
+ option.Execution.RestrictScriptSource = RestrictScriptSource.ModuleOnly;
var builder = new SourcePipelineBuilder(null, option);
- builder.Directory(GetSourcePath("FromFile.Rule.yaml"));
+ builder.Directory(GetSourcePath(path));
var sources = builder.Build();
- Assert.Single(sources);
- Assert.Single(sources[0].File);
+ Assert.Empty(sources);
}
- [Fact]
- public void Add_yaml_file_with_module_only()
+ [Theory]
+ [InlineData("", 28)]
+ [InlineData("John's Documents", 1)]
+ public void Directory_WithDirectory_ShouldFindCount(string path, int count)
{
- var option = GetOption();
- option.Execution.RestrictScriptSource = RestrictScriptSource.ModuleOnly;
- var builder = new SourcePipelineBuilder(null, option);
- builder.Directory(GetSourcePath("FromFile.Rule.yaml"));
+ var builder = new SourcePipelineBuilder(null, null);
+ builder.Directory(GetSourcePath(path));
var sources = builder.Build();
Assert.Single(sources);
- Assert.Single(sources[0].File);
+ Assert.Equal(count, sources[0].File.Length);
}
- [Fact]
- public void Add_directory_with_disable_powershell()
+ [Theory]
+ [InlineData("", 21)]
+ public void Directory_WithDirectoryAndDisablePowerShell_ShouldFindCount(string path, int count)
{
var option = GetOption();
option.Execution.RestrictScriptSource = RestrictScriptSource.DisablePowerShell;
var builder = new SourcePipelineBuilder(null, option);
- builder.Directory(GetSourcePath(""));
+ builder.Directory(GetSourcePath(path));
var sources = builder.Build();
Assert.Single(sources);
- Assert.Equal(20, sources[0].File.Length);
+ Assert.Equal(count, sources[0].File.Length);
}
- [Fact]
- public void Add_directory_with_module_only()
+ [Theory]
+ [InlineData("", 21)]
+ public void Directory_WithDirectoryAndModuleOnly_ShouldFindCount(string path, int count)
{
var option = GetOption();
option.Execution.RestrictScriptSource = RestrictScriptSource.ModuleOnly;
var builder = new SourcePipelineBuilder(null, option);
- builder.Directory(GetSourcePath(""));
+ builder.Directory(GetSourcePath(path));
+ var sources = builder.Build();
+
+ Assert.Single(sources);
+ Assert.Equal(count, sources[0].File.Length);
+ }
+
+ [Theory]
+ [InlineData("TestModule7", "0.0.1")]
+ [InlineData("TestModule8", "0.0.1-Alpha")]
+ public void ModuleByName_WithNameAndVersion_ShouldFindModuleFiles(string name, string version)
+ {
+ var builder = new SourcePipelineBuilder(null, null, cachePath: GetSourcePath(""));
+ builder.ModuleByName(name: name, version: version);
var sources = builder.Build();
Assert.Single(sources);
- Assert.Equal(20, sources[0].File.Length);
+ Assert.Equal(name, sources[0].Module.Name);
+ Assert.Equal(version, sources[0].Module.FullVersion);
+ Assert.NotEmpty(sources[0].File);
}
}
diff --git a/tests/PSRule.Tests/TestModule8/TestModule8.psd1 b/tests/PSRule.Tests/TestModule8/TestModule8.psd1
index 45bcf7699e..35cfd3993b 100644
--- a/tests/PSRule.Tests/TestModule8/TestModule8.psd1
+++ b/tests/PSRule.Tests/TestModule8/TestModule8.psd1
@@ -20,13 +20,13 @@ ModuleVersion = '0.0.1'
GUID = 'ff869216-0ca8-42e8-930f-5728719d4f5d'
# Author of this module
-Author = 'Armaan Mcleod'
+Author = 'Bernie White'
# Company or vendor of this module
-CompanyName = 'Armaan Mcleod'
+CompanyName = 'Bernie White'
# Copyright statement for this module
-Copyright = '(c) Armaan Mcleod. All rights reserved.'
+Copyright = '(c) Bernie White. All rights reserved.'
# Description of the functionality provided by this module
Description = 'A module for Pester testing for PSRule.'
@@ -109,7 +109,7 @@ PrivateData = @{
# ReleaseNotes = ''
# Prerelease string of this module
- # Prerelease = ''
+ Prerelease = 'Alpha'
# Flag to indicate whether the module requires explicit user acceptance for install/update/save
# RequireLicenseAcceptance = $false