Skip to content

Commit d276e3a

Browse files
committed
Prevent upgrades to prerelease when constrained to stable versions
1 parent eb0952e commit d276e3a

File tree

2 files changed

+36
-3
lines changed

2 files changed

+36
-3
lines changed

lib/src/solver/package_lister.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,16 @@ class PackageLister {
162162
/// Returns the best version of this package that matches [constraint]
163163
/// according to the solver's prioritization scheme, or `null` if no versions
164164
/// match.
165+
/// If [allowPrereleases] is false, this will only consider non-prerelease
166+
/// versions unless there are no non-prerelease versions that match
167+
/// [constraint].
165168
///
166169
/// Throws a [PackageNotFoundException] if this lister's package doesn't
167170
/// exist.
168-
Future<PackageId?> bestVersion(VersionConstraint constraint) async {
171+
Future<PackageId?> bestVersion(
172+
VersionConstraint constraint, {
173+
bool allowPrereleases = true,
174+
}) async {
169175
final locked = _locked;
170176
if (locked != null && constraint.allows(locked.version)) return locked;
171177

@@ -192,13 +198,14 @@ class PackageLister {
192198
if (isPastLimit(id.version)) break;
193199

194200
if (!constraint.allows(id.version)) continue;
201+
if (!allowPrereleases && id.version.isPreRelease) continue;
195202
if (!id.version.isPreRelease) {
196203
return id;
197204
}
198205
bestPrerelease ??= id;
199206
}
200207

201-
return bestPrerelease;
208+
return allowPrereleases ? bestPrerelease : null;
202209
}
203210

204211
/// Returns incompatibilities that encapsulate [id]'s dependencies, or that

lib/src/solver/version_solver.dart

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,25 @@ class VersionSolver {
6868
for (final package in _root.transitiveWorkspace) package.name: package,
6969
};
7070

71+
/// Set of direct dependencies from root pubspec.yaml
72+
late final Set<String> _rootDirectDependencies = _root.pubspec.dependencies
73+
.keys.toSet();
74+
75+
/// Returns true if the root's direct dependency for [packageName] is stable
76+
/// (not prerelease)
77+
bool _isRootDirectDependencyStable(String packageName) {
78+
final dep = _root.pubspec.dependencies[packageName];
79+
if (dep == null) return false;
80+
final constraint = dep.constraint;
81+
if (constraint is Version) {
82+
return !constraint.isPreRelease;
83+
}
84+
if (constraint is VersionRange && constraint.min != null) {
85+
return !constraint.min!.isPreRelease;
86+
}
87+
return false;
88+
}
89+
7190
/// The lockfile, indicating which package versions were previously selected.
7291
final LockFile _lockFile;
7392

@@ -398,7 +417,14 @@ class VersionSolver {
398417

399418
PackageId? version;
400419
try {
401-
version = await _packageLister(package).bestVersion(package.constraint);
420+
// Prereleases are allowed only if not direct or not stable.
421+
final isDirect = _rootDirectDependencies.contains(package.name);
422+
final isStable = _isRootDirectDependencyStable(package.name);
423+
final allowPrereleases = !(isDirect && isStable);
424+
version = await _packageLister(package).bestVersion(
425+
package.constraint,
426+
allowPrereleases: allowPrereleases,
427+
);
402428
} on PackageNotFoundException catch (error) {
403429
_addIncompatibility(
404430
Incompatibility([

0 commit comments

Comments
 (0)