From 840510b54807d0192ffd4d11b2eb1e9c19033df2 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Tue, 12 Mar 2024 14:48:15 +0000 Subject: [PATCH] Merge --- lib/src/command/add.dart | 10 +- lib/src/command/dependency_services.dart | 2 + lib/src/command/lish.dart | 3 + lib/src/command/unpack.dart | 2 + lib/src/command/upgrade.dart | 2 + lib/src/global_packages.dart | 3 +- lib/src/package.dart | 2 + lib/src/pubspec.dart | 42 +++--- lib/src/source.dart | 8 +- lib/src/source/cached.dart | 7 +- lib/src/source/git.dart | 18 ++- lib/src/source/hosted.dart | 13 +- lib/src/source/path.dart | 83 ++++++++++-- lib/src/source/root.dart | 2 +- lib/src/source/sdk.dart | 3 +- lib/src/source/unknown.dart | 2 +- test/get/git/check_out_transitive_test.dart | 45 ++++++- test/pubspec_test.dart | 134 +++++++++++++++----- test/version_solver_test.dart | 7 +- 19 files changed, 298 insertions(+), 90 deletions(-) diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart index 07f1c3614a..16cfa97b9d 100644 --- a/lib/src/command/add.dart +++ b/lib/src/command/add.dart @@ -26,6 +26,7 @@ import '../solver.dart'; import '../source/git.dart'; import '../source/hosted.dart'; import '../source/path.dart'; +import '../source/root.dart'; import '../utils.dart'; /// Handles the `add` pub command. Adds a dependency to `pubspec.yaml` and gets @@ -272,6 +273,7 @@ Specify multiple sdk packages with descriptors.'''); location: Uri.parse(entrypoint.pubspecPath), overridesFileContents: overridesFileContents, overridesLocation: Uri.file(overridesPath), + containingDescription: RootDescription(entrypoint.rootDir), ), ) .acquireDependencies( @@ -547,7 +549,11 @@ Specify multiple sdk packages with descriptors.'''); PathDescription(p.absolute(path), p.isRelative(path)), ); } else if (argResults.sdk != null) { - ref = cache.sdk.parseRef(packageName, argResults.sdk); + ref = cache.sdk.parseRef( + packageName, + argResults.sdk, + containingDescription: RootDescription(p.current), + ); } else { ref = PackageRef( packageName, @@ -634,7 +640,7 @@ Specify multiple sdk packages with descriptors.'''); }, cache.sources, // Resolve relative paths relative to current, not where the pubspec.yaml is. - location: p.toUri(p.join(p.current, 'descriptor')), + containingDescription: RootDescription(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 f095722964..5d6646868d 100644 --- a/lib/src/command/dependency_services.dart +++ b/lib/src/command/dependency_services.dart @@ -29,6 +29,7 @@ import '../solver.dart'; import '../solver/version_solver.dart'; import '../source/git.dart'; import '../source/hosted.dart'; +import '../source/root.dart'; import '../system_cache.dart'; import '../utils.dart'; @@ -450,6 +451,7 @@ class DependencyServicesApplyCommand extends PubCommand { updatedPubspec, cache.sources, location: toUri(entrypoint.pubspecPath), + containingDescription: RootDescription(entrypoint.rootDir), ), entrypoint.rootDir, entrypoint.root.workspaceChildren, diff --git a/lib/src/command/lish.dart b/lib/src/command/lish.dart index f6ca1a4065..f716b1b1d3 100644 --- a/lib/src/command/lish.dart +++ b/lib/src/command/lish.dart @@ -8,6 +8,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:http/http.dart' as http; +import 'package:path/path.dart' as p; import '../ascii_tree.dart' as tree; import '../authentication/client.dart'; @@ -22,6 +23,7 @@ import '../oauth2.dart' as oauth2; import '../pubspec.dart'; import '../solver/type.dart'; import '../source/hosted.dart' show validateAndNormalizeHostedUrl; +import '../source/root.dart'; import '../utils.dart'; import '../validator.dart'; @@ -346,6 +348,7 @@ the \$PUB_HOSTED_URL environment variable.''', ), ), cache.sources, + containingDescription: RootDescription(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 64f93b312b..ef7df99345 100644 --- a/lib/src/command/unpack.dart +++ b/lib/src/command/unpack.dart @@ -16,6 +16,7 @@ import '../pubspec.dart'; import '../sdk.dart'; import '../solver/type.dart'; import '../source/hosted.dart'; +import '../source/root.dart'; import '../utils.dart'; class UnpackCommand extends PubCommand { @@ -182,6 +183,7 @@ in a directory `foo-`. cache.sources, // Resolve relative paths relative to current, not where the pubspec.yaml is. location: p.toUri(p.join(p.current, 'descriptor')), + containingDescription: RootDescription('.'), ); } on FormatException catch (e) { usageException('Failed parsing package specification: ${e.message}'); diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart index cddd6c7040..5fb176a843 100644 --- a/lib/src/command/upgrade.dart +++ b/lib/src/command/upgrade.dart @@ -21,6 +21,7 @@ import '../pubspec_utils.dart'; import '../sdk.dart'; import '../solver.dart'; import '../source/hosted.dart'; +import '../source/root.dart'; import '../utils.dart'; /// Handles the `upgrade` pub command. @@ -399,6 +400,7 @@ be direct 'dependencies' or 'dev_dependencies', following packages are not: location: Uri.parse(entrypoint.pubspecPath), overridesFileContents: overridesFileContents, overridesLocation: Uri.file(overridesPath), + containingDescription: RootDescription(entrypoint.rootDir), ); } diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart index 6d59c1c3ca..84a4b39b8d 100644 --- a/lib/src/global_packages.dart +++ b/lib/src/global_packages.dart @@ -27,6 +27,7 @@ import 'source/cached.dart'; import 'source/git.dart'; import 'source/hosted.dart'; import 'source/path.dart'; +import 'source/root.dart'; import 'system_cache.dart'; import 'utils.dart'; @@ -113,7 +114,7 @@ class GlobalPackages { if (path != null) 'path': path, if (ref != null) 'ref': ref, }, - containingDir: '.', + containingDescription: RootDescription(p.current), ); } on FormatException catch (e) { throw ApplicationException(e.message); diff --git a/lib/src/package.dart b/lib/src/package.dart index 286fc903d8..954b0381ee 100644 --- a/lib/src/package.dart +++ b/lib/src/package.dart @@ -14,6 +14,7 @@ import 'io.dart'; import 'log.dart' as log; import 'package_name.dart'; import 'pubspec.dart'; +import 'source/root.dart'; import 'system_cache.dart'; import 'utils.dart'; @@ -134,6 +135,7 @@ class Package { sources, expectedName: name, allowOverridesFile: withPubspecOverrides, + containingDescription: RootDescription(dir), ); final workspacePackages = pubspec.workspace .map((e) => Package.load(null, p.join(dir, e), sources)) diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart index 2bf9250d85..2811545584 100644 --- a/lib/src/pubspec.dart +++ b/lib/src/pubspec.dart @@ -14,6 +14,8 @@ import 'language_version.dart'; import 'package_name.dart'; import 'pubspec_parse.dart'; import 'sdk.dart'; +import 'source.dart'; +import 'source/root.dart'; import 'system_cache.dart'; export 'pubspec_parse.dart' hide PubspecBase; @@ -58,11 +60,9 @@ class Pubspec extends PubspecBase { /// This will be null if this was created using [Pubspec] or [Pubspec.empty]. final SourceRegistry _sources; - /// The location from which the pubspec was loaded. - /// - /// This can be null if the pubspec was created in-memory or if its location - /// is unknown. - Uri? get _location => fields.span.sourceUrl; + /// It is used to resolve relative paths. And to resolve path-descriptions + /// from a git dependency as git-descriptions. + final Description _containingDescription; /// Directories of packages that should resolve together with this package. late List workspace = () { @@ -118,7 +118,7 @@ class Pubspec extends PubspecBase { _sources, languageVersion, _packageName, - _location, + _containingDescription, ); Map? _dependencies; @@ -131,7 +131,7 @@ class Pubspec extends PubspecBase { _sources, languageVersion, _packageName, - _location, + _containingDescription, ); Map? _devDependencies; @@ -163,7 +163,7 @@ class Pubspec extends PubspecBase { _sources, languageVersion, _packageName, - _location, + _containingDescription, fileType: _FileType.pubspecOverrides, ); } @@ -174,7 +174,7 @@ class Pubspec extends PubspecBase { _sources, languageVersion, _packageName, - _location, + _containingDescription, ); } @@ -263,6 +263,7 @@ class Pubspec extends PubspecBase { SourceRegistry sources, { String? expectedName, bool allowOverridesFile = false, + required Description containingDescription, }) { var pubspecPath = path.join(packageDir, pubspecYamlFilename); var overridesPath = path.join(packageDir, pubspecOverridesFilename); @@ -287,6 +288,7 @@ class Pubspec extends PubspecBase { location: path.toUri(pubspecPath), overridesFileContents: overridesFileContents, overridesLocation: path.toUri(overridesPath), + containingDescription: containingDescription, ); } @@ -316,6 +318,9 @@ class Pubspec extends PubspecBase { _sources = sources ?? ((String? name) => throw StateError('No source registry given')), _overridesFileFields = null, + // This is a dummy value. + // Dependencies should already be resolved, so we never need to do relative resolutions. + _containingDescription = RootDescription('.'), super( fields == null ? YamlMap() : YamlMap.wrap(fields), name: name, @@ -335,11 +340,13 @@ class Pubspec extends PubspecBase { YamlMap? overridesFields, String? expectedName, Uri? location, + required Description containingDescription, }) : _overridesFileFields = overridesFields, _includeDefaultSdkConstraint = true, _givenSdkConstraints = null, dependencyOverridesFromOverridesFile = overridesFields != null && overridesFields.containsKey('dependency_overrides'), + _containingDescription = containingDescription, super( fields is YamlMap ? fields @@ -368,6 +375,7 @@ class Pubspec extends PubspecBase { Uri? location, String? overridesFileContents, Uri? overridesLocation, + required Description containingDescription, }) { late final YamlMap pubspecMap; YamlMap? overridesFileMap; @@ -388,6 +396,7 @@ class Pubspec extends PubspecBase { overridesFields: overridesFileMap, expectedName: expectedName, location: location, + containingDescription: containingDescription, ); } @@ -484,7 +493,7 @@ Map _parseDependencies( SourceRegistry sources, LanguageVersion languageVersion, String? packageName, - Uri? location, { + Description containingDescription, { _FileType fileType = _FileType.pubspec, }) { var dependencies = {}; @@ -568,15 +577,10 @@ Map _parseDependencies( 'description', descriptionNode?.span, () { - String? pubspecDir; - if (location != null && _isFileUri(location)) { - pubspecDir = path.dirname(path.fromUri(location)); - } - return sources(sourceName).parseRef( name, descriptionNode?.value, - containingDir: pubspecDir, + containingDescription: containingDescription, languageVersion: languageVersion, ); }, @@ -592,12 +596,6 @@ Map _parseDependencies( return dependencies; } -/// Returns whether [uri] is a file URI. -/// -/// This is slightly more complicated than just checking if the scheme is -/// 'file', since relative URIs also refer to the filesystem on the VM. -bool _isFileUri(Uri uri) => uri.scheme == 'file' || uri.scheme == ''; - /// Parses [node] to a [VersionConstraint]. /// /// If or [defaultUpperBoundConstraint] is specified then it will be set as the diff --git a/lib/src/source.dart b/lib/src/source.dart index 18ea453f09..67891bac84 100644 --- a/lib/src/source.dart +++ b/lib/src/source.dart @@ -65,10 +65,8 @@ abstract class Source { /// hosted dependencies like `foo:` or `foo: ^1.2.3`, the [description] may /// also be `null`. /// - /// [containingDir] is the path to the directory of the pubspec where this - /// description appears. It may be `null` if the description is coming from - /// some in-memory source (such as pulling down a pubspec from - /// pub.dev). + /// [containingDescription] describes the location of the pubspec where this + /// description appears. /// /// [languageVersion] is the minimum Dart version parsed from the pubspec's /// `environment` field. Source implementations may use this parameter to only @@ -81,7 +79,7 @@ abstract class Source { PackageRef parseRef( String name, Object? description, { - String? containingDir, + required Description containingDescription, required LanguageVersion languageVersion, }); diff --git a/lib/src/source/cached.dart b/lib/src/source/cached.dart index 933b08c9ad..a3d84b432a 100644 --- a/lib/src/source/cached.dart +++ b/lib/src/source/cached.dart @@ -29,7 +29,12 @@ abstract class CachedSource extends Source { Future doDescribe(PackageId id, SystemCache cache) async { var packageDir = getDirectoryInCache(id, cache); if (fileExists(path.join(packageDir, 'pubspec.yaml'))) { - return Pubspec.load(packageDir, cache.sources, expectedName: id.name); + return Pubspec.load( + packageDir, + cache.sources, + expectedName: id.name, + containingDescription: id.description.description, + ); } return await describeUncached(id, cache); diff --git a/lib/src/source/git.dart b/lib/src/source/git.dart index 8c69329776..99bf59bff4 100644 --- a/lib/src/source/git.dart +++ b/lib/src/source/git.dart @@ -21,6 +21,8 @@ import '../source.dart'; import '../system_cache.dart'; import '../utils.dart'; import 'cached.dart'; +import 'path.dart'; +import 'root.dart'; /// A package source that gets packages from Git repos. class GitSource extends CachedSource { @@ -35,7 +37,7 @@ class GitSource extends CachedSource { PackageRef parseRef( String name, Object? description, { - String? containingDir, + Description? containingDescription, LanguageVersion? languageVersion, }) { String url; @@ -70,6 +72,12 @@ class GitSource extends CachedSource { path = descriptionPath; } + final containingDir = switch (containingDescription) { + RootDescription(path: final path) => path, + PathDescription(path: final path) => path, + _ => null, + }; + return PackageRef( name, GitDescription( @@ -234,6 +242,7 @@ class GitSource extends CachedSource { return Pubspec.parse( await _showFileAtRevision(resolvedDescription, 'pubspec.yaml', cache), cache.sources, + containingDescription: description, ).name; }); } @@ -338,6 +347,7 @@ class GitSource extends CachedSource { ), cache.sources, expectedName: ref.name, + containingDescription: ref.description, ); } @@ -755,7 +765,7 @@ class GitDescription extends Description { /// Represented as a relative url. final String path; - GitDescription._({ + GitDescription.raw({ required this.url, required this.relative, required String? ref, @@ -770,7 +780,7 @@ class GitDescription extends Description { required String? containingDir, }) { final validatedUrl = GitSource._validatedUrl(url, containingDir); - return GitDescription._( + return GitDescription.raw( url: validatedUrl.url, relative: validatedUrl.wasRelative, ref: ref, @@ -816,7 +826,7 @@ class GitDescription extends Description { other.path == path; } - GitDescription withRef(String newRef) => GitDescription._( + GitDescription withRef(String newRef) => GitDescription.raw( url: url, relative: relative, ref: newRef, diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart index 608d43b91c..0439faf562 100644 --- a/lib/src/source/hosted.dart +++ b/lib/src/source/hosted.dart @@ -31,6 +31,7 @@ import '../source.dart'; import '../system_cache.dart'; import '../utils.dart'; import 'cached.dart'; +import 'root.dart'; const contentHashesDocumentationUrl = 'https://dart.dev/go/content-hashes'; @@ -225,7 +226,7 @@ class HostedSource extends CachedSource { PackageRef parseRef( String name, Object? description, { - String? containingDir, + required Description containingDescription, required LanguageVersion languageVersion, }) { return PackageRef( @@ -400,6 +401,7 @@ class HostedSource extends CachedSource { cache.sources, expectedName: ref.name, location: location, + containingDescription: description, ); final archiveSha256 = map['archive_sha256']; if (archiveSha256 != null && archiveSha256 is! String) { @@ -1584,7 +1586,14 @@ See $contentHashesDocumentationUrl. } final Pubspec pubspec; try { - pubspec = Pubspec.load(tempDir, cache.sources); + pubspec = Pubspec.load( + tempDir, + cache.sources, + containingDescription: + // Dummy description. + // As we never use the dependencies, they don't need to be resolved. + RootDescription('.'), + ); final errors = pubspec.dependencyErrors; if (errors.isNotEmpty) { throw errors.first; diff --git a/lib/src/source/path.dart b/lib/src/source/path.dart index f881b8e9e3..e0451ef8f1 100644 --- a/lib/src/source/path.dart +++ b/lib/src/source/path.dart @@ -14,6 +14,9 @@ import '../package_name.dart'; import '../pubspec.dart'; import '../source.dart'; import '../system_cache.dart'; +import 'git.dart'; +import 'hosted.dart'; +import 'root.dart'; /// A package [Source] that gets packages from a given local file path. class PathSource extends Source { @@ -60,7 +63,7 @@ class PathSource extends Source { PackageRef parseRef( String name, Object? description, { - String? containingDir, + required Description containingDescription, LanguageVersion? languageVersion, }) { if (description is! String) { @@ -69,21 +72,70 @@ class PathSource extends Source { var dir = description; // Resolve the path relative to the containing file path, and remember // whether the original path was relative or absolute. - var isRelative = p.isRelative(description); - if (isRelative) { - // Relative paths coming from pubspecs that are not on the local file - // system aren't allowed. This can happen if a hosted or git dependency - // has a path dependency. - if (containingDir == null) { + var isRelative = p.isRelative(dir); + + if (containingDescription is PathDescription) { + return PackageRef( + name, + PathDescription( + isRelative + ? p.join(p.absolute(containingDescription.path), dir) + : dir, + isRelative, + ), + ); + } else if (containingDescription is RootDescription) { + return PackageRef( + name, + PathDescription( + p.normalize( + p.join( + p.absolute(containingDescription.path), + description, + ), + ), + isRelative, + ), + ); + } else if (containingDescription is GitDescription) { + if (!isRelative) { + throw FormatException( + '"$description" is an absolute path, it can\'t be referenced from a git pubspec.', + ); + } + final resolvedPath = p.url.joinAll([ + containingDescription.path, + ...p.posix.split(dir), + ]); + if (!p.isWithin('.', resolvedPath)) { + throw FormatException( + 'the path "$description" cannot refer outside the git repository $resolvedPath.', + ); + } + return PackageRef( + name, + GitDescription.raw( + url: containingDescription.url, + relative: containingDescription.relative, + ref: containingDescription.ref, + path: p.normalize( + p.join( + containingDescription.path, + dir, + ), + ), + ), + ); + } else if (containingDescription is HostedDescription) { + if (isRelative) { throw FormatException('"$description" is a relative path, but this ' 'isn\'t a local pubspec.'); } - - dir = p.normalize( - p.absolute(p.join(containingDir, description)), - ); + return PackageRef(name, PathDescription(dir, false)); + } else { + throw FormatException('"$description" is a path, but this ' + 'isn\'t a local pubspec.'); } - return PackageRef(name, PathDescription(dir, isRelative)); } @override @@ -168,7 +220,12 @@ class PathSource extends Source { throw ArgumentError('Wrong source'); } var dir = _validatePath(ref.name, description); - return Pubspec.load(dir, cache.sources, expectedName: ref.name); + return Pubspec.load( + dir, + cache.sources, + containingDescription: description, + expectedName: ref.name, + ); } @override diff --git a/lib/src/source/root.dart b/lib/src/source/root.dart index 0936e11c8a..2513b4d0b2 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, { - String? containingDir, + required Description containingDescription, required LanguageVersion languageVersion, }) { throw UnsupportedError('Trying to parse a root package description.'); diff --git a/lib/src/source/sdk.dart b/lib/src/source/sdk.dart index d37c13ef98..878d6d5b27 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, { - String? containingDir, + required Description containingDescription, LanguageVersion? languageVersion, }) { if (description is! String) { @@ -93,6 +93,7 @@ class SdkSource extends Source { _verifiedPackagePath(ref), cache.sources, expectedName: ref.name, + containingDescription: ref.description, ); /// Returns the path for the given [ref]. diff --git a/lib/src/source/unknown.dart b/lib/src/source/unknown.dart index 83f2164d84..04026622f2 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, { - String? containingDir, + required Description containingDescription, LanguageVersion? languageVersion, }) => PackageRef(name, UnknownDescription(description, this)); diff --git a/test/get/git/check_out_transitive_test.dart b/test/get/git/check_out_transitive_test.dart index 76833c6af2..4fed9b6a19 100644 --- a/test/get/git/check_out_transitive_test.dart +++ b/test/get/git/check_out_transitive_test.dart @@ -4,6 +4,7 @@ import 'package:path/path.dart' as p; import 'package:pub/src/exit_codes.dart' as exit_codes; +import 'package:pub/src/exit_codes.dart'; import 'package:test/test.dart'; import '../../descriptor.dart' as d; @@ -95,12 +96,44 @@ void main() { ); }); - test('cannot have relative path dependencies transitively from Git', + test('can have relative path dependencies transitively from Git', () async { + ensureGit(); + + await d.git('foo.git', [ + d.dir('foo', [ + d.libPubspec( + 'foo', + '1.0.0', + deps: { + 'bar': {'path': '../bar'}, + }, + ), + ]), + d.dir('bar', [d.libPubspec('bar', '1.0.0')]), + ]).create(); + + await d.appDir( + dependencies: { + 'foo': { + 'git': { + 'url': p + .toUri(p.absolute(d.sandbox, appPath, '../foo.git')) + .toString(), + 'path': 'foo/', + }, + }, + }, + ).create(); + + await pubGet(); + }); + + test( + 'cannot have relative path dependencies transitively from Git to outside the repo', () async { ensureGit(); await d.git('foo.git', [ - d.libDir('foo'), d.libPubspec( 'foo', '1.0.0', @@ -110,8 +143,7 @@ void main() { ), ]).create(); - await d - .dir('bar', [d.libDir('bar'), d.libPubspec('bar', '1.0.0')]).create(); + await d.dir('bar', [d.libPubspec('bar', '1.0.0')]).create(); await d.appDir( dependencies: { @@ -123,10 +155,11 @@ void main() { ).create(); await pubGet( + exitCode: DATA, error: contains( - '"../bar" is a relative path, but this isn\'t a local pubspec.', + 'Invalid description in the "foo" pubspec on the "bar" dependency: ' + 'the path "../bar" cannot refer outside the git repository', ), - exitCode: exit_codes.DATA, ); }); } diff --git a/test/pubspec_test.dart b/test/pubspec_test.dart index a5456277e7..e714f2e6f9 100644 --- a/test/pubspec_test.dart +++ b/test/pubspec_test.dart @@ -6,10 +6,12 @@ import 'dart:io'; import 'package:pub/src/exceptions.dart'; import 'package:pub/src/pubspec.dart'; +import 'package:pub/src/source.dart'; import 'package:pub/src/source/hosted.dart'; +import 'package:pub/src/source/root.dart'; import 'package:pub/src/system_cache.dart'; import 'package:pub_semver/pub_semver.dart'; -import 'package:test/test.dart'; +import 'package:test/test.dart' hide Description; void main() { group('parse()', () { @@ -20,9 +22,10 @@ void main() { void expectPubspecException( String contents, - void Function(Pubspec) fn, [ + void Function(Pubspec) fn, { String? expectedContains, - ]) { + Description? containingDescription, + }) { var expectation = const TypeMatcher(); if (expectedContains != null) { expectation = expectation.having( @@ -32,20 +35,33 @@ void main() { ); } - var pubspec = Pubspec.parse(contents, sources); + var pubspec = Pubspec.parse( + contents, + sources, + containingDescription: containingDescription ?? RootDescription('.'), + ); expect(() => fn(pubspec), throwsA(expectation)); } test("doesn't eagerly throw an error for an invalid field", () { // Shouldn't throw an error. - Pubspec.parse('version: not a semver', sources); + Pubspec.parse( + 'version: not a semver', + sources, + containingDescription: RootDescription('.'), + ); }); test( "eagerly throws an error if the pubspec name doesn't match the " 'expected name', () { expect( - () => Pubspec.parse('name: foo', sources, expectedName: 'bar'), + () => Pubspec.parse( + 'name: foo', + sources, + expectedName: 'bar', + containingDescription: RootDescription('.'), + ), throwsPubspecException, ); }); @@ -54,7 +70,12 @@ void main() { "eagerly throws an error if the pubspec doesn't have a name and an " 'expected name is passed', () { expect( - () => Pubspec.parse('{}', sources, expectedName: 'bar'), + () => Pubspec.parse( + '{}', + sources, + expectedName: 'bar', + containingDescription: RootDescription('.'), + ), throwsPubspecException, ); }); @@ -70,6 +91,7 @@ dependencies: version: ">=1.2.3 <3.4.5" ''', sources, + containingDescription: RootDescription('.'), ); var foo = pubspec.dependencies['foo']!; @@ -90,6 +112,7 @@ dependencies: version: ">=1.2.3 <0.0.0" ''', sources, + containingDescription: RootDescription('.'), ); var foo = pubspec.dependencies['foo']!; @@ -103,6 +126,7 @@ dependencies: dependencies: ''', sources, + containingDescription: RootDescription('.'), ); expect(pubspec.dependencies, isEmpty); @@ -119,6 +143,7 @@ dev_dependencies: version: ">=1.2.3 <3.4.5" ''', sources, + containingDescription: RootDescription('.'), ); var foo = pubspec.devDependencies['foo']!; @@ -134,6 +159,7 @@ dev_dependencies: dev_dependencies: ''', sources, + containingDescription: RootDescription('.'), ); expect(pubspec.devDependencies, isEmpty); @@ -150,6 +176,7 @@ dependency_overrides: version: ">=1.2.3 <3.4.5" ''', sources, + containingDescription: RootDescription('.'), ); var foo = pubspec.dependencyOverrides['foo']!; @@ -165,6 +192,7 @@ dependency_overrides: dependency_overrides: ''', sources, + containingDescription: RootDescription('.'), ); expect(pubspec.dependencyOverrides, isEmpty); @@ -178,6 +206,7 @@ dependencies: unknown: blah ''', sources, + containingDescription: RootDescription('.'), ); var foo = pubspec.dependencies['foo']!; @@ -193,6 +222,7 @@ dependencies: version: 1.2.3 ''', sources, + containingDescription: RootDescription('.'), ); var foo = pubspec.dependencies['foo']!; @@ -285,24 +315,28 @@ dependencies: expectPubspecException( 'version: [2, 0, 0]', (pubspec) => pubspec.version, - '"version" field must be a string', + expectedContains: '"version" field must be a string', ); }); test('throws if version is malformed (looking like a double)', () { expectPubspecException( - 'version: 2.1', - (pubspec) => pubspec.version, - '"version" field must have three numeric components: major, minor, ' - 'and patch. Instead of "2.1", consider "2.1.0"'); + 'version: 2.1', + (pubspec) => pubspec.version, + expectedContains: + '"version" field must have three numeric components: major, minor, ' + 'and patch. Instead of "2.1", consider "2.1.0"', + ); }); test('throws if version is malformed (looking like an int)', () { expectPubspecException( - 'version: 2', - (pubspec) => pubspec.version, - '"version" field must have three numeric components: major, minor, ' - 'and patch. Instead of "2", consider "2.0.0"'); + 'version: 2', + (pubspec) => pubspec.version, + expectedContains: + '"version" field must have three numeric components: major, minor, ' + 'and patch. Instead of "2", consider "2.0.0"', + ); }); test('throws if version is not a version', () { @@ -321,6 +355,7 @@ environment: workspace: ['a', 'b', 'c'] ''', sources, + containingDescription: RootDescription('.'), ).workspace, ['a', 'b', 'c'], ); @@ -335,6 +370,7 @@ environment: resolution: workspace ''', sources, + containingDescription: RootDescription('.'), ).resolution, Resolution.workspace, ); @@ -359,6 +395,7 @@ environment: resolution: workspace ''', sources, + containingDescription: RootDescription('.'), ).name, 'foo', ); @@ -416,6 +453,7 @@ resolution: "sometimes"''', # See https://dart.dev/tools/pub/cmd for details ''', sources, + containingDescription: RootDescription('.'), ); expect(pubspec.version, equals(Version.none)); expect(pubspec.dependencies, isEmpty); @@ -423,15 +461,18 @@ resolution: "sometimes"''', test('throws a useful error for unresolvable path dependencies', () { expectPubspecException( - ''' + ''' name: pkg dependencies: from_path: {path: non_local_path} ''', - (pubspec) => pubspec.dependencies, - '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.'); + containingDescription: HostedDescription('foo', 'https://pub.dev'), + (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.', + ); }); group('source dependencies', () { @@ -446,6 +487,7 @@ dependencies: name: bar ''', sources, + containingDescription: RootDescription('.'), ); var foo = pubspec.dependencies['foo']!; @@ -474,6 +516,7 @@ dependencies: url: https://example.org/pub/ ''', sources, + containingDescription: RootDescription('.'), ); var foo = pubspec.dependencies['foo']!; @@ -501,6 +544,7 @@ dependencies: hosted: https://example.org/pub/ ''', sources, + containingDescription: RootDescription('.'), ); var foo = pubspec.dependencies['foo']!; @@ -528,6 +572,7 @@ dependencies: hosted: bar ''', sources, + containingDescription: RootDescription('.'), ); var foo = pubspec.dependencies['foo']!; @@ -557,6 +602,7 @@ dependencies: hosted: https://example.org/pub/ ''', sources, + containingDescription: RootDescription('.'), ); expect( @@ -577,6 +623,7 @@ dependencies: foo: ''', sources, + containingDescription: RootDescription('.'), ); var foo = pubspec.dependencies['foo']!; @@ -596,16 +643,18 @@ dependencies: group('throws without a min SDK constraint', () { test('and without a name', () { expectPubspecException( - ''' + ''' name: pkg dependencies: foo: hosted: url: https://example.org/pub/ ''', - (pubspec) => pubspec.dependencies, - "The 'name' key must have a string value without a minimum Dart " - 'SDK constraint of 2.15.'); + (pubspec) => pubspec.dependencies, + expectedContains: + "The 'name' key must have a string value without a minimum Dart " + 'SDK constraint of 2.15.', + ); }); test( @@ -619,7 +668,8 @@ dependencies: hosted: http://pub.example.org ''', (pubspec) => pubspec.dependencies, - 'Using `hosted: ` is only supported with a minimum SDK constraint of 2.15.', + expectedContains: + 'Using `hosted: ` is only supported with a minimum SDK constraint of 2.15.', ); }, ); @@ -680,7 +730,11 @@ dependencies: group('environment', () { test('allows an omitted environment', () { - var pubspec = Pubspec.parse('name: testing', sources); + var pubspec = Pubspec.parse( + 'name: testing', + sources, + containingDescription: RootDescription('.'), + ); expect( pubspec.dartSdkConstraint.effectiveConstraint, VersionConstraint.parse('<2.0.0'), @@ -691,7 +745,11 @@ dependencies: }); test('default SDK constraint can be omitted with empty environment', () { - var pubspec = Pubspec.parse('', sources); + var pubspec = Pubspec.parse( + '', + sources, + containingDescription: RootDescription('.'), + ); expect( pubspec.dartSdkConstraint.effectiveConstraint, VersionConstraint.parse('<2.0.0'), @@ -708,6 +766,7 @@ dependencies: sdk: ">1.0.0" ''', sources, + containingDescription: RootDescription('.'), ); expect( pubspec.dartSdkConstraint.effectiveConstraint, @@ -726,6 +785,7 @@ dependencies: sdk: ">3.0.0" ''', sources, + containingDescription: RootDescription('.'), ); expect( pubspec.sdkConstraints, @@ -754,6 +814,7 @@ environment: fuchsia: ^5.6.7 ''', sources, + containingDescription: RootDescription('.'), ); expect( pubspec.sdkConstraints, @@ -814,7 +875,11 @@ environment: group('publishTo', () { test('defaults to null if omitted', () { - var pubspec = Pubspec.parse('', sources); + var pubspec = Pubspec.parse( + '', + sources, + containingDescription: RootDescription('.'), + ); expect(pubspec.publishTo, isNull); }); @@ -831,6 +896,7 @@ environment: publish_to: http://example.com ''', sources, + containingDescription: RootDescription('.'), ); expect(pubspec.publishTo, equals('http://example.com')); }); @@ -841,6 +907,7 @@ publish_to: http://example.com publish_to: none ''', sources, + containingDescription: RootDescription('.'), ); expect(pubspec.publishTo, equals('none')); }); @@ -862,7 +929,11 @@ publish_to: none group('executables', () { test('defaults to an empty map if omitted', () { - var pubspec = Pubspec.parse('', sources); + var pubspec = Pubspec.parse( + '', + sources, + containingDescription: RootDescription('.'), + ); expect(pubspec.executables, isEmpty); }); @@ -873,6 +944,7 @@ executables: abcDEF-123_: "abc DEF-123._" ''', sources, + containingDescription: RootDescription('.'), ); expect(pubspec.executables['abcDEF-123_'], equals('abc DEF-123._')); }); @@ -926,6 +998,7 @@ executables: command: ''', sources, + containingDescription: RootDescription('.'), ); expect(pubspec.executables['command'], equals('command')); }); @@ -944,6 +1017,7 @@ dependency_overrides: sources, overridesFileContents: overridesContents, overridesLocation: Uri.parse('file:///pubspec_overrides.yaml'), + containingDescription: RootDescription('.'), ); } diff --git a/test/version_solver_test.dart b/test/version_solver_test.dart index baddd53566..bf3a4e0900 100644 --- a/test/version_solver_test.dart +++ b/test/version_solver_test.dart @@ -9,6 +9,7 @@ import 'package:path/path.dart' as p; import 'package:pub/src/lock_file.dart'; import 'package:pub/src/pubspec.dart'; import 'package:pub/src/source/hosted.dart'; +import 'package:pub/src/source/root.dart'; import 'package:pub/src/system_cache.dart'; import 'package:test/test.dart'; @@ -1981,7 +1982,11 @@ Future expectResolves({ var registry = cache.sources; var lockFile = LockFile.load(p.join(d.sandbox, appPath, 'pubspec.lock'), registry); - var resultPubspec = Pubspec.fromMap({'dependencies': result}, registry); + var resultPubspec = Pubspec.fromMap( + {'dependencies': result}, + registry, + containingDescription: RootDescription('.'), + ); var ids = {...lockFile.packages}; for (var dep in resultPubspec.dependencies.values) {