Skip to content

Commit d1d68e4

Browse files
Dependencies: iterate available versions to find compatible installer for dependency resolution
1 parent 4ef1472 commit d1d68e4

File tree

1 file changed

+74
-40
lines changed

1 file changed

+74
-40
lines changed

src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp

Lines changed: 74 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)