Skip to content

Commit 1e3c17e

Browse files
authored
dependency_services: use revisions as versions for git deps. (#3383)
1 parent 9ecdd6b commit 1e3c17e

File tree

11 files changed

+945
-94
lines changed

11 files changed

+945
-94
lines changed

lib/src/command/dependency_services.dart

Lines changed: 127 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import '../package_name.dart';
2323
import '../pubspec.dart';
2424
import '../pubspec_utils.dart';
2525
import '../solver.dart';
26+
import '../source/git.dart';
2627
import '../system_cache.dart';
2728
import '../utils.dart';
2829

@@ -74,11 +75,11 @@ class DependencyServicesReportCommand extends PubCommand {
7475
Future<List<Object>> _computeUpgradeSet(
7576
Pubspec rootPubspec,
7677
PackageId? package, {
77-
required UpgradeType upgradeType,
78+
required _UpgradeType upgradeType,
7879
}) async {
7980
if (package == null) return [];
8081
final lockFile = entrypoint.lockFile;
81-
final pubspec = upgradeType == UpgradeType.multiBreaking
82+
final pubspec = upgradeType == _UpgradeType.multiBreaking
8283
? stripVersionUpperBounds(rootPubspec)
8384
: Pubspec(
8485
rootPubspec.name,
@@ -87,7 +88,7 @@ class DependencyServicesReportCommand extends PubCommand {
8788
sdkConstraints: rootPubspec.sdkConstraints,
8889
);
8990

90-
final dependencySet = dependencySetOfPackage(pubspec, package);
91+
final dependencySet = _dependencySetOfPackage(pubspec, package);
9192
if (dependencySet != null) {
9293
// Force the version to be the new version.
9394
dependencySet[package.name] =
@@ -110,38 +111,43 @@ class DependencyServicesReportCommand extends PubCommand {
110111
...resolution.packages.where((r) {
111112
if (r.name == rootPubspec.name) return false;
112113
final originalVersion = currentPackages[r.name];
113-
return originalVersion == null ||
114-
r.version != originalVersion.version;
114+
return originalVersion == null || r != originalVersion;
115115
}).map((p) {
116-
final depset = dependencySetOfPackage(rootPubspec, p);
116+
final depset = _dependencySetOfPackage(rootPubspec, p);
117117
final originalConstraint = depset?[p.name]?.constraint;
118+
final currentPackage = currentPackages[p.name];
118119
return {
119120
'name': p.name,
120-
'version': p.version.toString(),
121+
'version': p.versionOrHash(),
121122
'kind': _kindString(pubspec, p.name),
123+
'source': _source(p, containingDir: directory),
122124
'constraintBumped': originalConstraint == null
123125
? null
124-
: upgradeType == UpgradeType.compatible
126+
: upgradeType == _UpgradeType.compatible
125127
? originalConstraint.toString()
126128
: VersionConstraint.compatibleWith(p.version).toString(),
127129
'constraintWidened': originalConstraint == null
128130
? null
129-
: upgradeType == UpgradeType.compatible
131+
: upgradeType == _UpgradeType.compatible
130132
? originalConstraint.toString()
131133
: _widenConstraint(originalConstraint, p.version)
132134
.toString(),
133135
'constraintBumpedIfNeeded': originalConstraint == null
134136
? null
135-
: upgradeType == UpgradeType.compatible
137+
: upgradeType == _UpgradeType.compatible
136138
? originalConstraint.toString()
137139
: originalConstraint.allows(p.version)
138140
? originalConstraint.toString()
139141
: VersionConstraint.compatibleWith(p.version)
140142
.toString(),
141-
'previousVersion': currentPackages[p.name]?.version.toString(),
143+
'previousVersion': currentPackage?.versionOrHash(),
142144
'previousConstraint': originalConstraint?.toString(),
145+
'previousSource': currentPackage == null
146+
? null
147+
: _source(currentPackage, containingDir: directory),
143148
};
144149
}),
150+
// Find packages that were removed by the resolution
145151
for (final oldPackageName in lockFile.packages.keys)
146152
if (!resolution.packages
147153
.any((newPackage) => newPackage.name == oldPackageName))
@@ -154,8 +160,10 @@ class DependencyServicesReportCommand extends PubCommand {
154160
'constraintWidened': null,
155161
'constraintBumpedIfNeeded': null,
156162
'previousVersion':
157-
currentPackages[oldPackageName]?.version.toString(),
163+
currentPackages[oldPackageName]?.versionOrHash(),
158164
'previousConstraint': null,
165+
'previous': _source(currentPackages[oldPackageName]!,
166+
containingDir: directory)
159167
},
160168
];
161169
}
@@ -173,7 +181,7 @@ class DependencyServicesReportCommand extends PubCommand {
173181
devDependencies: compatiblePubspec.devDependencies.values,
174182
);
175183
final dependencySet =
176-
dependencySetOfPackage(singleBreakingPubspec, package);
184+
_dependencySetOfPackage(singleBreakingPubspec, package);
177185
final kind = _kindString(compatiblePubspec, package.name);
178186
PackageId? singleBreakingVersion;
179187
if (dependencySet != null) {
@@ -187,25 +195,24 @@ class DependencyServicesReportCommand extends PubCommand {
187195
}
188196
dependencies.add({
189197
'name': package.name,
190-
'version': package.version.toString(),
198+
'version': package.versionOrHash(),
191199
'kind': kind,
200+
'source': _source(package, containingDir: directory),
192201
'latest':
193202
(await cache.getLatest(package.toRef(), version: package.version))
194-
?.version
195-
.toString(),
203+
?.versionOrHash(),
196204
'constraint':
197205
_constraintOf(compatiblePubspec, package.name)?.toString(),
198-
if (compatibleVersion != null)
199-
'compatible': await _computeUpgradeSet(
200-
compatiblePubspec, compatibleVersion,
201-
upgradeType: UpgradeType.compatible),
206+
'compatible': await _computeUpgradeSet(
207+
compatiblePubspec, compatibleVersion,
208+
upgradeType: _UpgradeType.compatible),
202209
'singleBreaking': kind != 'transitive' && singleBreakingVersion == null
203210
? []
204211
: await _computeUpgradeSet(compatiblePubspec, singleBreakingVersion,
205-
upgradeType: UpgradeType.singleBreaking),
212+
upgradeType: _UpgradeType.singleBreaking),
206213
'multiBreaking': kind != 'transitive' && multiBreakingVersion != null
207214
? await _computeUpgradeSet(compatiblePubspec, multiBreakingVersion,
208-
upgradeType: UpgradeType.multiBreaking)
215+
upgradeType: _UpgradeType.multiBreaking)
209216
: [],
210217
});
211218
}
@@ -227,6 +234,14 @@ String _kindString(Pubspec pubspec, String packageName) {
227234
: 'transitive';
228235
}
229236

237+
Map<String, Object?> _source(PackageId id, {required String containingDir}) {
238+
return {
239+
'type': id.source.name,
240+
'description':
241+
id.description.serializeForLockfile(containingDir: containingDir),
242+
};
243+
}
244+
230245
/// Try to solve [pubspec] return [PackageId]s in the resolution or `null` if no
231246
/// resolution was found.
232247
Future<List<PackageId>?> _tryResolve(Pubspec pubspec, SystemCache cache) async {
@@ -269,16 +284,28 @@ class DependencyServicesListCommand extends PubCommand {
269284
for (final package in currentPackages) {
270285
dependencies.add({
271286
'name': package.name,
272-
'version': package.version.toString(),
287+
'version': package.versionOrHash(),
273288
'kind': _kindString(pubspec, package.name),
274289
'constraint': _constraintOf(pubspec, package.name).toString(),
290+
'source': _source(package, containingDir: directory),
275291
});
276292
}
277293
log.message(JsonEncoder.withIndent(' ').convert(result));
278294
}
279295
}
280296

281-
enum UpgradeType {
297+
extension on PackageId {
298+
String versionOrHash() {
299+
final description = this.description;
300+
if (description is GitResolvedDescription) {
301+
return description.resolvedRef;
302+
} else {
303+
return version.toString();
304+
}
305+
}
306+
}
307+
308+
enum _UpgradeType {
282309
/// Only upgrade pubspec.lock.
283310
compatible,
284311

@@ -315,7 +342,7 @@ class DependencyServicesApplyCommand extends PubCommand {
315342
toApply.add(
316343
_PackageVersion(
317344
change['name'],
318-
change['version'] != null ? Version.parse(change['version']) : null,
345+
change['version'],
319346
change['constraint'] != null
320347
? VersionConstraint.parse(change['constraint'])
321348
: null,
@@ -334,13 +361,24 @@ class DependencyServicesApplyCommand extends PubCommand {
334361
final targetPackage = p.name;
335362
final targetVersion = p.version;
336363
final targetConstraint = p.constraint;
364+
final targetRevision = p.gitRevision;
337365

338366
if (targetConstraint != null) {
339367
final section = pubspec.dependencies[targetPackage] != null
340368
? 'dependencies'
341369
: 'dev_dependencies';
342-
pubspecEditor
343-
.update([section, targetPackage], targetConstraint.toString());
370+
final packageConfig =
371+
pubspecEditor.parseAt([section, targetPackage]).value;
372+
if (packageConfig == null || packageConfig is String) {
373+
pubspecEditor
374+
.update([section, targetPackage], targetConstraint.toString());
375+
} else if (packageConfig is Map) {
376+
pubspecEditor.update(
377+
[section, targetPackage, 'version'], targetConstraint.toString());
378+
} else {
379+
fail(
380+
'The dependency $targetPackage does not have a map or string as a description');
381+
}
344382
} else if (targetVersion != null) {
345383
final constraint = _constraintOf(pubspec, targetPackage);
346384
if (constraint != null && !constraint.allows(targetVersion)) {
@@ -351,18 +389,43 @@ class DependencyServicesApplyCommand extends PubCommand {
351389
VersionConstraint.compatibleWith(targetVersion).toString());
352390
}
353391
}
354-
if (targetVersion != null &&
355-
lockFileEditor != null &&
356-
lockFileYaml['packages'].containsKey(targetPackage)) {
357-
lockFileEditor.update(
358-
['packages', targetPackage, 'version'], targetVersion.toString());
359-
}
360-
if (targetVersion == null &&
361-
lockFileEditor != null &&
362-
!lockFileYaml['packages'].containsKey(targetPackage)) {
363-
dataError(
364-
'Trying to remove non-existing transitive dependency $targetPackage.',
365-
);
392+
if (lockFileEditor != null) {
393+
if (targetVersion != null &&
394+
lockFileYaml['packages'].containsKey(targetPackage)) {
395+
lockFileEditor.update(
396+
['packages', targetPackage, 'version'], targetVersion.toString());
397+
} else if (targetRevision != null &&
398+
lockFileYaml['packages'].containsKey(targetPackage)) {
399+
final ref = entrypoint.lockFile.packages[targetPackage]!.toRef();
400+
final currentDescription = ref.description as GitDescription;
401+
final updatedRef = PackageRef(
402+
targetPackage,
403+
GitDescription(
404+
url: currentDescription.url,
405+
path: currentDescription.path,
406+
ref: targetRevision,
407+
containingDir: directory));
408+
final versions = await cache.getVersions(updatedRef);
409+
if (versions.isEmpty) {
410+
dataError(
411+
'Found no versions of $targetPackage with git revision `$targetRevision`.');
412+
}
413+
// GitSource can only return a single version.
414+
assert(versions.length == 1);
415+
416+
lockFileEditor.update(['packages', targetPackage, 'version'],
417+
versions.single.version.toString());
418+
lockFileEditor.update(
419+
['packages', targetPackage, 'description', 'resolved-ref'],
420+
targetRevision,
421+
);
422+
} else if (targetVersion == null &&
423+
targetRevision == null &&
424+
!lockFileYaml['packages'].containsKey(targetPackage)) {
425+
dataError(
426+
'Trying to remove non-existing transitive dependency $targetPackage.',
427+
);
428+
}
366429
}
367430
}
368431

@@ -407,11 +470,31 @@ class DependencyServicesApplyCommand extends PubCommand {
407470
class _PackageVersion {
408471
String name;
409472
Version? version;
473+
String? gitRevision;
410474
VersionConstraint? constraint;
411-
_PackageVersion(this.name, this.version, this.constraint);
475+
_PackageVersion(this.name, String? versionOrHash, this.constraint)
476+
: version =
477+
versionOrHash == null ? null : _tryParseVersion(versionOrHash),
478+
gitRevision =
479+
versionOrHash == null ? null : _tryParseHash(versionOrHash);
480+
}
481+
482+
Version? _tryParseVersion(String v) {
483+
try {
484+
return Version.parse(v);
485+
} on FormatException {
486+
return null;
487+
}
488+
}
489+
490+
String? _tryParseHash(String v) {
491+
if (RegExp(r'^[a-fA-F0-9]+$').hasMatch(v)) {
492+
return v;
493+
}
494+
return null;
412495
}
413496

414-
Map<String, PackageRange>? dependencySetOfPackage(
497+
Map<String, PackageRange>? _dependencySetOfPackage(
415498
Pubspec pubspec, PackageId package) {
416499
return pubspec.dependencies.containsKey(package.name)
417500
? pubspec.dependencies
@@ -427,7 +510,7 @@ VersionConstraint _widenConstraint(
427510
final min = original.min;
428511
final max = original.max;
429512
if (max != null && newVersion >= max) {
430-
return compatibleWithIfPossible(
513+
return _compatibleWithIfPossible(
431514
VersionRange(
432515
min: min,
433516
includeMin: original.includeMin,
@@ -436,7 +519,7 @@ VersionConstraint _widenConstraint(
436519
);
437520
}
438521
if (min != null && newVersion <= min) {
439-
return compatibleWithIfPossible(
522+
return _compatibleWithIfPossible(
440523
VersionRange(
441524
min: newVersion,
442525
includeMin: true,
@@ -451,7 +534,7 @@ VersionConstraint _widenConstraint(
451534
original, 'original', 'Must be a Version range or empty');
452535
}
453536

454-
VersionConstraint compatibleWithIfPossible(VersionRange versionRange) {
537+
VersionConstraint _compatibleWithIfPossible(VersionRange versionRange) {
455538
final min = versionRange.min;
456539
if (min != null && min.nextBreaking.firstPreRelease == versionRange.max) {
457540
return VersionConstraint.compatibleWith(min);

lib/src/pubspec_utils.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,7 @@ Pubspec stripVersionUpperBounds(Pubspec original,
117117
final packageRange = constrained[name]!;
118118
var unconstrainedRange = packageRange;
119119

120-
/// We only need to remove the upper bound if it is a hosted package.
121-
if (packageRange.description is HostedDescription &&
122-
(stripOnly!.isEmpty || stripOnly.contains(packageRange.name))) {
120+
if (stripOnly!.isEmpty || stripOnly.contains(packageRange.name)) {
123121
unconstrainedRange = PackageRange(
124122
packageRange.toRef(),
125123
stripUpperBound(packageRange.constraint),

lib/src/source/git.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,13 @@ class GitDescription extends Description {
718718
other.path == path;
719719
}
720720

721+
GitDescription withRef(String newRef) => GitDescription._(
722+
url: url,
723+
relative: relative,
724+
ref: newRef,
725+
path: path,
726+
);
727+
721728
@override
722729
int get hashCode => Object.hash(url, ref, path);
723730

0 commit comments

Comments
 (0)