@@ -45,71 +45,105 @@ namespace AppInstaller::CLI::Workflow
4545 m_nodePackageInstalledVersion = GetInstalledVersion (package);
4646 std::shared_ptr<IPackageVersionCollection> availableVersions = GetAvailableVersionsForInstalledVersion (package);
4747
48- if (m_context.Args .Contains (Execution::Args::Type::Force))
49- {
50- m_nodePackageLatestVersion = availableVersions->GetLatestVersion ();
51- }
52- else
53- {
54- Pinning::PinBehavior pinBehavior = m_context.Args .Contains (Execution::Args::Type::IncludePinned) ? Pinning::PinBehavior::IncludePinned : Pinning::PinBehavior::ConsiderPins;
55-
56- Pinning::PinningData pinningData{ Pinning::PinningData::Disposition::ReadOnly };
57- auto evaluator = pinningData.CreatePinStateEvaluator (pinBehavior, m_nodePackageInstalledVersion);
58-
59- m_nodePackageLatestVersion = evaluator.GetLatestAvailableVersionForPins (availableVersions);
60- }
61-
48+ // If the package is already installed and meets the dependency's MinVersion, skip.
6249 if (m_nodePackageInstalledVersion && dependencyNode.IsVersionOk (Utility::Version (m_nodePackageInstalledVersion->GetProperty (PackageVersionProperty::Version))))
6350 {
6451 // return empty dependency list,
6552 // as we won't keep searching for dependencies for installed packages
6653 return DependencyNodeProcessorResult::Skipped;
6754 }
6855
69- if (!m_nodePackageLatestVersion )
56+ if (!availableVersions )
7057 {
7158 error << Resource::String::DependenciesFlowPackageVersionNotFound (Utility::LocIndView{ Utility::Normalize (packageId) }) << std::endl;
72- AICLI_LOG (CLI, Error, << " Latest available version not found for package " << packageId);
59+ AICLI_LOG (CLI, Error, << " Available versions not found for package " << packageId);
7360 return DependencyNodeProcessorResult::Error;
7461 }
7562
76- if (!dependencyNode.IsVersionOk (Utility::Version (m_nodePackageLatestVersion->GetProperty (PackageVersionProperty::Version))))
77- {
78- error << Resource::String::DependenciesFlowNoMinVersion (Utility::LocIndView{ Utility::Normalize (packageId) }) << std::endl;
79- AICLI_LOG (CLI, Error, << " No suitable min version found for package " << packageId);
80- return DependencyNodeProcessorResult::Error;
81- }
82-
83- m_nodeManifest = m_nodePackageLatestVersion->GetManifest ();
84- m_nodeManifest.ApplyLocale ();
63+ // Determine pin behavior: --force should ignore pin restrictions for selection purposes.
64+ Pinning::PinBehavior pinBehavior = m_context.Args .Contains (Execution::Args::Type::IncludePinned) || m_context.Args .Contains (Execution::Args::Type::Force)
65+ ? Pinning::PinBehavior::IncludePinned
66+ : Pinning::PinBehavior::ConsiderPins;
8567
86- if (m_nodeManifest.Installers .empty ())
87- {
88- error << Resource::String::DependenciesFlowNoInstallerFound (Utility::LocIndView{ Utility::Normalize (m_nodeManifest.Id ) }) << std::endl;
89- AICLI_LOG (CLI, Error, << " Installer not found for manifest " << m_nodeManifest.Id << " with version" << m_nodeManifest.Version );
90- return DependencyNodeProcessorResult::Error;
91- }
68+ Pinning::PinningData pinningData{ Pinning::PinningData::Disposition::ReadOnly };
69+ auto evaluator = pinningData.CreatePinStateEvaluator (pinBehavior, m_nodePackageInstalledVersion);
9270
71+ // Iterate versions from newest to oldest, looking for the first version that:
72+ // - satisfies dependency.MinVersion
73+ // - is not pinned (unless includePinned or force)
74+ // - has at least one applicable installer according to ManifestComparator
75+ bool foundCandidate = false ;
9376 IPackageVersion::Metadata installationMetadata;
9477 if (m_nodePackageInstalledVersion)
9578 {
9679 installationMetadata = m_nodePackageInstalledVersion->GetMetadata ();
9780 }
9881
99- ManifestComparator manifestComparator (GetManifestComparatorOptions (m_context, installationMetadata));
100- auto [installer, inapplicabilities] = manifestComparator.GetPreferredInstaller (m_nodeManifest);
82+ Manifest::ManifestComparator manifestComparator (GetManifestComparatorOptions (m_context, installationMetadata));
83+
84+ auto versionKeys = availableVersions->GetVersionKeys ();
85+ for (const auto & key : versionKeys)
86+ {
87+ auto candidateVersion = availableVersions->GetVersion (key);
88+ if (!candidateVersion)
89+ {
90+ continue ;
91+ }
92+
93+ // Skip pinned versions unless explicitly allowed by flags
94+ auto pinType = evaluator.EvaluatePinType (candidateVersion);
95+ if (pinType != Pinning::PinType::Unknown && !m_context.Args .Contains (Execution::Args::Type::IncludePinned) && !m_context.Args .Contains (Execution::Args::Type::Force))
96+ {
97+ // This version is pinned and the user did not request to include pinned versions
98+ AICLI_LOG (CLI, Info, << " Skipping pinned version " << candidateVersion->GetProperty (PackageVersionProperty::Version) << " for package " << packageId);
99+ continue ;
100+ }
101+
102+ // Check MinVersion constraint from the dependency node
103+ Utility::Version candidateVer (candidateVersion->GetProperty (PackageVersionProperty::Version));
104+ if (!dependencyNode.IsVersionOk (candidateVer))
105+ {
106+ // Candidate version is lower than required min version for this dependency
107+ AICLI_LOG (CLI, Info, << " Skipping version " << candidateVer.ToString () << " because it does not meet MinVersion for dependency " << dependencyNode.Id ());
108+ continue ;
109+ }
110+
111+ // Load manifest for this version and attempt installer selection
112+ Manifest::Manifest manifest = candidateVersion->GetManifest ();
113+ manifest.ApplyLocale ();
114+
115+ if (manifest.Installers .empty ())
116+ {
117+ AICLI_LOG (CLI, Info, << " No installers in manifest for " << manifest.Id << " version " << manifest.Version );
118+ continue ;
119+ }
120+
121+ auto [installer, inapplicabilities] = manifestComparator.GetPreferredInstaller (manifest);
122+
123+ if (!installer.has_value ())
124+ {
125+ // No suitable installer for this manifest; keep searching older versions.
126+ AICLI_LOG (CLI, Info, << " No suitable installer found for manifest " << manifest.Id << " version " << manifest.Version );
127+ continue ;
128+ }
129+
130+ // Found a working candidate
131+ m_nodePackageLatestVersion = candidateVersion;
132+ m_nodeManifest = std::move (manifest);
133+ m_installer = installer.value ();
134+ foundCandidate = true ;
135+ break ;
136+ }
101137
102- if (!installer. has_value () )
138+ if (!foundCandidate )
103139 {
104- auto manifestId = Utility::LocIndString{ Utility::Normalize (m_nodeManifest.Id ) };
105- auto manifestVersion = Utility::LocIndString{ m_nodeManifest.Version };
106- error << Resource::String::DependenciesFlowNoSuitableInstallerFound (manifestId, manifestVersion) << std::endl;
107- AICLI_LOG (CLI, Error, << " No suitable installer found for manifest " << m_nodeManifest.Id << " with version " << m_nodeManifest.Version );
140+ error << Resource::String::DependenciesFlowNoSuitableInstallerFound (Utility::LocIndView{ Utility::Normalize (packageId) }, Utility::LocIndView{ Utility::LocIndString{ /* empty version to indicate search failed across versions */ " " } }) << std::endl;
141+ AICLI_LOG (CLI, Error, << " No suitable installer found for any available version of package " << packageId);
108142 return DependencyNodeProcessorResult::Error;
109143 }
110144
111- m_installer = installer. value ();
145+ // Extract the dependency list from the chosen installer's dependencies
112146 m_dependenciesList = m_installer.Dependencies ;
113147 return DependencyNodeProcessorResult::Success;
114148 }
115- }
149+ }
0 commit comments