Tests for PR#1963 - #2029
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
🟡 Changes recommended
There are confirmed logic bugs in RID/TFM parsing/filtering paths (including RID compatibility-chain handling and dependency-group selection edge cases) that can cause incorrect behavior or test failures.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR introduces platform-aware installation support in PSResourceGet by adding Runtime Identifier (RID) and Target Framework Moniker (TFM) filtering during package extraction, along with TFM-aware dependency-group selection and a new suite of tests covering these behaviors.
Changes:
- Add
-RuntimeIdentifierand-TargetFrameworkparameters toInstall-PSResource, including argument completers, and implement RID/TFM filtering plus merge behavior for already-installed packages. - Implement new helpers for RID detection/compatibility and runtime asset filtering/discovery.
- Add unit + integration tests for RID/TFM filtering and nuspec dependency parsing behavior.
File summaries
| File | Description |
|---|---|
| test/PlatformFilteringTests/RuntimePackageHelper.Tests.ps1 | Adds unit tests for runtime entry detection/filtering and RID discovery. |
| test/PlatformFilteringTests/RuntimeIdentifierHelper.Tests.ps1 | Adds unit tests for RID detection and compatibility-chain logic. |
| test/PlatformFilteringTests/PlatformAwareInstall.Tests.ps1 | Adds integration tests covering RID/TFM filtering, overrides, merge installs, and warnings. |
| src/code/Utils.cs | Adds MergeDirContents helper to merge extracted filtered content into existing installs. |
| src/code/RuntimePackageHelper.cs | New helper for filtering runtime assets and scanning archives for available RIDs. |
| src/code/RuntimeIdentifierHelper.cs | New helper for current RID detection and compatibility-chain calculation. |
| src/code/PSResourceInfo.cs | Updates dependency parsing to select the best matching TFM dependency group (JSON + nuspec). |
| src/code/InternalHooks.cs | Adds test hook wrappers to exercise the new helpers from Pester tests. |
| src/code/InstallPSResource.cs | Adds new cmdlet parameters and passes them into the install pipeline. |
| src/code/InstallHelper.cs | Implements RID/TFM filtering during extraction, merge behavior, and user warnings. |
| src/code/ArgumentCompleter.cs | Adds argument completers for -RuntimeIdentifier and -TargetFramework. |
Review details
Suppressed comments (2)
src/code/RuntimePackageHelper.cs:126
- GetRidFromRuntimesEntry only extracts a RID from root-level "{rid}/..." entries. With the current implementation, callers scanning a package using the common "runtimes/{rid}/..." layout will always get null and downstream logic (e.g., available RID discovery) will behave incorrectly.
string normalizedPath = entryFullName.Replace('\\', ZipPathSeparator);
string[] parts = normalizedPath.Split(ZipPathSeparator);
if (parts.Length >= 2 && IsRidFolder(parts[0]))
{
src/code/PSResourceInfo.cs:766
- If FrameworkReducer returns a framework that isn’t exactly present in groupMap, FirstOrDefault() yields a default JsonElement (ValueKind.Undefined). Assigning that into selectedGroupElement leaves HasValue=true and can throw later when TryGetProperty is called. Guard against Undefined and let the fallback logic pick an Any/first group instead.
NuGetFramework bestMatch = reducer.GetNearest(currentFramework, groupMap.Select(g => g.framework));
if (bestMatch != null)
{
selectedGroupElement = groupMap.FirstOrDefault(g => g.framework.Equals(bestMatch)).groupElement;
- Files reviewed: 11/11 changed files
- Comments generated: 4
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| string tfmString = tfmElement.GetString(); | ||
| if (!string.IsNullOrWhiteSpace(tfmString)) | ||
| { | ||
| foreach ( | ||
| JsonElement dependency in dependenciesElement.EnumerateArray().Where( | ||
| x => x.TryGetProperty("id", out JsonElement idProperty) && | ||
| !string.IsNullOrWhiteSpace(idProperty.GetString()) | ||
| ) | ||
| ) | ||
| NuGetFramework parsed = NuGetFramework.Parse(tfmString); | ||
| if (parsed != null && !parsed.IsUnsupported) |
| else if (primaryRid.StartsWith("linux", StringComparison.OrdinalIgnoreCase)) | ||
| { | ||
| // Linux compatibility chain | ||
| // linux-x64 -> linux -> unix -> any | ||
| // linux-armel -> linux-arm -> linux -> unix -> any | ||
| string arch = ExtractArchitecture(primaryRid); | ||
| if (arch != null) | ||
| { | ||
| // armel (ARM EABI soft-float) is compatible with arm | ||
| if (string.Equals(arch, "armel", StringComparison.OrdinalIgnoreCase)) | ||
| { | ||
| compatibleRids.Add("linux-arm"); | ||
| } | ||
| compatibleRids.Add("linux"); | ||
| compatibleRids.Add("unix"); | ||
| compatibleRids.Add("any"); | ||
| } | ||
| } |
| public static bool IsRuntimesEntry(string entryFullName) | ||
| { | ||
| if (string.IsNullOrEmpty(entryFullName)) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| string normalizedPath = entryFullName.Replace('\\', ZipPathSeparator); | ||
| string[] segments = normalizedPath.Split(ZipPathSeparator); | ||
|
|
||
| // Pattern: {rid}/... (root-level RID folders like win-x64/native.dll) | ||
| return segments.Length >= 2 && IsRidFolder(segments[0]); | ||
| } |
| using System.Collections.Generic; | ||
| using System.IO; | ||
| using System.IO.Compression; | ||
| using System.Reflection; | ||
| using System.Runtime.InteropServices; |
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
PR Summary
PR Context
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerWIP:or[ WIP ]to the beginning of the title (theWIPbot will keep its status check atPendingwhile the prefix is present) and remove the prefix when the PR is ready.