From a3b6b924f48d633a708d28fb8eb707634b9f16fb Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Fri, 15 Nov 2024 12:59:03 +0000 Subject: [PATCH 01/12] Add 'tag_pattern' feature --- lib/src/command/add.dart | 3 + lib/src/command/dependency_services.dart | 2 + lib/src/command/upgrade.dart | 7 +- lib/src/global_packages.dart | 2 + lib/src/language_version.dart | 3 + lib/src/source/git.dart | 139 ++++++++++++++++--- lib/src/source/path.dart | 1 + test/descriptor/git.dart | 9 ++ test/get/git/check_out_and_upgrade_test.dart | 80 +++++++++++ test/get/git/ssh_url_test.dart | 1 + 10 files changed, 222 insertions(+), 25 deletions(-) diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart index 5fe7f048a..305af541d 100644 --- a/lib/src/command/add.dart +++ b/lib/src/command/add.dart @@ -538,6 +538,7 @@ Specify multiple sdk packages with descriptors.'''); containingDir: p.current, ref: argResults.gitRef, path: argResults.gitPath, + tagPattern: argResults.tagPattern, ), ); } on FormatException catch (e) { @@ -775,6 +776,8 @@ extension on ArgResults { bool get isDryRun => flag('dry-run'); String? get gitUrl => this['git-url'] as String?; String? get gitPath => this['git-path'] as String?; + String? get tagPattern => this['tag-pattern'] as String?; + String? get gitRef => this['git-ref'] as String?; String? get hostedUrl => this['hosted-url'] as String?; String? get path => this['path'] as String?; diff --git a/lib/src/command/dependency_services.dart b/lib/src/command/dependency_services.dart index 9ae3aeebc..6fb2585af 100644 --- a/lib/src/command/dependency_services.dart +++ b/lib/src/command/dependency_services.dart @@ -397,6 +397,7 @@ class DependencyServicesApplyCommand extends PubCommand { } else if (targetRevision != null && (lockFileYaml['packages'] as Map).containsKey(targetPackage)) { final ref = entrypoint.lockFile.packages[targetPackage]!.toRef(); + final currentDescription = ref.description as GitDescription; final updatedRef = PackageRef( targetPackage, @@ -405,6 +406,7 @@ class DependencyServicesApplyCommand extends PubCommand { path: currentDescription.path, ref: targetRevision, containingDir: directory, + tagPattern: currentDescription.tagPattern, ), ); final versions = await cache.getVersions(updatedRef); diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart index edd7eb6c6..cc4b43a3c 100644 --- a/lib/src/command/upgrade.dart +++ b/lib/src/command/upgrade.dart @@ -17,7 +17,6 @@ import '../package_name.dart'; import '../pubspec.dart'; import '../pubspec_utils.dart'; import '../solver.dart'; -import '../source/hosted.dart'; import '../utils.dart'; /// Handles the `upgrade` pub command. @@ -247,11 +246,11 @@ be direct 'dependencies' or 'dev_dependencies', following packages are not: // Mapping from original to changed value. var changes = >{}; for (final package in entrypoint.workspaceRoot.transitiveWorkspace) { - final declaredHostedDependencies = [ + final declaredUpgradableDependencies = [ ...package.dependencies.values, ...package.devDependencies.values, - ].where((dep) => dep.source is HostedSource); - for (final dep in declaredHostedDependencies) { + ].where((dep) => dep.source.hasMultipleVersions); + for (final dep in declaredUpgradableDependencies) { final resolvedPackage = resolvedPackages[dep.name]!; if (!toUpgrade.contains(dep.name)) { // If we're not trying to upgrade this package, or it wasn't in the diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart index 16e2f1730..ab7fde78f 100644 --- a/lib/src/global_packages.dart +++ b/lib/src/global_packages.dart @@ -91,6 +91,7 @@ class GlobalPackages { required bool overwriteBinStubs, String? path, String? ref, + String? tagPattern, }) async { final name = await cache.git.getPackageNameFromRepo( repo, @@ -98,6 +99,7 @@ class GlobalPackages { path, cache, relativeTo: p.current, + tagPattern: tagPattern, ); // TODO(nweiz): Add some special handling for git repos that contain path diff --git a/lib/src/language_version.dart b/lib/src/language_version.dart index 578744458..dcec9e39a 100644 --- a/lib/src/language_version.dart +++ b/lib/src/language_version.dart @@ -72,6 +72,8 @@ class LanguageVersion implements Comparable { bool get supportsWorkspaces => this >= firstVersionWithWorkspaces; + bool get supportsTagPattern => this >= firstVersionWithTagPattern; + /// Minimum language version at which short hosted syntax is supported. /// /// This allows `hosted` dependencies to be expressed as: @@ -109,6 +111,7 @@ class LanguageVersion implements Comparable { static const firstVersionWithNullSafety = LanguageVersion(2, 12); static const firstVersionWithShorterHostedSyntax = LanguageVersion(2, 15); static const firstVersionWithWorkspaces = LanguageVersion(3, 5); + static const firstVersionWithTagPattern = LanguageVersion(3, 7); /// Transform language version to string that can be parsed with /// [LanguageVersion.parse]. diff --git a/lib/src/source/git.dart b/lib/src/source/git.dart index 54f4dd2e1..ab8eef6f8 100644 --- a/lib/src/source/git.dart +++ b/lib/src/source/git.dart @@ -32,6 +32,8 @@ class GitSource extends CachedSource { @override final name = 'git'; + @override + final hasMultipleVersions = true; @override PackageRef parseRef( @@ -43,6 +45,7 @@ class GitSource extends CachedSource { String url; String? ref; String? path; + String? tagPattern; if (description is String) { url = description; } else if (description is! Map) { @@ -72,6 +75,32 @@ class GitSource extends CachedSource { 'string.'); } path = descriptionPath; + + // TODO: can we avoid relying on key presence? + if (description.containsKey('tag_pattern')) { + if (languageVersion != null && !languageVersion.supportsWorkspaces) { + throw FormatException( + 'Using `git: {tagPattern: }` is only supported with a minimum SDK ' + 'constraint of ${LanguageVersion.firstVersionWithTagPattern}.', + ); + } + switch (description['tag_pattern']) { + case final String descriptionTagPattern: + tagPattern = descriptionTagPattern; + case null: + tagPattern = 'v*'; + default: + throw const FormatException( + "The 'tagPattern' field of the description " + 'must be a string or null.'); + } + } + + if (ref != null && tagPattern != null) { + throw const FormatException( + 'A git description cannot have both a ref and a `tagPattern`.', + ); + } } final containingDir = switch (containingDescription) { @@ -87,6 +116,7 @@ class GitSource extends CachedSource { containingDir: containingDir, ref: ref, path: _validatedPath(path), + tagPattern: tagPattern, ), ); } @@ -121,6 +151,13 @@ class GitSource extends CachedSource { throw const FormatException("The 'url' field of the description " 'must be a string.'); } + + final tagPattern = description['tag_pattern']; + if (tagPattern is! String?) { + throw const FormatException("The 'tag_pattern' field of the description " + 'must be a string.'); + } + return PackageId( name, version, @@ -132,6 +169,7 @@ class GitSource extends CachedSource { description['path'], ), containingDir: containingDir, + tagPattern: tagPattern, ), resolvedRef, ), @@ -230,17 +268,25 @@ class GitSource extends CachedSource { String? path, SystemCache cache, { required String relativeTo, + required String? tagPattern, }) async { + if (ref != null && tagPattern != null) { + fail('Cannot have both a `tagPattern` and a `ref`'); + } final description = GitDescription( url: url, ref: ref, path: path, containingDir: relativeTo, + tagPattern: tagPattern, // TODO ); return await _pool.withResource(() async { await _ensureRepoCache(description, cache); final path = _repoCachePath(description, cache); - final revision = await _firstRevision(path, description.ref); + + final revision = tagPattern != null + ? (await _listRevisionsWithTagPattern(path, tagPattern)).last + : await _firstRevision(path, description.ref); final resolvedDescription = ResolvedGitDescription(description, revision); return Pubspec.parse( @@ -297,16 +343,42 @@ class GitSource extends CachedSource { return await _pool.withResource(() async { await _ensureRepoCache(description, cache); final path = _repoCachePath(description, cache); - final revision = await _firstRevision(path, description.ref); - final pubspec = await _describeUncached(ref, revision, cache); + final List revisions; + if (description.tagPattern case final String tagPattern) { + revisions = await _listRevisionsWithTagPattern(path, tagPattern); + } else { + revisions = [await _firstRevision(path, description.ref)]; + } - return [ - PackageId( - ref.name, - pubspec.version, - ResolvedGitDescription(description, revision), - ), - ]; + final result = []; + final seenVersions = {}; + for (final revision in revisions) { + final Pubspec pubspec; + try { + pubspec = await _describeUncached(ref, revision, cache); + } on Exception { + log.fine( + 'Found no valid pubspec of ${ref.name} at $revision, ignoring,', + ); + continue; + } + final prev = seenVersions[pubspec.version]; + if (prev != null) { + log.fine( + 'Repeated version ${pubspec.version} of ${ref.name} ' + 'at $revision and $prev', + ); + } + seenVersions[pubspec.version] = revision; + result.add( + PackageId( + ref.name, + pubspec.version, + ResolvedGitDescription(description, revision), + ), + ); + } + return result; }); } @@ -327,6 +399,8 @@ class GitSource extends CachedSource { ); } + final Map<(PackageRef, String), Pubspec> _pubspecAtRevisionCache = {}; + /// Like [describeUncached], but takes a separate [ref] and Git [revision] /// rather than a single ID. Future _describeUncached( @@ -338,18 +412,20 @@ class GitSource extends CachedSource { if (description is! GitDescription) { throw ArgumentError('Wrong source'); } - await _ensureRevision(description, revision, cache); + return _pubspecAtRevisionCache[(ref, revision)] ??= await () async { + await _ensureRevision(description, revision, cache); - return Pubspec.parse( - await _showFileAtRevision( - ResolvedGitDescription(description, revision), - 'pubspec.yaml', - cache, - ), - cache.sources, - expectedName: ref.name, - containingDescription: ref.description, - ); + return Pubspec.parse( + await _showFileAtRevision( + ResolvedGitDescription(description, revision), + 'pubspec.yaml', + cache, + ), + cache.sources, + expectedName: ref.name, + containingDescription: ref.description, + ); + }(); } /// Clones a Git repo to the local filesystem. @@ -647,6 +723,19 @@ class GitSource extends CachedSource { String _packageListPath(String revisionCachePath) => p.join(revisionCachePath, '.git/pub-packages'); + /// + Future> _listRevisionsWithTagPattern( + String path, + String tagPattern, + ) async { + return (await git.run( + ['tag', '--list', tagPattern, '--format', '%(objectname)'], + workingDir: path, + )) + .trim() + .split('\n'); + } + /// Runs "git rev-list" on [reference] in [path] and returns the first result. /// /// This assumes that the canonical clone already exists. @@ -761,6 +850,8 @@ class GitDescription extends Description { /// not allow strings of the form: 'git@github.com:dart-lang/pub.git'. final String url; + final String? tagPattern; + /// `true` if [url] was parsed from a relative url. final bool relative; @@ -777,6 +868,7 @@ class GitDescription extends Description { required this.relative, required String? ref, required String? path, + required this.tagPattern, }) : ref = ref ?? 'HEAD', path = path ?? '.'; @@ -785,6 +877,7 @@ class GitDescription extends Description { required String? ref, required String? path, required String? containingDir, + required String? tagPattern, }) { final validatedUrl = GitSource._validatedUrl(url, containingDir); return GitDescription.raw( @@ -792,6 +885,7 @@ class GitDescription extends Description { relative: validatedUrl.wasRelative, ref: ref, path: path, + tagPattern: tagPattern, ); } @@ -819,6 +913,7 @@ class GitDescription extends Description { 'url': relativeUrl, if (ref != 'HEAD') 'ref': ref, if (path != '.') 'path': path, + if (tagPattern != null) 'tag_pattern': tagPattern, }; } @@ -838,6 +933,7 @@ class GitDescription extends Description { relative: relative, ref: newRef, path: path, + tagPattern: tagPattern, ); @override @@ -883,6 +979,7 @@ class ResolvedGitDescription extends ResolvedDescription { return { 'url': url, 'ref': description.ref, + 'tag_pattern': description.tagPattern, 'resolved-ref': resolvedRef, 'path': description.path, }; diff --git a/lib/src/source/path.dart b/lib/src/source/path.dart index 921e95e59..69fe5ce01 100644 --- a/lib/src/source/path.dart +++ b/lib/src/source/path.dart @@ -113,6 +113,7 @@ class PathSource extends Source { url: containingDescription.url, relative: containingDescription.relative, ref: containingDescription.ref, + tagPattern: containingDescription.tagPattern, path: resolvedPath, ), ); diff --git a/test/descriptor/git.dart b/test/descriptor/git.dart index 95ae0b21e..294d3db70 100644 --- a/test/descriptor/git.dart +++ b/test/descriptor/git.dart @@ -40,6 +40,15 @@ class GitRepoDescriptor extends DirectoryDescriptor { ]); } + /// Adds a tag named [tag] to the repo described by `this`. + /// + /// [parent] defaults to [sandbox]. + Future tag(String tag, [String? parent]) async { + await _runGitCommands(parent, [ + ['tag', '-a', tag, '-m', 'Some message'], + ]); + } + /// Return a Future that completes to the commit in the git repository /// referred to by [ref]. /// diff --git a/test/get/git/check_out_and_upgrade_test.dart b/test/get/git/check_out_and_upgrade_test.dart index 32a320b1a..c6dbc4d05 100644 --- a/test/get/git/check_out_and_upgrade_test.dart +++ b/test/get/git/check_out_and_upgrade_test.dart @@ -61,4 +61,84 @@ void main() { expect(packageSpec('foo'), isNot(originalFooSpec)); }); + + test('checks out and upgrades a package from with a tag-pattern', () async { + ensureGit(); + + final repo = d.git( + 'foo.git', + [d.libDir('foo'), d.libPubspec('foo', '1.0.0')], + ); + await repo.create(); + await repo.tag('v1'); + + await d.appDir( + dependencies: { + 'foo': { + 'git': {'url': '../foo.git', 'tag_pattern': 'v*'}, + 'version': '^1.0.0', + }, + }, + pubspec: { + 'environment': {'sdk': '^3.7.0'}, + }, + ).create(); + + await pubGet( + output: contains('+ foo 1.0.0'), + environment: { + '_PUB_TEST_SDK_VERSION': '3.7.0', + }, + ); + + // This should be found by `pub upgrade`. + await d.git( + 'foo.git', + [d.libDir('foo'), d.libPubspec('foo', '1.5.0')], + ).commit(); + await repo.tag('v1.5'); + + // The untagged version should not be found by `pub upgrade`. + await d.git( + 'foo.git', + [d.libDir('foo'), d.libPubspec('foo', '1.7.0')], + ).commit(); + + // This should be found by `pub upgrade --major-versions` + await d.git( + 'foo.git', + [d.libDir('foo'), d.libPubspec('foo', '2.0.0')], + ).commit(); + await repo.tag('v2'); + + // A version that is not tagged according to the pattern should not be + // chosen by the `upgrade --major-versions`. + await d.git( + 'foo.git', + [d.libDir('foo'), d.libPubspec('foo', '3.0.0')], + ).commit(); + await repo.tag('unrelatedTag'); + + await pubUpgrade( + output: allOf( + contains('> foo 1.5.0'), + contains('Changed 1 dependency!'), + ), + environment: { + '_PUB_TEST_SDK_VERSION': '3.7.0', + }, + ); + + await pubUpgrade( + args: ['--major-versions'], + output: allOf( + contains('> foo 2.0.0'), + contains('foo: ^1.0.0 -> ^2.0.0'), + contains('Changed 1 dependency!'), + ), + environment: { + '_PUB_TEST_SDK_VERSION': '3.7.0', + }, + ); + }); } diff --git a/test/get/git/ssh_url_test.dart b/test/get/git/ssh_url_test.dart index 01439d880..0f11e3e40 100644 --- a/test/get/git/ssh_url_test.dart +++ b/test/get/git/ssh_url_test.dart @@ -23,6 +23,7 @@ void main() { ref: 'main', path: 'abc/', containingDir: null, + tagPattern: null, ); expect( description.format(), From a482697c5b8c7dee1e6656207b804e813036d6f1 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Fri, 15 Nov 2024 13:37:27 +0000 Subject: [PATCH 02/12] Test for interdependencies --- test/get/git/tag_pattern_test.dart | 169 +++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 test/get/git/tag_pattern_test.dart diff --git a/test/get/git/tag_pattern_test.dart b/test/get/git/tag_pattern_test.dart new file mode 100644 index 000000000..3967ffe2c --- /dev/null +++ b/test/get/git/tag_pattern_test.dart @@ -0,0 +1,169 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:path/path.dart' as p; +import 'package:test/test.dart'; + +import '../../descriptor.dart' as d; +import '../../test_pub.dart'; + +void main() { + test( + 'Versions inside a tag_pattern dependency can depend on versions from ' + 'another commit', () async { + ensureGit(); + await d.git('foo.git', [ + d.libPubspec( + 'foo', + '1.0.0', + sdk: '^3.7.0', + deps: { + 'bar': { + 'git': {'url': p.join(d.sandbox, 'bar'), 'tag_pattern': '*'}, + 'version': '^2.0.0', + }, + }, + ), + ]).create(); + await d.git('foo.git', []).tag('a'); + + await d.git('foo.git', [ + d.libPubspec( + 'foo', + '2.0.0', + sdk: '^3.7.0', + deps: { + 'bar': { + 'git': {'url': p.join(d.sandbox, 'bar.git'), 'tag_pattern': '*'}, + 'version': '^1.0.0', + }, + }, + ), + ]).commit(); + await d.git('foo.git', []).tag('b'); + + await d.git('bar.git', [ + d.libPubspec( + 'bar', + '1.0.0', + sdk: '^3.7.0', + deps: { + 'foo': { + 'git': {'url': p.join(d.sandbox, 'bar.git'), 'tag_pattern': '*'}, + 'version': '^2.0.0', + }, + }, + ), + ]).create(); + await d.git('bar.git', []).tag('a'); + + await d.git('bar.git', [ + d.libPubspec( + 'bar', + '2.0.0', + sdk: '^3.7.0', + deps: { + 'foo': { + 'git': {'url': p.join(d.sandbox, 'foo.git'), 'tag_pattern': '*'}, + 'version': '^1.0.0', + }, + }, + ), + ]).commit(); + await d.git('bar.git', []).tag('b'); + + await d.appDir( + dependencies: { + 'foo': { + 'git': {'url': p.join(d.sandbox, 'foo.git'), 'tag_pattern': '*'}, + 'version': '^1.0.0', + }, + }, + pubspec: { + 'environment': {'sdk': '^3.7.0'}, + }, + ).create(); + + await pubGet( + output: allOf( + contains('+ foo 1.0.0'), + contains('+ bar 2.0.0'), + ), + environment: { + '_PUB_TEST_SDK_VERSION': '3.7.0', + }, + ); + }); + + test( + 'Versions inside a tag_pattern dependency can depend on versions from ' + 'another commit, via path-dependencies', () async { + ensureGit(); + + await d.git('repo.git', [ + d.dir('foo', [ + d.libPubspec( + 'foo', + '1.0.0', + deps: { + 'bar': {'path': '../bar', 'version': '^2.0.0'}, + }, + ), + ]), + d.dir('bar', [ + d.libPubspec( + 'bar', + '2.0.0', + deps: { + 'foo': {'path': '../foo', 'version': '^1.0.0'}, + }, + ), + ]), + ]).create(); + await d.git('repo.git', []).tag('a'); + await d.git('repo.git', [ + d.dir('foo', [ + d.libPubspec( + 'foo', + '2.0.0', + deps: { + 'bar': {'path': '../bar', 'version': '^2.0.0'}, + }, + ), + ]), + d.dir('bar', [ + d.libPubspec( + 'bar', + '1.0.0', + deps: { + 'foo': {'path': '../foo', 'version': '^1.0.0'}, + }, + ), + ]), + ]).commit(); + await d.git('repo.git', []).tag('b'); + + await d.appDir( + dependencies: { + 'foo': { + 'git': {'url': '../repo.git', 'tag_pattern': '*', 'path': 'foo'}, + 'version': '^1.0.0', + }, + }, + pubspec: { + 'environment': {'sdk': '^3.7.0'}, + }, + ).create(); + + await pubGet( + output: allOf( + contains('+ foo 1.0.0'), + contains('+ bar 2.0.0'), + ), + environment: { + '_PUB_TEST_SDK_VERSION': '3.7.0', + }, + ); + }); +} From 4cb927ebdde6b05d38de3434ae5071ba808b0f14 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Fri, 15 Nov 2024 14:55:04 +0000 Subject: [PATCH 03/12] Fix `pub add --git-tag-pattern` --- lib/src/command/add.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart index 305af541d..c227b283c 100644 --- a/lib/src/command/add.dart +++ b/lib/src/command/add.dart @@ -121,6 +121,11 @@ For example: help: 'Path of git package in repository', hide: true, ); + argParser.addOption( + 'git-tag-pattern', + help: 'The tag-pattern to search for versions in repository', + hide: true, + ); argParser.addOption( 'hosted-url', help: 'URL of package host server', @@ -527,6 +532,11 @@ Specify multiple sdk packages with descriptors.'''); if (gitUrl == null) { usageException('The `--git-url` is required for git dependencies.'); } + if (argResults.gitRef != null && argResults.tagPattern != null) { + usageException( + 'Cannot provide both `--git-ref` and `--git-tag-pattern`.', + ); + } /// Process the git options to return the simplest representation to be /// added to the pubspec. @@ -776,7 +786,7 @@ extension on ArgResults { bool get isDryRun => flag('dry-run'); String? get gitUrl => this['git-url'] as String?; String? get gitPath => this['git-path'] as String?; - String? get tagPattern => this['tag-pattern'] as String?; + String? get tagPattern => this['git-tag-pattern'] as String?; String? get gitRef => this['git-ref'] as String?; String? get hostedUrl => this['hosted-url'] as String?; From 9bc9e6566ab43c9bf610b159c6d4655ee96f4308 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Fri, 15 Nov 2024 14:55:33 +0000 Subject: [PATCH 04/12] Make `hasMultipleVersions` a property of the description --- lib/src/command/upgrade.dart | 2 +- lib/src/package_name.dart | 2 +- lib/src/solver/version_solver.dart | 5 ++- lib/src/source.dart | 11 +++-- lib/src/source/git.dart | 69 ++++++++++++++++++------------ lib/src/source/hosted.dart | 5 ++- lib/src/source/path.dart | 3 ++ lib/src/source/root.dart | 3 ++ lib/src/source/sdk.dart | 3 ++ lib/src/source/unknown.dart | 3 ++ 10 files changed, 68 insertions(+), 38 deletions(-) diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart index cc4b43a3c..140ec0db1 100644 --- a/lib/src/command/upgrade.dart +++ b/lib/src/command/upgrade.dart @@ -249,7 +249,7 @@ be direct 'dependencies' or 'dev_dependencies', following packages are not: final declaredUpgradableDependencies = [ ...package.dependencies.values, ...package.devDependencies.values, - ].where((dep) => dep.source.hasMultipleVersions); + ].where((dep) => dep.description.hasMultipleVersions); for (final dep in declaredUpgradableDependencies) { final resolvedPackage = resolvedPackages[dep.name]!; if (!toUpgrade.contains(dep.name)) { diff --git a/lib/src/package_name.dart b/lib/src/package_name.dart index c74ec4aaf..e05a22366 100644 --- a/lib/src/package_name.dart +++ b/lib/src/package_name.dart @@ -174,7 +174,7 @@ class PackageRange { bool get _showVersionConstraint { if (isRoot) return false; if (!constraint.isAny) return true; - return description.source.hasMultipleVersions; + return description.hasMultipleVersions; } /// Returns a copy of `this` with the same semantics, but with a `^`-style diff --git a/lib/src/solver/version_solver.dart b/lib/src/solver/version_solver.dart index a77d2c119..56dd04cbf 100644 --- a/lib/src/solver/version_solver.dart +++ b/lib/src/solver/version_solver.dart @@ -578,7 +578,10 @@ class VersionSolver { // can't be downgraded. if (_type == SolveType.downgrade) { final locked = _lockFile.packages[package]; - if (locked != null && !locked.source.hasMultipleVersions) return locked; + if (locked != null && + !locked.description.description.hasMultipleVersions) { + return locked; + } } if (_unlock.isEmpty || _unlock.contains(package)) return null; diff --git a/lib/src/source.dart b/lib/src/source.dart index 0c3fe70cc..b91d50756 100644 --- a/lib/src/source.dart +++ b/lib/src/source.dart @@ -51,12 +51,6 @@ abstract class Source { /// all sources. String get name; - /// Whether this source can choose between multiple versions of the same - /// package during version solving. - /// - /// Defaults to `false`. - bool get hasMultipleVersions => false; - /// Parses a [PackageRef] from a name and a user-provided [description]. /// /// When a [Pubspec] is parsed, it reads in the description for each @@ -190,6 +184,11 @@ abstract class Source { /// with a version constraint. abstract class Description { Source get source; + + /// Whether the source can choose between multiple versions of this + /// package during version solving. + bool get hasMultipleVersions; + Object? serializeForPubspec({ required String? containingDir, required LanguageVersion languageVersion, diff --git a/lib/src/source/git.dart b/lib/src/source/git.dart index ab8eef6f8..f7383a591 100644 --- a/lib/src/source/git.dart +++ b/lib/src/source/git.dart @@ -32,8 +32,6 @@ class GitSource extends CachedSource { @override final name = 'git'; - @override - final hasMultipleVersions = true; @override PackageRef parseRef( @@ -343,33 +341,41 @@ class GitSource extends CachedSource { return await _pool.withResource(() async { await _ensureRepoCache(description, cache); final path = _repoCachePath(description, cache); - final List revisions; + final result = []; if (description.tagPattern case final String tagPattern) { - revisions = await _listRevisionsWithTagPattern(path, tagPattern); + final revisions = await _listRevisionsWithTagPattern(path, tagPattern); + final seenVersions = {}; + for (final revision in revisions) { + final Pubspec pubspec; + try { + pubspec = await _describeUncached(ref, revision, cache); + result.add( + PackageId( + ref.name, + pubspec.version, + ResolvedGitDescription(description, revision), + ), + ); + } on Exception catch (e) { + log.fine('No mathcing pubspec at $revision, $e'); + continue; + } + final prev = seenVersions[pubspec.version]; + + if (prev != null) { + log.fine( + 'Repeated version ${pubspec.version} of ${ref.name} ' + 'at $revision and $prev', + ); + } + seenVersions[pubspec.version] = revision; + } + return result; } else { - revisions = [await _firstRevision(path, description.ref)]; - } + final revision = await _firstRevision(path, description.ref); - final result = []; - final seenVersions = {}; - for (final revision in revisions) { final Pubspec pubspec; - try { - pubspec = await _describeUncached(ref, revision, cache); - } on Exception { - log.fine( - 'Found no valid pubspec of ${ref.name} at $revision, ignoring,', - ); - continue; - } - final prev = seenVersions[pubspec.version]; - if (prev != null) { - log.fine( - 'Repeated version ${pubspec.version} of ${ref.name} ' - 'at $revision and $prev', - ); - } - seenVersions[pubspec.version] = revision; + pubspec = await _describeUncached(ref, revision, cache); result.add( PackageId( ref.name, @@ -377,8 +383,14 @@ class GitSource extends CachedSource { ResolvedGitDescription(description, revision), ), ); + return [ + PackageId( + ref.name, + pubspec.version, + ResolvedGitDescription(description, revision), + ), + ]; } - return result; }); } @@ -950,6 +962,9 @@ class GitDescription extends Description { } return p.prettyUri(url); } + + @override + bool get hasMultipleVersions => tagPattern != null; } class ResolvedGitDescription extends ResolvedDescription { @@ -979,7 +994,7 @@ class ResolvedGitDescription extends ResolvedDescription { return { 'url': url, 'ref': description.ref, - 'tag_pattern': description.tagPattern, + if (description.tagPattern != null) 'tag_pattern': description.tagPattern, 'resolved-ref': resolvedRef, 'path': description.path, }; diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart index c7d5a03b6..b3a37d65a 100644 --- a/lib/src/source/hosted.dart +++ b/lib/src/source/hosted.dart @@ -127,8 +127,6 @@ class HostedSource extends CachedSource { @override final name = 'hosted'; - @override - final hasMultipleVersions = true; static String pubDevUrl = 'https://pub.dev'; static String pubDartlangUrl = 'https://pub.dartlang.org'; @@ -1820,6 +1818,9 @@ class HostedDescription extends Description { @override HostedSource get source => HostedSource.instance; + + @override + bool get hasMultipleVersions => true; } class ResolvedHostedDescription extends ResolvedDescription { diff --git a/lib/src/source/path.dart b/lib/src/source/path.dart index 69fe5ce01..6f22c12e0 100644 --- a/lib/src/source/path.dart +++ b/lib/src/source/path.dart @@ -300,6 +300,9 @@ class PathDescription extends Description { @override int get hashCode => canonicalize(path).hashCode; + + @override + bool get hasMultipleVersions => false; } class ResolvedPathDescription extends ResolvedDescription { diff --git a/lib/src/source/root.dart b/lib/src/source/root.dart index 2513b4d0b..25a6c7627 100644 --- a/lib/src/source/root.dart +++ b/lib/src/source/root.dart @@ -121,4 +121,7 @@ class RootDescription extends Description { @override int get hashCode => 'root'.hashCode; + + @override + bool get hasMultipleVersions => false; } diff --git a/lib/src/source/sdk.dart b/lib/src/source/sdk.dart index 3dfa037f6..de7b3b1a9 100644 --- a/lib/src/source/sdk.dart +++ b/lib/src/source/sdk.dart @@ -188,6 +188,9 @@ class SdkDescription extends Description { bool operator ==(Object other) { return other is SdkDescription && other.sdk == sdk; } + + @override + bool get hasMultipleVersions => false; } class ResolvedSdkDescription extends ResolvedDescription { diff --git a/lib/src/source/unknown.dart b/lib/src/source/unknown.dart index 3ba7221bc..ecf4e84a2 100644 --- a/lib/src/source/unknown.dart +++ b/lib/src/source/unknown.dart @@ -113,6 +113,9 @@ class UnknownDescription extends Description { @override int get hashCode => Object.hash(source.name, json.encode(description)); + + @override + bool get hasMultipleVersions => false; } class ResolvedUnknownDescription extends ResolvedDescription { From 9a6517e1c39fd052f17ae4961803eca6d88ef048 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Thu, 27 Mar 2025 14:35:41 +0000 Subject: [PATCH 05/12] {{version}} --- lib/src/command/add.dart | 7 +- lib/src/command/dependency_services.dart | 2 +- lib/src/command/lish.dart | 3 +- lib/src/command/unpack.dart | 5 +- lib/src/entrypoint.dart | 4 +- lib/src/global_packages.dart | 2 +- lib/src/pubspec.dart | 14 +-- lib/src/source.dart | 2 +- lib/src/source/cached.dart | 2 +- lib/src/source/git.dart | 113 ++++++++++++------- lib/src/source/hosted.dart | 42 ++++--- lib/src/source/path.dart | 48 ++++---- lib/src/source/root.dart | 3 +- lib/src/source/sdk.dart | 5 +- lib/src/source/unknown.dart | 2 +- pubspec.lock | 14 +-- test/get/git/check_out_and_upgrade_test.dart | 8 +- test/get/git/tag_pattern_test.dart | 58 +++++++--- test/pubspec_test.dart | 83 +++++++------- test/version_solver_test.dart | 66 +++++++---- 20 files changed, 284 insertions(+), 199 deletions(-) diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart index c227b283c..eb5e9e2f2 100644 --- a/lib/src/command/add.dart +++ b/lib/src/command/add.dart @@ -279,7 +279,8 @@ Specify multiple sdk packages with descriptors.'''); location: Uri.parse(entrypoint.workPackage.pubspecPath), overridesFileContents: overridesFileContents, overridesLocation: Uri.file(overridesPath), - containingDescription: RootDescription(entrypoint.workPackage.dir), + containingDescription: + ResolvedRootDescription.fromDir(entrypoint.workPackage.dir), ), ) .acquireDependencies( @@ -563,7 +564,7 @@ Specify multiple sdk packages with descriptors.'''); ref = cache.sdk.parseRef( packageName, argResults.sdk, - containingDescription: RootDescription(p.current), + containingDescription: ResolvedRootDescription.fromDir(p.current), ); } else { ref = PackageRef( @@ -653,7 +654,7 @@ Specify multiple sdk packages with descriptors.'''); cache.sources, // Resolve relative paths relative to current, not where the // pubspec.yaml is. - containingDescription: RootDescription(p.current), + containingDescription: ResolvedRootDescription.fromDir(p.current), ); } on FormatException catch (e) { usageException('Failed parsing package specification: ${e.message}'); diff --git a/lib/src/command/dependency_services.dart b/lib/src/command/dependency_services.dart index 6fb2585af..d9be3632c 100644 --- a/lib/src/command/dependency_services.dart +++ b/lib/src/command/dependency_services.dart @@ -452,7 +452,7 @@ class DependencyServicesApplyCommand extends PubCommand { updatedPubspecs[package.dir].toString(), cache.sources, location: toUri(package.pubspecPath), - containingDescription: RootDescription(package.dir), + containingDescription: ResolvedRootDescription.fromDir(package.dir), ), ); // Resolve versions, this will update transitive dependencies that were diff --git a/lib/src/command/lish.dart b/lib/src/command/lish.dart index 892dc3861..822d1adfd 100644 --- a/lib/src/command/lish.dart +++ b/lib/src/command/lish.dart @@ -382,7 +382,8 @@ the \$PUB_HOSTED_URL environment variable.''', ), ), cache.sources, - containingDescription: RootDescription(p.dirname(archive)), + containingDescription: + ResolvedRootDescription.fromDir(p.dirname(archive)), ); } on FormatException catch (e) { dataError('Failed to read pubspec.yaml from archive: ${e.message}'); diff --git a/lib/src/command/unpack.dart b/lib/src/command/unpack.dart index 3ec9ddff6..b54267294 100644 --- a/lib/src/command/unpack.dart +++ b/lib/src/command/unpack.dart @@ -141,7 +141,8 @@ in a directory `foo-`. final pubspec = Pubspec.load( destinationDir, cache.sources, - containingDescription: RootDescription(destinationDir), + containingDescription: + ResolvedRootDescription.fromDir(destinationDir), ); final buffer = StringBuffer(); if (pubspec.resolution != Resolution.none) { @@ -219,7 +220,7 @@ Creating `pubspec_overrides.yaml` to resolve it without those overrides.''', // Resolve relative paths relative to current, not where the // pubspec.yaml is. location: p.toUri(p.join(p.current, 'descriptor')), - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); } on FormatException catch (e) { usageException('Failed parsing package specification: ${e.message}'); diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart index 7c8da54bc..c29d7755a 100644 --- a/lib/src/entrypoint.dart +++ b/lib/src/entrypoint.dart @@ -96,7 +96,7 @@ class Entrypoint { pubspec = Pubspec.load( dir, cache.sources, - containingDescription: RootDescription(dir), + containingDescription: ResolvedRootDescription.fromDir(dir), allowOverridesFile: true, ); } on FileException { @@ -118,7 +118,7 @@ class Entrypoint { cache.sources, expectedName: expectedName, allowOverridesFile: withPubspecOverrides, - containingDescription: RootDescription(path), + containingDescription: ResolvedRootDescription.fromDir(path), ), withPubspecOverrides: true, ); diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart index ab7fde78f..3d9e702b9 100644 --- a/lib/src/global_packages.dart +++ b/lib/src/global_packages.dart @@ -115,7 +115,7 @@ class GlobalPackages { if (path != null) 'path': path, if (ref != null) 'ref': ref, }, - containingDescription: RootDescription(p.current), + containingDescription: ResolvedRootDescription.fromDir(p.current), ); } on FormatException catch (e) { throw ApplicationException(e.message); diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart index 8c40b6d71..2db45504f 100644 --- a/lib/src/pubspec.dart +++ b/lib/src/pubspec.dart @@ -61,7 +61,7 @@ class Pubspec extends PubspecBase { /// It is used to resolve relative paths. And to resolve path-descriptions /// from a git dependency as git-descriptions. - final Description _containingDescription; + final ResolvedDescription _containingDescription; /// Directories of packages that should resolve together with this package. late List workspace = () { @@ -277,7 +277,7 @@ environment: SourceRegistry sources, { String? expectedName, bool allowOverridesFile = false, - required Description containingDescription, + required ResolvedDescription containingDescription, }) { final pubspecPath = p.join(packageDir, pubspecYamlFilename); final overridesPath = p.join(packageDir, pubspecOverridesFilename); @@ -322,7 +322,7 @@ environment: sources, expectedName: expectedName, allowOverridesFile: withPubspecOverrides, - containingDescription: RootDescription(dir), + containingDescription: ResolvedRootDescription.fromDir(dir), ); } @@ -355,7 +355,7 @@ environment: _overridesFileFields = null, // This is a dummy value. Dependencies should already be resolved, so we // never need to do relative resolutions. - _containingDescription = RootDescription('.'), + _containingDescription = ResolvedRootDescription.fromDir('.'), super( fields == null ? YamlMap() : YamlMap.wrap(fields), name: name, @@ -375,7 +375,7 @@ environment: YamlMap? overridesFields, String? expectedName, Uri? location, - required Description containingDescription, + required ResolvedDescription containingDescription, }) : _overridesFileFields = overridesFields, _includeDefaultSdkConstraint = true, _givenSdkConstraints = null, @@ -423,7 +423,7 @@ environment: Uri? location, String? overridesFileContents, Uri? overridesLocation, - required Description containingDescription, + required ResolvedDescription containingDescription, }) { late final YamlMap pubspecMap; YamlMap? overridesFileMap; @@ -567,7 +567,7 @@ Map _parseDependencies( SourceRegistry sources, LanguageVersion languageVersion, String? packageName, - Description containingDescription, { + ResolvedDescription containingDescription, { _FileType fileType = _FileType.pubspec, }) { final dependencies = {}; diff --git a/lib/src/source.dart b/lib/src/source.dart index b91d50756..37727e5b7 100644 --- a/lib/src/source.dart +++ b/lib/src/source.dart @@ -75,7 +75,7 @@ abstract class Source { PackageRef parseRef( String name, Object? description, { - required Description containingDescription, + required ResolvedDescription containingDescription, required LanguageVersion languageVersion, }); diff --git a/lib/src/source/cached.dart b/lib/src/source/cached.dart index 86dbc0a18..2cfa2a1ff 100644 --- a/lib/src/source/cached.dart +++ b/lib/src/source/cached.dart @@ -32,7 +32,7 @@ abstract class CachedSource extends Source { packageDir, cache.sources, expectedName: id.name, - containingDescription: id.description.description, + containingDescription: id.description, ); } diff --git a/lib/src/source/git.dart b/lib/src/source/git.dart index f7383a591..1c567d2b2 100644 --- a/lib/src/source/git.dart +++ b/lib/src/source/git.dart @@ -24,6 +24,8 @@ import 'cached.dart'; import 'path.dart'; import 'root.dart'; +typedef TaggedVersion = ({Version version, String commitId}); + /// A package source that gets packages from Git repos. class GitSource extends CachedSource { static GitSource instance = GitSource._(); @@ -37,7 +39,7 @@ class GitSource extends CachedSource { PackageRef parseRef( String name, Object? description, { - Description? containingDescription, + ResolvedDescription? containingDescription, LanguageVersion? languageVersion, }) { String url; @@ -101,7 +103,7 @@ class GitSource extends CachedSource { } } - final containingDir = switch (containingDescription) { + final containingDir = switch (containingDescription?.description) { RootDescription(path: final path) => path, PathDescription(path: final path) => path, _ => null, @@ -283,14 +285,16 @@ class GitSource extends CachedSource { final path = _repoCachePath(description, cache); final revision = tagPattern != null - ? (await _listRevisionsWithTagPattern(path, tagPattern)).last + ? (await _listTaggedVersions(path, compileTagPattern(tagPattern))) + .last + .commitId : await _firstRevision(path, description.ref); final resolvedDescription = ResolvedGitDescription(description, revision); return Pubspec.parse( await _showFileAtRevision(resolvedDescription, 'pubspec.yaml', cache), cache.sources, - containingDescription: description, + containingDescription: resolvedDescription, ).name; }); } @@ -343,32 +347,16 @@ class GitSource extends CachedSource { final path = _repoCachePath(description, cache); final result = []; if (description.tagPattern case final String tagPattern) { - final revisions = await _listRevisionsWithTagPattern(path, tagPattern); - final seenVersions = {}; - for (final revision in revisions) { - final Pubspec pubspec; - try { - pubspec = await _describeUncached(ref, revision, cache); - result.add( - PackageId( - ref.name, - pubspec.version, - ResolvedGitDescription(description, revision), - ), - ); - } on Exception catch (e) { - log.fine('No mathcing pubspec at $revision, $e'); - continue; - } - final prev = seenVersions[pubspec.version]; - - if (prev != null) { - log.fine( - 'Repeated version ${pubspec.version} of ${ref.name} ' - 'at $revision and $prev', - ); - } - seenVersions[pubspec.version] = revision; + final versions = + await _listTaggedVersions(path, compileTagPattern(tagPattern)); + for (final version in versions) { + result.add( + PackageId( + ref.name, + version.version, + ResolvedGitDescription(description, version.commitId), + ), + ); } return result; } else { @@ -397,18 +385,26 @@ class GitSource extends CachedSource { /// Since we don't have an easy way to read from a remote Git repo, this /// just installs [id] into the system cache, then describes it from there. @override - Future describeUncached(PackageId id, SystemCache cache) { + Future describeUncached(PackageId id, SystemCache cache) async { final description = id.description; if (description is! ResolvedGitDescription) { throw StateError('Called with wrong ref'); } - return _pool.withResource( + final pubspec = await _pool.withResource( () => _describeUncached( id.toRef(), description.resolvedRef, cache, ), ); + if (pubspec.version != id.version) { + throw PackageNotFoundException( + 'Expected ${id.name} version ${id.version} ' + 'at commit ${description.resolvedRef}, ' + 'found ${pubspec.version}.', + ); + } + return pubspec; } final Map<(PackageRef, String), Pubspec> _pubspecAtRevisionCache = {}; @@ -426,16 +422,16 @@ class GitSource extends CachedSource { } return _pubspecAtRevisionCache[(ref, revision)] ??= await () async { await _ensureRevision(description, revision, cache); - + final resolvedDescription = ResolvedGitDescription(description, revision); return Pubspec.parse( await _showFileAtRevision( - ResolvedGitDescription(description, revision), + resolvedDescription, 'pubspec.yaml', cache, ), cache.sources, expectedName: ref.name, - containingDescription: ref.description, + containingDescription: resolvedDescription, ); }(); } @@ -736,16 +732,27 @@ class GitSource extends CachedSource { p.join(revisionCachePath, '.git/pub-packages'); /// - Future> _listRevisionsWithTagPattern( + Future> _listTaggedVersions( String path, - String tagPattern, + RegExp compiledTagPattern, ) async { - return (await git.run( - ['tag', '--list', tagPattern, '--format', '%(objectname)'], + final output = await git.run( + ['tag', '--list', '--format', '%(refname:lstrip=2) %(objectname)'], workingDir: path, - )) - .trim() - .split('\n'); + ); + final lines = output.trim().split('\n'); + final result = []; + for (final line in lines) { + final parts = line.split(' '); + if (parts.length != 2) { + throw PackageNotFoundException('Bad output from `git tag --list`'); + } + final match = compiledTagPattern.firstMatch(parts[0]); + if (match == null) continue; + final version = Version.parse(match[1]!); + result.add((version: version, commitId: parts[1])); + } + return result; } /// Runs "git rev-list" on [reference] in [path] and returns the first result. @@ -875,6 +882,8 @@ class GitDescription extends Description { /// Represented as a relative url. final String path; + late final RegExp compiledTagPattern = compileTagPattern(tagPattern!); + GitDescription.raw({ required this.url, required this.relative, @@ -994,7 +1003,7 @@ class ResolvedGitDescription extends ResolvedDescription { return { 'url': url, 'ref': description.ref, - if (description.tagPattern != null) 'tag_pattern': description.tagPattern, + if (description.tagPattern != null) 'tag-pattern': description.tagPattern, 'resolved-ref': resolvedRef, 'path': description.path, }; @@ -1022,3 +1031,21 @@ String _gitDirArg(String path) { Platform.isWindows ? path.replaceAll('\\', '/') : path; return '--git-dir=$forwardSlashPath'; } + +final tagPatternPattern = RegExp(r'^(.*){{version}}(.*)$'); + +// Adapted from pub_semver-2.1.4/lib/src/version.dart +const versionPattern = r'(\d+)\.(\d+)\.(\d+)' // Version number. + r'(-([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?' // Pre-release. + r'(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?'; // build + +RegExp compileTagPattern(String tagPattern) { + final match = tagPatternPattern.firstMatch(tagPattern); + if (match == null) { + throw const FormatException('Tag patterns must contain {{version})'); + } + final before = RegExp.escape(match[1]!); + final after = RegExp.escape(match[2]!); + + return RegExp(r'^' '$before($versionPattern)$after' r'$'); +} diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart index b3a37d65a..854ea3af1 100644 --- a/lib/src/source/hosted.dart +++ b/lib/src/source/hosted.dart @@ -231,7 +231,7 @@ class HostedSource extends CachedSource { PackageRef parseRef( String name, Object? description, { - required Description containingDescription, + required ResolvedDescription containingDescription, required LanguageVersion languageVersion, }) { return PackageRef( @@ -413,17 +413,20 @@ class HostedSource extends CachedSource { if (pubspecData is! Map) { throw const FormatException('pubspec must be a map'); } + + final archiveSha256 = map['archive_sha256']; + if (archiveSha256 != null && archiveSha256 is! String) { + throw const FormatException('archive_sha256 must be a String'); + } + final parsedContentHash = _parseContentHash(archiveSha256 as String?); final pubspec = Pubspec.fromMap( pubspecData, cache.sources, expectedName: ref.name, location: location, - containingDescription: description, + containingDescription: + ResolvedHostedDescription(description, sha256: parsedContentHash), ); - final archiveSha256 = map['archive_sha256']; - if (archiveSha256 != null && archiveSha256 is! String) { - throw const FormatException('archive_sha256 must be a String'); - } final archiveUrl = map['archive_url']; if (archiveUrl is! String) { throw const FormatException('archive_url must be a String'); @@ -448,7 +451,6 @@ class HostedSource extends CachedSource { } advisoriesDate = DateTime.parse(advisoriesUpdated); } - final status = PackageStatus( isDiscontinued: isDiscontinued, discontinuedReplacedBy: replacedBy, @@ -460,7 +462,7 @@ class HostedSource extends CachedSource { pubspec, Uri.parse(archiveUrl), status, - _parseContentHash(archiveSha256 as String?), + parsedContentHash, ); }).toList(); } @@ -537,16 +539,20 @@ class HostedSource extends CachedSource { if (listing == null || listing.isEmpty) return; final latestVersion = maxBy<_VersionInfo, Version>(listing, (e) => e.version)!; - final dependencies = latestVersion.pubspec.dependencies.values; - unawaited( - withDependencyType(DependencyType.none, () async { - for (final packageRange in dependencies) { - if (packageRange.source is HostedSource) { - preschedule!(_RefAndCache(packageRange.toRef(), cache)); + try { + final dependencies = latestVersion.pubspec.dependencies.values; + unawaited( + withDependencyType(DependencyType.none, () async { + for (final packageRange in dependencies) { + if (packageRange.source is HostedSource) { + preschedule!(_RefAndCache(packageRange.toRef(), cache)); + } } - } - }), - ); + }), + ); + } on FormatException { + // Ignore malformed dependencies. + } } final cache = refAndCache.cache; @@ -1647,7 +1653,7 @@ See $contentHashesDocumentationUrl. containingDescription: // Dummy description. As we never use the dependencies, they don't // need to be resolved. - RootDescription('.'), + ResolvedRootDescription.fromDir('.'), ); final errors = pubspec.dependencyErrors; if (errors.isNotEmpty) { diff --git a/lib/src/source/path.dart b/lib/src/source/path.dart index 6f22c12e0..d1e9c43fb 100644 --- a/lib/src/source/path.dart +++ b/lib/src/source/path.dart @@ -54,7 +54,7 @@ class PathSource extends Source { PackageRef parseRef( String name, Object? description, { - required Description containingDescription, + required ResolvedDescription containingDescription, LanguageVersion? languageVersion, }) { if (description is! String) { @@ -64,31 +64,31 @@ class PathSource extends Source { // Resolve the path relative to the containing file path, and remember // whether the original path was relative or absolute. final isRelative = p.isRelative(dir); - - if (containingDescription is PathDescription) { + if (containingDescription is ResolvedPathDescription) { return PackageRef( name, PathDescription( isRelative - ? p.join(p.absolute(containingDescription.path), dir) + ? p.join(p.absolute(containingDescription.description.path), dir) : dir, isRelative, ), ); - } else if (containingDescription is RootDescription) { + } else if (containingDescription is ResolvedRootDescription) { return PackageRef( name, PathDescription( p.normalize( p.join( - p.absolute(containingDescription.path), + p.absolute(containingDescription.description.path), description, ), ), isRelative, ), ); - } else if (containingDescription is GitDescription) { + } else if (containingDescription is ResolvedGitDescription) { + print('containing: $containingDescription $name ${description} '); if (!isRelative) { throw FormatException( '"$description" is an absolute path, ' @@ -97,7 +97,7 @@ class PathSource extends Source { } final resolvedPath = p.url.normalize( p.url.joinAll([ - containingDescription.path, + containingDescription.description.path, ...p.posix.split(dir), ]), ); @@ -110,10 +110,11 @@ class PathSource extends Source { return PackageRef( name, GitDescription.raw( - url: containingDescription.url, - relative: containingDescription.relative, - ref: containingDescription.ref, - tagPattern: containingDescription.tagPattern, + url: containingDescription.description.url, + relative: containingDescription.description.relative, + // Always refer to the same commit as the containing pubspec. + ref: containingDescription.resolvedRef, + tagPattern: null, path: resolvedPath, ), ); @@ -190,11 +191,12 @@ class PathSource extends Source { } // There's only one package ID for a given path. We just need to find the // version. - final pubspec = _loadPubspec(ref, cache); + final resolvedDescription = ResolvedPathDescription(description); + final pubspec = _loadPubspec(ref, resolvedDescription, cache); final id = PackageId( ref.name, pubspec.version, - ResolvedPathDescription(description), + resolvedDescription, ); // Store the pubspec in memory if we need to refer to it again. cache.cachedPubspecs[id] = pubspec; @@ -203,14 +205,18 @@ class PathSource extends Source { @override Future doDescribe(PackageId id, SystemCache cache) async => - _loadPubspec(id.toRef(), cache); + _loadPubspec( + id.toRef(), + id.description as ResolvedPathDescription, + cache, + ); - Pubspec _loadPubspec(PackageRef ref, SystemCache cache) { - final description = ref.description; - if (description is! PathDescription) { - throw ArgumentError('Wrong source'); - } - final dir = _validatePath(ref.name, description); + Pubspec _loadPubspec( + PackageRef ref, + ResolvedPathDescription description, + SystemCache cache, + ) { + final dir = _validatePath(ref.name, description.description); return Pubspec.load( dir, cache.sources, diff --git a/lib/src/source/root.dart b/lib/src/source/root.dart index 25a6c7627..cf3a131ea 100644 --- a/lib/src/source/root.dart +++ b/lib/src/source/root.dart @@ -69,7 +69,7 @@ class RootSource extends Source { PackageRef parseRef( String name, Object? description, { - required Description containingDescription, + required ResolvedDescription containingDescription, required LanguageVersion languageVersion, }) { throw UnsupportedError('Trying to parse a root package description.'); @@ -81,6 +81,7 @@ class ResolvedRootDescription extends ResolvedDescription { RootDescription get description => super.description as RootDescription; ResolvedRootDescription(RootDescription super.description); + ResolvedRootDescription.fromDir(String dir) : super(RootDescription(dir)); @override Object? serializeForLockfile({required String? containingDir}) { diff --git a/lib/src/source/sdk.dart b/lib/src/source/sdk.dart index de7b3b1a9..23877b3fa 100644 --- a/lib/src/source/sdk.dart +++ b/lib/src/source/sdk.dart @@ -29,7 +29,7 @@ class SdkSource extends Source { PackageRef parseRef( String name, Object? description, { - required Description containingDescription, + required ResolvedDescription containingDescription, LanguageVersion? languageVersion, }) { if (description is! String) { @@ -94,7 +94,8 @@ class SdkSource extends Source { _verifiedPackagePath(ref), cache.sources, expectedName: ref.name, - containingDescription: ref.description, + containingDescription: + ResolvedSdkDescription(ref.description as SdkDescription), ); /// Validate that there are no non-sdk dependencies if the SDK does not diff --git a/lib/src/source/unknown.dart b/lib/src/source/unknown.dart index ecf4e84a2..b5e9a96a7 100644 --- a/lib/src/source/unknown.dart +++ b/lib/src/source/unknown.dart @@ -37,7 +37,7 @@ class UnknownSource extends Source { PackageRef parseRef( String name, Object? description, { - required Description containingDescription, + required ResolvedDescription containingDescription, LanguageVersion? languageVersion, }) => PackageRef(name, UnknownDescription(description, this)); diff --git a/pubspec.lock b/pubspec.lock index dff072b5a..55f8da2a2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,23 +5,23 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77" + sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" url: "https://pub.dev" source: hosted - version: "73.0.0" + version: "76.0.0" _macros: dependency: transitive description: dart source: sdk - version: "0.3.2" + version: "0.3.3" analyzer: dependency: "direct main" description: name: analyzer - sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a" + sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" url: "https://pub.dev" source: hosted - version: "6.8.0" + version: "6.11.0" args: dependency: "direct main" description: @@ -194,10 +194,10 @@ packages: dependency: transitive description: name: macros - sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" url: "https://pub.dev" source: hosted - version: "0.1.2-main.4" + version: "0.1.3-main.0" matcher: dependency: transitive description: diff --git a/test/get/git/check_out_and_upgrade_test.dart b/test/get/git/check_out_and_upgrade_test.dart index c6dbc4d05..8c5e95870 100644 --- a/test/get/git/check_out_and_upgrade_test.dart +++ b/test/get/git/check_out_and_upgrade_test.dart @@ -70,12 +70,12 @@ void main() { [d.libDir('foo'), d.libPubspec('foo', '1.0.0')], ); await repo.create(); - await repo.tag('v1'); + await repo.tag('v1.0.0'); await d.appDir( dependencies: { 'foo': { - 'git': {'url': '../foo.git', 'tag_pattern': 'v*'}, + 'git': {'url': '../foo.git', 'tag_pattern': 'v{{version}}'}, 'version': '^1.0.0', }, }, @@ -96,7 +96,7 @@ void main() { 'foo.git', [d.libDir('foo'), d.libPubspec('foo', '1.5.0')], ).commit(); - await repo.tag('v1.5'); + await repo.tag('v1.5.0'); // The untagged version should not be found by `pub upgrade`. await d.git( @@ -109,7 +109,7 @@ void main() { 'foo.git', [d.libDir('foo'), d.libPubspec('foo', '2.0.0')], ).commit(); - await repo.tag('v2'); + await repo.tag('v2.0.0'); // A version that is not tagged according to the pattern should not be // chosen by the `upgrade --major-versions`. diff --git a/test/get/git/tag_pattern_test.dart b/test/get/git/tag_pattern_test.dart index 3967ffe2c..499d2e0b5 100644 --- a/test/get/git/tag_pattern_test.dart +++ b/test/get/git/tag_pattern_test.dart @@ -20,13 +20,16 @@ void main() { sdk: '^3.7.0', deps: { 'bar': { - 'git': {'url': p.join(d.sandbox, 'bar'), 'tag_pattern': '*'}, + 'git': { + 'url': p.join(d.sandbox, 'bar'), + 'tag_pattern': '{{version}}', + }, 'version': '^2.0.0', }, }, ), ]).create(); - await d.git('foo.git', []).tag('a'); + await d.git('foo.git', []).tag('1.0.0'); await d.git('foo.git', [ d.libPubspec( @@ -35,13 +38,16 @@ void main() { sdk: '^3.7.0', deps: { 'bar': { - 'git': {'url': p.join(d.sandbox, 'bar.git'), 'tag_pattern': '*'}, + 'git': { + 'url': p.join(d.sandbox, 'bar.git'), + 'tag_pattern': '{{version}}', + }, 'version': '^1.0.0', }, }, ), ]).commit(); - await d.git('foo.git', []).tag('b'); + await d.git('foo.git', []).tag('2.0.0'); await d.git('bar.git', [ d.libPubspec( @@ -50,13 +56,16 @@ void main() { sdk: '^3.7.0', deps: { 'foo': { - 'git': {'url': p.join(d.sandbox, 'bar.git'), 'tag_pattern': '*'}, + 'git': { + 'url': p.join(d.sandbox, 'bar.git'), + 'tag_pattern': '{{version}}', + }, 'version': '^2.0.0', }, }, ), ]).create(); - await d.git('bar.git', []).tag('a'); + await d.git('bar.git', []).tag('1.0.0'); await d.git('bar.git', [ d.libPubspec( @@ -65,18 +74,24 @@ void main() { sdk: '^3.7.0', deps: { 'foo': { - 'git': {'url': p.join(d.sandbox, 'foo.git'), 'tag_pattern': '*'}, + 'git': { + 'url': p.join(d.sandbox, 'foo.git'), + 'tag_pattern': '{{version}}', + }, 'version': '^1.0.0', }, }, ), ]).commit(); - await d.git('bar.git', []).tag('b'); + await d.git('bar.git', []).tag('2.0.0'); await d.appDir( dependencies: { 'foo': { - 'git': {'url': p.join(d.sandbox, 'foo.git'), 'tag_pattern': '*'}, + 'git': { + 'url': p.join(d.sandbox, 'foo.git'), + 'tag_pattern': '{{version}}', + }, 'version': '^1.0.0', }, }, @@ -97,8 +112,8 @@ void main() { }); test( - 'Versions inside a tag_pattern dependency can depend on versions from ' - 'another commit, via path-dependencies', () async { + 'Versions inside a tag_pattern dependency cannot depend on ' + 'version from another commit via path-dependencies', () async { ensureGit(); await d.git('repo.git', [ @@ -121,7 +136,9 @@ void main() { ), ]), ]).create(); - await d.git('repo.git', []).tag('a'); + await d.git('repo.git', []).tag('foo-1.0.0'); + await d.git('repo.git', []).tag('bar-2.0.0'); + await d.git('repo.git', [ d.dir('foo', [ d.libPubspec( @@ -142,12 +159,17 @@ void main() { ), ]), ]).commit(); - await d.git('repo.git', []).tag('b'); + await d.git('repo.git', []).tag('foo-2.0.0'); + await d.git('repo.git', []).tag('bar-1.0.0'); await d.appDir( dependencies: { 'foo': { - 'git': {'url': '../repo.git', 'tag_pattern': '*', 'path': 'foo'}, + 'git': { + 'url': '../repo.git', + 'tag_pattern': 'foo-{{version}}', + 'path': 'foo', + }, 'version': '^1.0.0', }, }, @@ -157,10 +179,10 @@ void main() { ).create(); await pubGet( - output: allOf( - contains('+ foo 1.0.0'), - contains('+ bar 2.0.0'), - ), + error: matches(r'Because foo from git ../repo.git at HEAD in foo ' + r'depends on bar \^2.0.0 from git ' + r'which depends on foo from git ../repo.git at [a-f0-9]* in foo, ' + r'foo <2.0.0 from git is forbidden'), environment: { '_PUB_TEST_SDK_VERSION': '3.7.0', }, diff --git a/test/pubspec_test.dart b/test/pubspec_test.dart index c181fcc0c..ec14403da 100644 --- a/test/pubspec_test.dart +++ b/test/pubspec_test.dart @@ -25,7 +25,7 @@ void main() { void Function(Pubspec) fn, { String? expectedContains, String? hintContains, - Description? containingDescription, + ResolvedDescription? containingDescription, }) { var expectation = const TypeMatcher(); if (expectedContains != null) { @@ -46,7 +46,8 @@ void main() { final pubspec = Pubspec.parse( contents, sources, - containingDescription: containingDescription ?? RootDescription('.'), + containingDescription: + containingDescription ?? ResolvedRootDescription.fromDir('.'), ); expect(() => fn(pubspec), throwsA(expectation)); } @@ -56,7 +57,7 @@ void main() { Pubspec.parse( 'version: not a semver', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); }); @@ -68,7 +69,7 @@ void main() { 'name: foo', sources, expectedName: 'bar', - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ), throwsPubspecException, ); @@ -82,7 +83,7 @@ void main() { '{}', sources, expectedName: 'bar', - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ), throwsPubspecException, ); @@ -99,7 +100,7 @@ dependencies: version: ">=1.2.3 <3.4.5" ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); final foo = pubspec.dependencies['foo']!; @@ -120,7 +121,7 @@ dependencies: version: ">=1.2.3 <0.0.0" ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); final foo = pubspec.dependencies['foo']!; @@ -134,7 +135,7 @@ dependencies: dependencies: ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect(pubspec.dependencies, isEmpty); @@ -151,7 +152,7 @@ dev_dependencies: version: ">=1.2.3 <3.4.5" ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); final foo = pubspec.devDependencies['foo']!; @@ -167,7 +168,7 @@ dev_dependencies: dev_dependencies: ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect(pubspec.devDependencies, isEmpty); @@ -184,7 +185,7 @@ dependency_overrides: version: ">=1.2.3 <3.4.5" ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); final foo = pubspec.dependencyOverrides['foo']!; @@ -200,7 +201,7 @@ dependency_overrides: dependency_overrides: ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect(pubspec.dependencyOverrides, isEmpty); @@ -214,7 +215,7 @@ dependencies: unknown: blah ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); final foo = pubspec.dependencies['foo']!; @@ -230,7 +231,7 @@ dependencies: version: 1.2.3 ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); final foo = pubspec.dependencies['foo']!; @@ -363,7 +364,7 @@ environment: workspace: ['a', 'b', 'c'] ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ).workspace, ['a', 'b', 'c'], ); @@ -378,7 +379,7 @@ environment: resolution: workspace ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ).resolution, Resolution.workspace, ); @@ -411,7 +412,7 @@ environment: resolution: workspace ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ).name, 'foo', ); @@ -477,7 +478,7 @@ resolution: "sometimes"''', # See https://dart.dev/tools/pub/cmd for details ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect(pubspec.version, equals(Version.none)); expect(pubspec.dependencies, isEmpty); @@ -490,13 +491,15 @@ name: pkg dependencies: from_path: {path: non_local_path} ''', - containingDescription: HostedDescription('foo', 'https://pub.dev'), + containingDescription: ResolvedHostedDescription( + HostedDescription('foo', 'https://pub.dev'), + sha256: null, + ), (pubspec) => pubspec.dependencies, expectedContains: 'Invalid description in the "pkg" pubspec on the "from_path" ' - 'dependency: "non_local_path" is a relative path, ' - 'but this isn\'t a ' - 'local pubspec.', + 'dependency: "non_local_path" is a path, but ' + 'this isn\'t a local pubspec.', ); }); @@ -512,7 +515,7 @@ dependencies: name: bar ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); final foo = pubspec.dependencies['foo']!; @@ -541,7 +544,7 @@ dependencies: url: https://example.org/pub/ ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); final foo = pubspec.dependencies['foo']!; @@ -569,7 +572,7 @@ dependencies: hosted: https://example.org/pub/ ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); final foo = pubspec.dependencies['foo']!; @@ -597,7 +600,7 @@ dependencies: hosted: bar ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); final foo = pubspec.dependencies['foo']!; @@ -627,7 +630,7 @@ dependencies: hosted: https://example.org/pub/ ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect( @@ -648,7 +651,7 @@ dependencies: foo: ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); final foo = pubspec.dependencies['foo']!; @@ -758,7 +761,7 @@ dependencies: final pubspec = Pubspec.parse( 'name: testing', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect( pubspec.dartSdkConstraint.effectiveConstraint, @@ -773,7 +776,7 @@ dependencies: final pubspec = Pubspec.parse( '', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect( pubspec.dartSdkConstraint.effectiveConstraint, @@ -791,7 +794,7 @@ dependencies: sdk: ">1.0.0" ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect( pubspec.dartSdkConstraint.effectiveConstraint, @@ -810,7 +813,7 @@ dependencies: sdk: ">3.0.0" ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect( pubspec.sdkConstraints, @@ -839,7 +842,7 @@ environment: fuchsia: ^5.6.7 ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect( pubspec.sdkConstraints, @@ -903,7 +906,7 @@ environment: final pubspec = Pubspec.parse( '', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect(pubspec.publishTo, isNull); }); @@ -921,7 +924,7 @@ environment: publish_to: http://example.com ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect(pubspec.publishTo, equals('http://example.com')); }); @@ -932,7 +935,7 @@ publish_to: http://example.com publish_to: none ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect(pubspec.publishTo, equals('none')); }); @@ -957,7 +960,7 @@ publish_to: none final pubspec = Pubspec.parse( '', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect(pubspec.executables, isEmpty); }); @@ -969,7 +972,7 @@ executables: abcDEF-123_: "abc DEF-123._" ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect(pubspec.executables['abcDEF-123_'], equals('abc DEF-123._')); }); @@ -1023,7 +1026,7 @@ executables: command: ''', sources, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); expect(pubspec.executables['command'], equals('command')); }); @@ -1042,7 +1045,7 @@ dependency_overrides: sources, overridesFileContents: overridesContents, overridesLocation: Uri.parse('file:///pubspec_overrides.yaml'), - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); } diff --git a/test/version_solver_test.dart b/test/version_solver_test.dart index a2bd6a7ed..31d7e5c6d 100644 --- a/test/version_solver_test.dart +++ b/test/version_solver_test.dart @@ -516,26 +516,32 @@ Because myapp depends on foo ^1.0.0 which doesn't match any versions, version so test('mismatched sources', () async { await d.dir('shared', [d.libPubspec('shared', '1.0.0')]).create(); - - await servePackages() - ..serve('foo', '1.0.0', deps: {'shared': '1.0.0'}) - ..serve( + await d.dir('bar', [ + d.libPubspec( 'bar', '1.0.0', deps: { 'shared': {'path': p.join(d.sandbox, 'shared')}, }, - ) + ), + ]).create(); + await servePackages() + ..serve('foo', '1.0.0', deps: {'shared': '1.0.0'}) ..serve('shared', '1.0.0'); - await d.appDir(dependencies: {'foo': '1.0.0', 'bar': '1.0.0'}).create(); + await d.appDir( + dependencies: { + 'foo': '1.0.0', + 'bar': {'path': '../bar'}, + }, + ).create(); await expectResolves( error: equalsIgnoringWhitespace(''' - Because every version of bar depends on shared from path and every - version of foo depends on shared from hosted, bar is incompatible with - foo. - So, because myapp depends on both foo 1.0.0 and bar 1.0.0, version - solving failed. + Because every version of bar from path depends on shared + from path and every version of foo depends on shared from hosted, + bar from path is incompatible with foo. +So, because myapp depends on both foo 1.0.0 and bar from path, +version solving failed. '''), ); }); @@ -876,7 +882,7 @@ void backtracking() { // dependencies are traversed breadth-first (all of myapps's immediate deps // before any other their deps). // - // This means it doesn't discover the source conflict until after selecting + // This means it doesn't discover the version conflict until after selecting // c. When that happens, it should backjump past c instead of trying older // versions of it since they aren't related to the conflict. test('successful backjump to conflicting source', () async { @@ -884,12 +890,13 @@ void backtracking() { await servePackages() ..serve('a', '1.0.0') + ..serve('a', '2.0.0') ..serve('b', '1.0.0', deps: {'a': 'any'}) ..serve( 'b', '2.0.0', deps: { - 'a': {'path': p.join(d.sandbox, 'a')}, + 'a': '^2.0.0', }, ) ..serve('c', '1.0.0') @@ -898,7 +905,8 @@ void backtracking() { ..serve('c', '4.0.0') ..serve('c', '5.0.0'); - await d.appDir(dependencies: {'a': 'any', 'b': 'any', 'c': 'any'}).create(); + await d + .appDir(dependencies: {'a': '1.0.0', 'b': 'any', 'c': 'any'}).create(); await expectResolves(result: {'a': '1.0.0', 'b': '1.0.0', 'c': '5.0.0'}); }); @@ -933,29 +941,37 @@ void backtracking() { // fail in this case with no backtracking. test('failing backjump to conflicting source', () async { await d.dir('a', [d.libPubspec('a', '1.0.0')]).create(); - - await servePackages() - ..serve('a', '1.0.0') - ..serve( + await d.dir('b', [ + d.libPubspec( 'b', '1.0.0', deps: { 'a': {'path': p.join(d.sandbox, 'shared')}, }, - ) + ), + ]).create(); + + await servePackages() + ..serve('a', '1.0.0') ..serve('c', '1.0.0') ..serve('c', '2.0.0') ..serve('c', '3.0.0') ..serve('c', '4.0.0') ..serve('c', '5.0.0'); - await d.appDir(dependencies: {'a': 'any', 'b': 'any', 'c': 'any'}).create(); + await d.appDir( + dependencies: { + 'a': 'any', + 'b': {'path': '../b'}, + 'c': 'any', + }, + ).create(); await expectResolves( error: equalsIgnoringWhitespace(''' - Because every version of b depends on a from path and myapp depends on - a from hosted, b is forbidden. - So, because myapp depends on b any, version solving failed. - '''), +Because every version of b from path depends on a from path +and myapp depends on a from hosted, b from path is forbidden. +So, because myapp depends on b from path, version solving failed. +'''), ); }); @@ -1987,7 +2003,7 @@ Future expectResolves({ final resultPubspec = Pubspec.fromMap( {'dependencies': result}, registry, - containingDescription: RootDescription('.'), + containingDescription: ResolvedRootDescription.fromDir('.'), ); final ids = {...lockFile.packages}; From 71e0f0f881b2bfd5d65aad45fd30546377541978 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Thu, 27 Mar 2025 15:27:52 +0000 Subject: [PATCH 06/12] Format --- lib/src/command/add.dart | 5 +- lib/src/command/lish.dart | 5 +- lib/src/command/unpack.dart | 5 +- lib/src/source/git.dart | 6 +- lib/src/source/sdk.dart | 5 +- test/get/git/check_out_and_upgrade_test.dart | 81 +++++++++--------- test/get/git/tag_pattern_test.dart | 87 ++++++++++---------- test/version_solver_test.dart | 41 +++++---- 8 files changed, 114 insertions(+), 121 deletions(-) diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart index 892951dca..49352e791 100644 --- a/lib/src/command/add.dart +++ b/lib/src/command/add.dart @@ -280,8 +280,9 @@ Specify multiple sdk packages with descriptors.'''); location: Uri.parse(entrypoint.workPackage.pubspecPath), overridesFileContents: overridesFileContents, overridesLocation: Uri.file(overridesPath), - containingDescription: - ResolvedRootDescription.fromDir(entrypoint.workPackage.dir), + containingDescription: ResolvedRootDescription.fromDir( + entrypoint.workPackage.dir, + ), ), ) .acquireDependencies( diff --git a/lib/src/command/lish.dart b/lib/src/command/lish.dart index 15ca6242c..a2d405f87 100644 --- a/lib/src/command/lish.dart +++ b/lib/src/command/lish.dart @@ -395,8 +395,9 @@ the \$PUB_HOSTED_URL environment variable.'''); ), ), cache.sources, - containingDescription: - ResolvedRootDescription.fromDir(p.dirname(archive)), + containingDescription: ResolvedRootDescription.fromDir( + p.dirname(archive), + ), ); } on FormatException catch (e) { dataError('Failed to read pubspec.yaml from archive: ${e.message}'); diff --git a/lib/src/command/unpack.dart b/lib/src/command/unpack.dart index 82138fe2e..98821205a 100644 --- a/lib/src/command/unpack.dart +++ b/lib/src/command/unpack.dart @@ -144,8 +144,9 @@ in a directory `foo-`. final pubspec = Pubspec.load( destinationDir, cache.sources, - containingDescription: - ResolvedRootDescription.fromDir(destinationDir), + containingDescription: ResolvedRootDescription.fromDir( + destinationDir, + ), ); final buffer = StringBuffer(); if (pubspec.resolution != Resolution.none) { diff --git a/lib/src/source/git.dart b/lib/src/source/git.dart index 09ca2b8b0..3932bd1c4 100644 --- a/lib/src/source/git.dart +++ b/lib/src/source/git.dart @@ -93,7 +93,7 @@ class GitSource extends CachedSource { tagPattern = descriptionTagPattern; default: throw const FormatException( - "The 'tagPattern' field of the description " + "The 'tag_pattern' field of the description " 'must be a string or null.', ); } @@ -101,12 +101,12 @@ class GitSource extends CachedSource { if (ref != null && tagPattern != null) { throw const FormatException( - 'A git description cannot have both a ref and a `tagPattern`.', + 'A git description cannot have both a ref and a `tag_pattern`.', ); } if (languageVersion.forbidsUnknownDescriptionKeys) { for (final key in description.keys) { - if (!['url', 'ref', 'path', 'tagPattern'].contains(key)) { + if (!['url', 'ref', 'path', 'tag_pattern'].contains(key)) { throw FormatException('Unknown key "$key" in description.'); } } diff --git a/lib/src/source/sdk.dart b/lib/src/source/sdk.dart index cf4463d68..8469ef6b9 100644 --- a/lib/src/source/sdk.dart +++ b/lib/src/source/sdk.dart @@ -91,8 +91,9 @@ class SdkSource extends Source { _verifiedPackagePath(ref), cache.sources, expectedName: ref.name, - containingDescription: - ResolvedSdkDescription(ref.description as SdkDescription), + containingDescription: ResolvedSdkDescription( + ref.description as SdkDescription, + ), ); /// Validate that there are no non-sdk dependencies if the SDK does not diff --git a/test/get/git/check_out_and_upgrade_test.dart b/test/get/git/check_out_and_upgrade_test.dart index b6ba6daf9..4b463305e 100644 --- a/test/get/git/check_out_and_upgrade_test.dart +++ b/test/get/git/check_out_and_upgrade_test.dart @@ -65,68 +65,63 @@ void main() { test('checks out and upgrades a package from with a tag-pattern', () async { ensureGit(); - final repo = d.git( - 'foo.git', - [d.libDir('foo'), d.libPubspec('foo', '1.0.0')], - ); + final repo = d.git('foo.git', [ + d.libDir('foo'), + d.libPubspec('foo', '1.0.0'), + ]); await repo.create(); await repo.tag('v1.0.0'); - await d.appDir( - dependencies: { - 'foo': { - 'git': {'url': '../foo.git', 'tag_pattern': 'v{{version}}'}, - 'version': '^1.0.0', - }, - }, - pubspec: { - 'environment': {'sdk': '^3.7.0'}, - }, - ).create(); + await d + .appDir( + dependencies: { + 'foo': { + 'git': {'url': '../foo.git', 'tag_pattern': 'v{{version}}'}, + 'version': '^1.0.0', + }, + }, + pubspec: { + 'environment': {'sdk': '^3.7.0'}, + }, + ) + .create(); await pubGet( output: contains('+ foo 1.0.0'), - environment: { - '_PUB_TEST_SDK_VERSION': '3.7.0', - }, + environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, ); // This should be found by `pub upgrade`. - await d.git( - 'foo.git', - [d.libDir('foo'), d.libPubspec('foo', '1.5.0')], - ).commit(); + await d.git('foo.git', [ + d.libDir('foo'), + d.libPubspec('foo', '1.5.0'), + ]).commit(); await repo.tag('v1.5.0'); // The untagged version should not be found by `pub upgrade`. - await d.git( - 'foo.git', - [d.libDir('foo'), d.libPubspec('foo', '1.7.0')], - ).commit(); + await d.git('foo.git', [ + d.libDir('foo'), + d.libPubspec('foo', '1.7.0'), + ]).commit(); // This should be found by `pub upgrade --major-versions` - await d.git( - 'foo.git', - [d.libDir('foo'), d.libPubspec('foo', '2.0.0')], - ).commit(); + await d.git('foo.git', [ + d.libDir('foo'), + d.libPubspec('foo', '2.0.0'), + ]).commit(); await repo.tag('v2.0.0'); // A version that is not tagged according to the pattern should not be // chosen by the `upgrade --major-versions`. - await d.git( - 'foo.git', - [d.libDir('foo'), d.libPubspec('foo', '3.0.0')], - ).commit(); + await d.git('foo.git', [ + d.libDir('foo'), + d.libPubspec('foo', '3.0.0'), + ]).commit(); await repo.tag('unrelatedTag'); await pubUpgrade( - output: allOf( - contains('> foo 1.5.0'), - contains('Changed 1 dependency!'), - ), - environment: { - '_PUB_TEST_SDK_VERSION': '3.7.0', - }, + output: allOf(contains('> foo 1.5.0'), contains('Changed 1 dependency!')), + environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, ); await pubUpgrade( @@ -136,9 +131,7 @@ void main() { contains('foo: ^1.0.0 -> ^2.0.0'), contains('Changed 1 dependency!'), ), - environment: { - '_PUB_TEST_SDK_VERSION': '3.7.0', - }, + environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, ); }); } diff --git a/test/get/git/tag_pattern_test.dart b/test/get/git/tag_pattern_test.dart index 499d2e0b5..a15fe7048 100644 --- a/test/get/git/tag_pattern_test.dart +++ b/test/get/git/tag_pattern_test.dart @@ -9,8 +9,7 @@ import '../../descriptor.dart' as d; import '../../test_pub.dart'; void main() { - test( - 'Versions inside a tag_pattern dependency can depend on versions from ' + test('Versions inside a tag_pattern dependency can depend on versions from ' 'another commit', () async { ensureGit(); await d.git('foo.git', [ @@ -85,34 +84,30 @@ void main() { ]).commit(); await d.git('bar.git', []).tag('2.0.0'); - await d.appDir( - dependencies: { - 'foo': { - 'git': { - 'url': p.join(d.sandbox, 'foo.git'), - 'tag_pattern': '{{version}}', + await d + .appDir( + dependencies: { + 'foo': { + 'git': { + 'url': p.join(d.sandbox, 'foo.git'), + 'tag_pattern': '{{version}}', + }, + 'version': '^1.0.0', + }, }, - 'version': '^1.0.0', - }, - }, - pubspec: { - 'environment': {'sdk': '^3.7.0'}, - }, - ).create(); + pubspec: { + 'environment': {'sdk': '^3.7.0'}, + }, + ) + .create(); await pubGet( - output: allOf( - contains('+ foo 1.0.0'), - contains('+ bar 2.0.0'), - ), - environment: { - '_PUB_TEST_SDK_VERSION': '3.7.0', - }, + output: allOf(contains('+ foo 1.0.0'), contains('+ bar 2.0.0')), + environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, ); }); - test( - 'Versions inside a tag_pattern dependency cannot depend on ' + test('Versions inside a tag_pattern dependency cannot depend on ' 'version from another commit via path-dependencies', () async { ensureGit(); @@ -162,30 +157,32 @@ void main() { await d.git('repo.git', []).tag('foo-2.0.0'); await d.git('repo.git', []).tag('bar-1.0.0'); - await d.appDir( - dependencies: { - 'foo': { - 'git': { - 'url': '../repo.git', - 'tag_pattern': 'foo-{{version}}', - 'path': 'foo', + await d + .appDir( + dependencies: { + 'foo': { + 'git': { + 'url': '../repo.git', + 'tag_pattern': 'foo-{{version}}', + 'path': 'foo', + }, + 'version': '^1.0.0', + }, }, - 'version': '^1.0.0', - }, - }, - pubspec: { - 'environment': {'sdk': '^3.7.0'}, - }, - ).create(); + pubspec: { + 'environment': {'sdk': '^3.7.0'}, + }, + ) + .create(); await pubGet( - error: matches(r'Because foo from git ../repo.git at HEAD in foo ' - r'depends on bar \^2.0.0 from git ' - r'which depends on foo from git ../repo.git at [a-f0-9]* in foo, ' - r'foo <2.0.0 from git is forbidden'), - environment: { - '_PUB_TEST_SDK_VERSION': '3.7.0', - }, + error: matches( + r'Because foo from git ../repo.git at HEAD in foo ' + r'depends on bar \^2.0.0 from git ' + r'which depends on foo from git ../repo.git at [a-f0-9]* in foo, ' + r'foo <2.0.0 from git is forbidden', + ), + environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, ); }); } diff --git a/test/version_solver_test.dart b/test/version_solver_test.dart index 56b621d7c..dcf5a4241 100644 --- a/test/version_solver_test.dart +++ b/test/version_solver_test.dart @@ -533,12 +533,14 @@ Because myapp depends on foo ^1.0.0 which doesn't match any versions, version so ..serve('foo', '1.0.0', deps: {'shared': '1.0.0'}) ..serve('shared', '1.0.0'); - await d.appDir( - dependencies: { - 'foo': '1.0.0', - 'bar': {'path': '../bar'}, - }, - ).create(); + await d + .appDir( + dependencies: { + 'foo': '1.0.0', + 'bar': {'path': '../bar'}, + }, + ) + .create(); await expectResolves( error: equalsIgnoringWhitespace(''' Because every version of bar from path depends on shared @@ -904,13 +906,7 @@ void backtracking() { ..serve('a', '1.0.0') ..serve('a', '2.0.0') ..serve('b', '1.0.0', deps: {'a': 'any'}) - ..serve( - 'b', - '2.0.0', - deps: { - 'a': '^2.0.0', - }, - ) + ..serve('b', '2.0.0', deps: {'a': '^2.0.0'}) ..serve('c', '1.0.0') ..serve('c', '2.0.0') ..serve('c', '3.0.0') @@ -918,7 +914,8 @@ void backtracking() { ..serve('c', '5.0.0'); await d - .appDir(dependencies: {'a': '1.0.0', 'b': 'any', 'c': 'any'}).create(); + .appDir(dependencies: {'a': '1.0.0', 'b': 'any', 'c': 'any'}) + .create(); await expectResolves(result: {'a': '1.0.0', 'b': '1.0.0', 'c': '5.0.0'}); }); @@ -971,13 +968,15 @@ void backtracking() { ..serve('c', '4.0.0') ..serve('c', '5.0.0'); - await d.appDir( - dependencies: { - 'a': 'any', - 'b': {'path': '../b'}, - 'c': 'any', - }, - ).create(); + await d + .appDir( + dependencies: { + 'a': 'any', + 'b': {'path': '../b'}, + 'c': 'any', + }, + ) + .create(); await expectResolves( error: equalsIgnoringWhitespace(''' Because every version of b from path depends on a from path From 6124471056af8dd35e5eb71776376a7f320c2229 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Fri, 28 Mar 2025 08:12:52 +0000 Subject: [PATCH 07/12] Validate tag_pattern early --- lib/src/source/git.dart | 13 ++++++++++++- test/get/git/tag_pattern_test.dart | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/src/source/git.dart b/lib/src/source/git.dart index 3932bd1c4..af104f390 100644 --- a/lib/src/source/git.dart +++ b/lib/src/source/git.dart @@ -91,6 +91,8 @@ class GitSource extends CachedSource { switch (description['tag_pattern']) { case final String descriptionTagPattern: tagPattern = descriptionTagPattern; + // Do an early compilation to validate the format. + compileTagPattern(tagPattern); default: throw const FormatException( "The 'tag_pattern' field of the description " @@ -773,6 +775,8 @@ class GitSource extends CachedSource { 'tag', '--list', '--format', + // We can use space here, as it is not allowed in a git tag + // https://git-scm.com/docs/git-check-ref-format '%(refname:lstrip=2) %(objectname)', ], workingDir: path); final lines = output.trim().split('\n'); @@ -1079,10 +1083,17 @@ const versionPattern = r'(-([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?' // Pre-release. r'(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?'; // build +/// Takes a [tagPattern] and returns a [RegExp] matching the relevant tags. +/// +/// The tagPattern should contain '{{version}}' which will match a pub_semver +/// version. The rest of the tagPattern is matched verbatim. RegExp compileTagPattern(String tagPattern) { final match = tagPatternPattern.firstMatch(tagPattern); if (match == null) { - throw const FormatException('Tag patterns must contain {{version})'); + throw const FormatException( + 'The `tag_pattern` must contain "{{version}" ' + 'to match different versions', + ); } final before = RegExp.escape(match[1]!); final after = RegExp.escape(match[2]!); diff --git a/test/get/git/tag_pattern_test.dart b/test/get/git/tag_pattern_test.dart index a15fe7048..dd4133327 100644 --- a/test/get/git/tag_pattern_test.dart +++ b/test/get/git/tag_pattern_test.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:path/path.dart' as p; +import 'package:pub/src/exit_codes.dart'; import 'package:test/test.dart'; import '../../descriptor.dart' as d; @@ -185,4 +186,29 @@ void main() { environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, ); }); + + test('tag_pattern must contain "{{version}}"', () async { + await d + .appDir( + dependencies: { + 'foo': { + 'git': {'url': 'some/git/path', 'tag_pattern': 'v100'}, + }, + }, + pubspec: { + 'environment': {'sdk': '^3.7.0'}, + }, + ) + .create(); + + await pubGet( + environment: {'_PUB_TEST_SDK_VERSION': '3.8.0'}, + error: contains( + 'Invalid description in the "myapp" pubspec on the "foo" dependency: ' + 'The `tag_pattern` must contain "{{version}" ' + 'to match different versions', + ), + exitCode: DATA, + ); + }); } From 2c7fb43ae39afae5b1ac50d05e6f13835c3808a3 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Fri, 28 Mar 2025 08:15:59 +0000 Subject: [PATCH 08/12] not coming until 3.9... --- lib/src/language_version.dart | 2 +- test/get/git/check_out_and_upgrade_test.dart | 4 ++-- test/get/git/tag_pattern_test.dart | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/src/language_version.dart b/lib/src/language_version.dart index 24995dbc3..fdfb2bfb5 100644 --- a/lib/src/language_version.dart +++ b/lib/src/language_version.dart @@ -107,7 +107,7 @@ class LanguageVersion implements Comparable { static const firstVersionWithNullSafety = LanguageVersion(2, 12); static const firstVersionWithShorterHostedSyntax = LanguageVersion(2, 15); static const firstVersionWithWorkspaces = LanguageVersion(3, 5); - static const firstVersionWithTagPattern = LanguageVersion(3, 7); + static const firstVersionWithTagPattern = LanguageVersion(3, 9); static const firstVersionForbidingUnknownDescriptionKeys = LanguageVersion( 3, 7, diff --git a/test/get/git/check_out_and_upgrade_test.dart b/test/get/git/check_out_and_upgrade_test.dart index 4b463305e..b709e389c 100644 --- a/test/get/git/check_out_and_upgrade_test.dart +++ b/test/get/git/check_out_and_upgrade_test.dart @@ -81,14 +81,14 @@ void main() { }, }, pubspec: { - 'environment': {'sdk': '^3.7.0'}, + 'environment': {'sdk': '^3.9.0'}, }, ) .create(); await pubGet( output: contains('+ foo 1.0.0'), - environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, + environment: {'_PUB_TEST_SDK_VERSION': '3.9.0'}, ); // This should be found by `pub upgrade`. diff --git a/test/get/git/tag_pattern_test.dart b/test/get/git/tag_pattern_test.dart index dd4133327..73c12b10b 100644 --- a/test/get/git/tag_pattern_test.dart +++ b/test/get/git/tag_pattern_test.dart @@ -17,7 +17,7 @@ void main() { d.libPubspec( 'foo', '1.0.0', - sdk: '^3.7.0', + sdk: '^3.9.0', deps: { 'bar': { 'git': { @@ -35,7 +35,7 @@ void main() { d.libPubspec( 'foo', '2.0.0', - sdk: '^3.7.0', + sdk: '^3.9.0', deps: { 'bar': { 'git': { @@ -53,7 +53,7 @@ void main() { d.libPubspec( 'bar', '1.0.0', - sdk: '^3.7.0', + sdk: '^3.9.0', deps: { 'foo': { 'git': { @@ -71,7 +71,7 @@ void main() { d.libPubspec( 'bar', '2.0.0', - sdk: '^3.7.0', + sdk: '^3.9.0', deps: { 'foo': { 'git': { @@ -97,14 +97,14 @@ void main() { }, }, pubspec: { - 'environment': {'sdk': '^3.7.0'}, + 'environment': {'sdk': '^3.9.0'}, }, ) .create(); await pubGet( output: allOf(contains('+ foo 1.0.0'), contains('+ bar 2.0.0')), - environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, + environment: {'_PUB_TEST_SDK_VERSION': '3.9.0'}, ); }); @@ -171,7 +171,7 @@ void main() { }, }, pubspec: { - 'environment': {'sdk': '^3.7.0'}, + 'environment': {'sdk': '^3.9.0'}, }, ) .create(); @@ -183,7 +183,7 @@ void main() { r'which depends on foo from git ../repo.git at [a-f0-9]* in foo, ' r'foo <2.0.0 from git is forbidden', ), - environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, + environment: {'_PUB_TEST_SDK_VERSION': '3.9.0'}, ); }); @@ -196,13 +196,13 @@ void main() { }, }, pubspec: { - 'environment': {'sdk': '^3.7.0'}, + 'environment': {'sdk': '^3.9.0'}, }, ) .create(); await pubGet( - environment: {'_PUB_TEST_SDK_VERSION': '3.8.0'}, + environment: {'_PUB_TEST_SDK_VERSION': '3.9.0'}, error: contains( 'Invalid description in the "myapp" pubspec on the "foo" dependency: ' 'The `tag_pattern` must contain "{{version}" ' From 6960eb7777d27c957444d92b37396f1eb499e80b Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Fri, 28 Mar 2025 08:58:08 +0000 Subject: [PATCH 09/12] windows compatz2 --- test/get/git/tag_pattern_test.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/get/git/tag_pattern_test.dart b/test/get/git/tag_pattern_test.dart index 73c12b10b..d23a0de87 100644 --- a/test/get/git/tag_pattern_test.dart +++ b/test/get/git/tag_pattern_test.dart @@ -175,12 +175,12 @@ void main() { }, ) .create(); - + final s = p.separator; await pubGet( error: matches( - r'Because foo from git ../repo.git at HEAD in foo ' + r'Because foo from git ..${s}repo.git at HEAD in foo ' r'depends on bar \^2.0.0 from git ' - r'which depends on foo from git ../repo.git at [a-f0-9]* in foo, ' + r'which depends on foo from git ..${s}repo.git at [a-f0-9]* in foo, ' r'foo <2.0.0 from git is forbidden', ), environment: {'_PUB_TEST_SDK_VERSION': '3.9.0'}, From 03ba0efd61679a6c90341d0d7f3a45bc3b208efd Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Tue, 1 Apr 2025 07:59:16 +0000 Subject: [PATCH 10/12] Windows compatz in tests --- test/get/git/tag_pattern_test.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/get/git/tag_pattern_test.dart b/test/get/git/tag_pattern_test.dart index d23a0de87..a887b305e 100644 --- a/test/get/git/tag_pattern_test.dart +++ b/test/get/git/tag_pattern_test.dart @@ -178,10 +178,10 @@ void main() { final s = p.separator; await pubGet( error: matches( - r'Because foo from git ..${s}repo.git at HEAD in foo ' - r'depends on bar \^2.0.0 from git ' - r'which depends on foo from git ..${s}repo.git at [a-f0-9]* in foo, ' - r'foo <2.0.0 from git is forbidden', + 'Because foo from git ..${s}repo.git at HEAD in foo ' + 'depends on bar \\^2.0.0 from git ' + 'which depends on foo from git ..${s}repo.git at [a-f0-9]+ in foo, ' + 'foo <2.0.0 from git is forbidden', ), environment: {'_PUB_TEST_SDK_VERSION': '3.9.0'}, ); From 4e8de79c7ee740a2243ffb6eaca0aeb26395974b Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Tue, 1 Apr 2025 08:15:09 +0000 Subject: [PATCH 11/12] Fix test sdk version --- test/get/git/check_out_and_upgrade_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/get/git/check_out_and_upgrade_test.dart b/test/get/git/check_out_and_upgrade_test.dart index b709e389c..bbe598a65 100644 --- a/test/get/git/check_out_and_upgrade_test.dart +++ b/test/get/git/check_out_and_upgrade_test.dart @@ -121,7 +121,7 @@ void main() { await pubUpgrade( output: allOf(contains('> foo 1.5.0'), contains('Changed 1 dependency!')), - environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, + environment: {'_PUB_TEST_SDK_VERSION': '3.9.0'}, ); await pubUpgrade( @@ -131,7 +131,7 @@ void main() { contains('foo: ^1.0.0 -> ^2.0.0'), contains('Changed 1 dependency!'), ), - environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, + environment: {'_PUB_TEST_SDK_VERSION': '3.9.0'}, ); }); } From b57d89a9963b3e362b7b681bde909f3600bf8baf Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Tue, 1 Apr 2025 08:27:46 +0000 Subject: [PATCH 12/12] Escaping --- test/get/git/tag_pattern_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/get/git/tag_pattern_test.dart b/test/get/git/tag_pattern_test.dart index a887b305e..b7bf8d6eb 100644 --- a/test/get/git/tag_pattern_test.dart +++ b/test/get/git/tag_pattern_test.dart @@ -175,7 +175,7 @@ void main() { }, ) .create(); - final s = p.separator; + final s = RegExp.escape(p.separator); await pubGet( error: matches( 'Because foo from git ..${s}repo.git at HEAD in foo '