Allow relative path-dependencies from git dependencies
diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart
index 07f1c36..16cfa97 100644
--- a/lib/src/command/add.dart
+++ b/lib/src/command/add.dart
@@ -26,6 +26,7 @@
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 @@
location: Uri.parse(entrypoint.pubspecPath),
overridesFileContents: overridesFileContents,
overridesLocation: Uri.file(overridesPath),
+ containingDescription: RootDescription(entrypoint.rootDir),
),
)
.acquireDependencies(
@@ -547,7 +549,11 @@
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 @@
},
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 f095722..5d66468 100644
--- a/lib/src/command/dependency_services.dart
+++ b/lib/src/command/dependency_services.dart
@@ -29,6 +29,7 @@
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 @@
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 f6ca1a4..f716b1b 100644
--- a/lib/src/command/lish.dart
+++ b/lib/src/command/lish.dart
@@ -8,6 +8,7 @@
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 '../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 @@
),
),
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 64f93b3..ef7df99 100644
--- a/lib/src/command/unpack.dart
+++ b/lib/src/command/unpack.dart
@@ -16,6 +16,7 @@
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 @@
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 cddd6c7..5fb176a 100644
--- a/lib/src/command/upgrade.dart
+++ b/lib/src/command/upgrade.dart
@@ -21,6 +21,7 @@
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 @@
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 cdd281c..fe82886 100644
--- a/lib/src/global_packages.dart
+++ b/lib/src/global_packages.dart
@@ -27,6 +27,7 @@
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 @@
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 286fc90..954b038 100644
--- a/lib/src/package.dart
+++ b/lib/src/package.dart
@@ -14,6 +14,7 @@
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 @@
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 2bf9250..2811545 100644
--- a/lib/src/pubspec.dart
+++ b/lib/src/pubspec.dart
@@ -14,6 +14,8 @@
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 @@
/// 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<String> workspace = () {
@@ -118,7 +118,7 @@
_sources,
languageVersion,
_packageName,
- _location,
+ _containingDescription,
);
Map<String, PackageRange>? _dependencies;
@@ -131,7 +131,7 @@
_sources,
languageVersion,
_packageName,
- _location,
+ _containingDescription,
);
Map<String, PackageRange>? _devDependencies;
@@ -163,7 +163,7 @@
_sources,
languageVersion,
_packageName,
- _location,
+ _containingDescription,
fileType: _FileType.pubspecOverrides,
);
}
@@ -174,7 +174,7 @@
_sources,
languageVersion,
_packageName,
- _location,
+ _containingDescription,
);
}
@@ -263,6 +263,7 @@
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 @@
location: path.toUri(pubspecPath),
overridesFileContents: overridesFileContents,
overridesLocation: path.toUri(overridesPath),
+ containingDescription: containingDescription,
);
}
@@ -316,6 +318,9 @@
_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 @@
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 @@
Uri? location,
String? overridesFileContents,
Uri? overridesLocation,
+ required Description containingDescription,
}) {
late final YamlMap pubspecMap;
YamlMap? overridesFileMap;
@@ -388,6 +396,7 @@
overridesFields: overridesFileMap,
expectedName: expectedName,
location: location,
+ containingDescription: containingDescription,
);
}
@@ -484,7 +493,7 @@
SourceRegistry sources,
LanguageVersion languageVersion,
String? packageName,
- Uri? location, {
+ Description containingDescription, {
_FileType fileType = _FileType.pubspec,
}) {
var dependencies = <String, PackageRange>{};
@@ -568,15 +577,10 @@
'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 @@
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 18ea453..67891ba 100644
--- a/lib/src/source.dart
+++ b/lib/src/source.dart
@@ -65,10 +65,8 @@
/// 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 @@
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 933b08c..a3d84b4 100644
--- a/lib/src/source/cached.dart
+++ b/lib/src/source/cached.dart
@@ -29,7 +29,12 @@
Future<Pubspec> 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 8c69329..99bf59b 100644
--- a/lib/src/source/git.dart
+++ b/lib/src/source/git.dart
@@ -21,6 +21,8 @@
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 @@
PackageRef parseRef(
String name,
Object? description, {
- String? containingDir,
+ Description? containingDescription,
LanguageVersion? languageVersion,
}) {
String url;
@@ -70,6 +72,12 @@
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 @@
return Pubspec.parse(
await _showFileAtRevision(resolvedDescription, 'pubspec.yaml', cache),
cache.sources,
+ containingDescription: description,
).name;
});
}
@@ -338,6 +347,7 @@
),
cache.sources,
expectedName: ref.name,
+ containingDescription: ref.description,
);
}
@@ -755,7 +765,7 @@
/// Represented as a relative url.
final String path;
- GitDescription._({
+ GitDescription.raw({
required this.url,
required this.relative,
required String? ref,
@@ -770,7 +780,7 @@
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 @@
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 608d43b..0439faf 100644
--- a/lib/src/source/hosted.dart
+++ b/lib/src/source/hosted.dart
@@ -31,6 +31,7 @@
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 @@
PackageRef parseRef(
String name,
Object? description, {
- String? containingDir,
+ required Description containingDescription,
required LanguageVersion languageVersion,
}) {
return PackageRef(
@@ -400,6 +401,7 @@
cache.sources,
expectedName: ref.name,
location: location,
+ containingDescription: description,
);
final archiveSha256 = map['archive_sha256'];
if (archiveSha256 != null && archiveSha256 is! String) {
@@ -1584,7 +1586,14 @@
}
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 f881b8e..e0451ef 100644
--- a/lib/src/source/path.dart
+++ b/lib/src/source/path.dart
@@ -14,6 +14,9 @@
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 @@
PackageRef parseRef(
String name,
Object? description, {
- String? containingDir,
+ required Description containingDescription,
LanguageVersion? languageVersion,
}) {
if (description is! String) {
@@ -69,21 +72,70 @@
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 @@
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 0936e11..2513b4d 100644
--- a/lib/src/source/root.dart
+++ b/lib/src/source/root.dart
@@ -69,7 +69,7 @@
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 d37c13e..878d6d5 100644
--- a/lib/src/source/sdk.dart
+++ b/lib/src/source/sdk.dart
@@ -29,7 +29,7 @@
PackageRef parseRef(
String name,
Object? description, {
- String? containingDir,
+ required Description containingDescription,
LanguageVersion? languageVersion,
}) {
if (description is! String) {
@@ -93,6 +93,7 @@
_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 83f2164..0402662 100644
--- a/lib/src/source/unknown.dart
+++ b/lib/src/source/unknown.dart
@@ -37,7 +37,7 @@
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 76833c6..4fed9b6 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 @@
);
});
- 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 @@
),
]).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 @@
).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 a545627..e714f2e 100644
--- a/test/pubspec_test.dart
+++ b/test/pubspec_test.dart
@@ -6,10 +6,12 @@
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 expectPubspecException(
String contents,
- void Function(Pubspec) fn, [
+ void Function(Pubspec) fn, {
String? expectedContains,
- ]) {
+ Description? containingDescription,
+ }) {
var expectation = const TypeMatcher<SourceSpanApplicationException>();
if (expectedContains != null) {
expectation = expectation.having(
@@ -32,20 +35,33 @@
);
}
- 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 @@
"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 @@
version: ">=1.2.3 <3.4.5"
''',
sources,
+ containingDescription: RootDescription('.'),
);
var foo = pubspec.dependencies['foo']!;
@@ -90,6 +112,7 @@
version: ">=1.2.3 <0.0.0"
''',
sources,
+ containingDescription: RootDescription('.'),
);
var foo = pubspec.dependencies['foo']!;
@@ -103,6 +126,7 @@
dependencies:
''',
sources,
+ containingDescription: RootDescription('.'),
);
expect(pubspec.dependencies, isEmpty);
@@ -119,6 +143,7 @@
version: ">=1.2.3 <3.4.5"
''',
sources,
+ containingDescription: RootDescription('.'),
);
var foo = pubspec.devDependencies['foo']!;
@@ -134,6 +159,7 @@
dev_dependencies:
''',
sources,
+ containingDescription: RootDescription('.'),
);
expect(pubspec.devDependencies, isEmpty);
@@ -150,6 +176,7 @@
version: ">=1.2.3 <3.4.5"
''',
sources,
+ containingDescription: RootDescription('.'),
);
var foo = pubspec.dependencyOverrides['foo']!;
@@ -165,6 +192,7 @@
dependency_overrides:
''',
sources,
+ containingDescription: RootDescription('.'),
);
expect(pubspec.dependencyOverrides, isEmpty);
@@ -178,6 +206,7 @@
unknown: blah
''',
sources,
+ containingDescription: RootDescription('.'),
);
var foo = pubspec.dependencies['foo']!;
@@ -193,6 +222,7 @@
version: 1.2.3
''',
sources,
+ containingDescription: RootDescription('.'),
);
var foo = pubspec.dependencies['foo']!;
@@ -285,24 +315,28 @@
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 @@
workspace: ['a', 'b', 'c']
''',
sources,
+ containingDescription: RootDescription('.'),
).workspace,
['a', 'b', 'c'],
);
@@ -335,6 +370,7 @@
resolution: workspace
''',
sources,
+ containingDescription: RootDescription('.'),
).resolution,
Resolution.workspace,
);
@@ -359,6 +395,7 @@
resolution: workspace
''',
sources,
+ containingDescription: RootDescription('.'),
).name,
'foo',
);
@@ -416,6 +453,7 @@
# 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 @@
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 @@
name: bar
''',
sources,
+ containingDescription: RootDescription('.'),
);
var foo = pubspec.dependencies['foo']!;
@@ -474,6 +516,7 @@
url: https://example.org/pub/
''',
sources,
+ containingDescription: RootDescription('.'),
);
var foo = pubspec.dependencies['foo']!;
@@ -501,6 +544,7 @@
hosted: https://example.org/pub/
''',
sources,
+ containingDescription: RootDescription('.'),
);
var foo = pubspec.dependencies['foo']!;
@@ -528,6 +572,7 @@
hosted: bar
''',
sources,
+ containingDescription: RootDescription('.'),
);
var foo = pubspec.dependencies['foo']!;
@@ -557,6 +602,7 @@
hosted: https://example.org/pub/
''',
sources,
+ containingDescription: RootDescription('.'),
);
expect(
@@ -577,6 +623,7 @@
foo:
''',
sources,
+ containingDescription: RootDescription('.'),
);
var foo = pubspec.dependencies['foo']!;
@@ -596,16 +643,18 @@
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 @@
hosted: http://pub.example.org
''',
(pubspec) => pubspec.dependencies,
- 'Using `hosted: <url>` is only supported with a minimum SDK constraint of 2.15.',
+ expectedContains:
+ 'Using `hosted: <url>` is only supported with a minimum SDK constraint of 2.15.',
);
},
);
@@ -680,7 +730,11 @@
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 @@
});
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 @@
sdk: ">1.0.0"
''',
sources,
+ containingDescription: RootDescription('.'),
);
expect(
pubspec.dartSdkConstraint.effectiveConstraint,
@@ -726,6 +785,7 @@
sdk: ">3.0.0"
''',
sources,
+ containingDescription: RootDescription('.'),
);
expect(
pubspec.sdkConstraints,
@@ -754,6 +814,7 @@
fuchsia: ^5.6.7
''',
sources,
+ containingDescription: RootDescription('.'),
);
expect(
pubspec.sdkConstraints,
@@ -814,7 +875,11 @@
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 @@
publish_to: http://example.com
''',
sources,
+ containingDescription: RootDescription('.'),
);
expect(pubspec.publishTo, equals('http://example.com'));
});
@@ -841,6 +907,7 @@
publish_to: none
''',
sources,
+ containingDescription: RootDescription('.'),
);
expect(pubspec.publishTo, equals('none'));
});
@@ -862,7 +929,11 @@
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 @@
abcDEF-123_: "abc DEF-123._"
''',
sources,
+ containingDescription: RootDescription('.'),
);
expect(pubspec.executables['abcDEF-123_'], equals('abc DEF-123._'));
});
@@ -926,6 +998,7 @@
command:
''',
sources,
+ containingDescription: RootDescription('.'),
);
expect(pubspec.executables['command'], equals('command'));
});
@@ -944,6 +1017,7 @@
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 baddd53..bf3a4e0 100644
--- a/test/version_solver_test.dart
+++ b/test/version_solver_test.dart
@@ -9,6 +9,7 @@
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 @@
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) {