Resolve workspace root and workPackage when invoking pub from any sub-directory (#4186)
diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart index 9d74d3e..88f4bf5 100644 --- a/lib/src/command/add.dart +++ b/lib/src/command/add.dart
@@ -256,8 +256,7 @@ } String? overridesFileContents; - final overridesPath = - p.join(entrypoint.workspaceRoot.dir, Pubspec.pubspecOverridesFilename); + final overridesPath = entrypoint.workspaceRoot.pubspecOverridesPath; try { overridesFileContents = readTextFile(overridesPath); } on IOException { @@ -268,7 +267,7 @@ /// gets a report on the other packages that might change version due /// to this new dependency. await entrypoint - .withPubspec( + .withWorkPubspec( Pubspec.parse( newPubspecText, cache.sources,
diff --git a/lib/src/command/remove.dart b/lib/src/command/remove.dart index 152c1df..11eea6b 100644 --- a/lib/src/command/remove.dart +++ b/lib/src/command/remove.dart
@@ -93,7 +93,7 @@ final rootPubspec = entrypoint.workspaceRoot.pubspec; final newPubspec = _removePackagesFromPubspec(rootPubspec, targets); - await entrypoint.withPubspec(newPubspec).acquireDependencies( + await entrypoint.withWorkPubspec(newPubspec).acquireDependencies( SolveType.get, precompile: !isDryRun && argResults.flag('precompile'), dryRun: isDryRun,
diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart index 91b0e26..1832187 100644 --- a/lib/src/command/upgrade.dart +++ b/lib/src/command/upgrade.dart
@@ -364,7 +364,7 @@ } await entrypoint - .withPubspec(_updatedPubspec(newPubspecText, entrypoint)) + .withWorkPubspec(_updatedPubspec(newPubspecText, entrypoint)) .acquireDependencies( solveType, dryRun: _dryRun,
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart index e20ef20..45b7cfd 100644 --- a/lib/src/entrypoint.dart +++ b/lib/src/entrypoint.dart
@@ -33,6 +33,7 @@ import 'solver/report.dart'; import 'solver/solve_suggestions.dart'; import 'source/cached.dart'; +import 'source/root.dart'; import 'source/unknown.dart'; import 'system_cache.dart'; import 'utils.dart'; @@ -58,25 +59,112 @@ /// /// [workspaceRoot] will be the package in the nearest parent directory that /// has `resolution: null` - // TODO(https://github.com/dart-lang/pub/issues/4127): make this actually - // true. final String workingDir; - Package? _workspaceRoot; + /// Finds the [workspaceRoot] and [workPackage] based on [workingDir]. + /// + /// Works by iterating through the parent directories from [workingDir]. + /// + /// [workPackage] is the package of first dir we find with a `pubspec.yaml` + /// file. + /// + /// [workspaceRoot] is the package of the first dir we find with a + /// `pubspec.yaml` that does not have `resolution: workspace`. + /// + /// [workPackage] and [workspaceRoot] can be the same. And will always be the + /// same when no `workspace` is involved. + /// = + /// If [workingDir] doesn't exist, [fail]. + /// + /// If no `pubspec.yaml` is found without `resolution: workspace` we [fail]. + static ({Package root, Package work}) _loadWorkspace( + String workingDir, + SystemCache cache, + ) { + if (!dirExists(workingDir)) { + fail('The directory `$workingDir` does not exist.'); + } + // Keep track of all the pubspecs met when walking up the file system. + // The first of these is the workingPackage. + final pubspecsMet = <String, Pubspec>{}; + for (final dir in parentDirs(workingDir)) { + final Pubspec pubspec; + + try { + pubspec = Pubspec.load( + dir, + cache.sources, + containingDescription: RootDescription(dir), + allowOverridesFile: true, + ); + } on FileException { + continue; + } + pubspecsMet[p.canonicalize(dir)] = pubspec; + final Package root; + if (pubspec.resolution == Resolution.none) { + root = Package.load( + dir, + cache.sources, + loadPubspec: ( + path, { + expectedName, + required withPubspecOverrides, + }) => + pubspecsMet[p.canonicalize(path)] ?? + Pubspec.load( + path, + cache.sources, + expectedName: expectedName, + allowOverridesFile: withPubspecOverrides, + containingDescription: RootDescription(path), + ), + withPubspecOverrides: true, + ); + for (final package in root.transitiveWorkspace) { + if (identical(pubspecsMet.entries.first.value, package.pubspec)) { + return (root: root, work: package); + } + } + assert(false); + } + } + if (pubspecsMet.isEmpty) { + throw FileException( + 'Found no `pubspec.yaml` file in `${p.normalize(p.absolute(workingDir))}` or parent directories', + p.join(workingDir, 'pubspec.yaml'), + ); + } else { + final firstEntry = pubspecsMet.entries.first; + throw FileException( + ''' +Found a pubspec.yaml at ${firstEntry.key}. But it has resolution `${firstEntry.value.resolution.name}`. +But found no workspace root including it in parent directories. + +See $workspacesDocUrl for more information.''', + p.join(workingDir, 'pubspec.yaml'), + ); + } + } + + /// Stores the result of [_loadWorkspace]. + /// Only access via [workspaceRoot], [workPackage] and [canFindWorkspaceRoot]. + ({Package root, Package work})? _packages; + + /// Only access via [workspaceRoot], [workPackage] and [canFindWorkspaceRoot]. + ({Package root, Package work}) get _getPackages => + _packages ??= _loadWorkspace(workingDir, cache); /// The root package this entrypoint is associated with. /// /// For a global package, this is the activated package. - Package get workspaceRoot => _workspaceRoot ??= Package.load( - null, - workingDir, - cache.sources, - withPubspecOverrides: true, - ); + Package get workspaceRoot => _getPackages.root; + /// True if we can find a `pubspec.yaml` to resolve in [workingDir] or any + /// parent directory. bool get canFindWorkspaceRoot { try { - workspaceRoot; + _getPackages; return true; } on FileException { return false; @@ -87,8 +175,6 @@ /// /// It will be the package in the nearest parent directory to `workingDir`. /// Example: if a workspace looks like this: - // TODO(https://github.com/dart-lang/pub/issues/4127): make this actually - // true. /// /// foo/ pubspec.yaml # contains `workspace: [- 'bar'] bar/ pubspec.yaml # /// contains `resolution: workspace`. @@ -98,7 +184,7 @@ /// /// Running `pub add` in `foo` will have foo as workPackage, and add /// dependencies to `foo/pubspec.yaml`. - Package get workPackage => workspaceRoot; + Package get workPackage => _getPackages.work; /// The system-wide cache which caches packages that need to be fetched over /// the network. @@ -193,9 +279,9 @@ var packages = { for (var packageEntry in packageConfig.nonInjectedPackages) packageEntry.name: Package.load( - packageEntry.name, packageEntry.resolvedRootDir(packageConfigPath), cache.sources, + expectedName: packageEntry.name, ), }; packages[workspaceRoot.name] = workspaceRoot; @@ -229,46 +315,59 @@ this._example, this._packageGraph, this.cache, - this._workspaceRoot, + this._packages, this.isCachedGlobal, ); - /// An entrypoint representing a package at [rootDir]. + /// An entrypoint for the workspace containing [workingDir]/ /// /// If [checkInCache] is `true` (the default) an error will be thrown if /// [rootDir] is located inside [cache.rootDir]. + Entrypoint( this.workingDir, this.cache, { - ({Pubspec pubspec, List<Package> workspacePackages})? preloaded, bool checkInCache = true, - }) : _workspaceRoot = preloaded == null - ? null - : Package( - preloaded.pubspec, - workingDir, - preloaded.workspacePackages, - ), - isCachedGlobal = false { + }) : isCachedGlobal = false { if (checkInCache && p.isWithin(cache.rootDir, workingDir)) { fail('Cannot operate on packages inside the cache.'); } } /// Creates an entrypoint at the same location, that will use [pubspec] for - /// resolution. - Entrypoint withPubspec(Pubspec pubspec) { + /// resolution of the [workPackage]. + Entrypoint withWorkPubspec(Pubspec pubspec) { + final existingPubspecs = <String, Pubspec>{}; + // First extract all pubspecs from the workspace. + for (final package in workspaceRoot.transitiveWorkspace) { + existingPubspecs[package.dir] = package.pubspec; + } + // Then override the one of the workPackage. + existingPubspecs[p.canonicalize(workPackage.dir)] = pubspec; + final newWorkspaceRoot = Package.load( + workspaceRoot.dir, + cache.sources, + loadPubspec: ( + dir, { + expectedName, + required withPubspecOverrides, + }) => + existingPubspecs[p.canonicalize(dir)] ?? + Pubspec.load( + dir, + cache.sources, + containingDescription: RootDescription(dir), + ), + ); + final newWorkPackage = newWorkspaceRoot.transitiveWorkspace + .firstWhere((package) => package.dir == workPackage.dir); return Entrypoint._( workingDir, _lockFile, _example, _packageGraph, cache, - Package( - pubspec, - workingDir, - workspaceRoot.workspaceChildren, - ), + (root: newWorkspaceRoot, work: newWorkPackage), isCachedGlobal, ); } @@ -276,11 +375,12 @@ /// Creates an entrypoint given package and lockfile objects. /// If a SolveResult is already created it can be passed as an optimization. Entrypoint.global( - Package this._workspaceRoot, + Package package, this._lockFile, this.cache, { SolveResult? solveResult, - }) : workingDir = _workspaceRoot.dir, + }) : _packages = (root: package, work: package), + workingDir = package.dir, isCachedGlobal = true { if (solveResult != null) { _packageGraph = @@ -410,7 +510,7 @@ }) async { workspaceRoot; // This will throw early if pubspec.yaml could not be found. summaryOnly = summaryOnly || _summaryOnlyEnvironment; - final suffix = workspaceRoot.dir == '.' ? '' : ' in ${workspaceRoot.dir}'; + final suffix = workspaceRoot.dir == '.' ? '' : ' in `${workspaceRoot.dir}`'; if (enforceLockfile && !fileExists(lockFilePath)) { throw ApplicationException(''' @@ -978,7 +1078,7 @@ } var touchedLockFile = false; late final lockFile = _loadLockFile(lockFilePath, cache); - late final root = Package.load(null, dir, cache.sources); + late final root = Package.load(dir, cache.sources); if (!lockfileNewerThanPubspecs) { if (isLockFileUpToDate(lockFile, root)) {
diff --git a/lib/src/io.dart b/lib/src/io.dart index af86eae..fabafa3 100644 --- a/lib/src/io.dart +++ b/lib/src/io.dart
@@ -1229,3 +1229,32 @@ RegExp(r'^[a-zA-Z0-9-_=@.^]+$').stringMatch(x) == null ? "'${x.replaceAll(r'\', r'\\').replaceAll("'", r"'\''")}'" : x; + +/// Returns all parent directories of [path], starting from [path] to the +/// filesystem root. +/// +/// If [path] is relative the directories will also be. +/// +/// If [from] is passed, directories are made relative to that. +/// +/// Examples: +/// parentDirs('/a/b/c') => ('/a/b/c', '/a/b', '/a', '/') +/// parentDirs('./d/e', from: '/a/b/c') => ('./d/e', './d', '.', '..', '../..', '../../..') +Iterable<String> parentDirs(String path, {String? from}) sync* { + var relative = false; + var d = path; + while (true) { + if (relative) { + yield p.relative(d, from: from); + } else { + yield d; + } + if (!p.isWithin(from ?? p.current, d)) { + d = p.normalize(p.join(from ?? p.current, d)); + relative = true; + } + final parent = p.dirname(d); + if (parent == d) break; + d = parent; + } +}
diff --git a/lib/src/package.dart b/lib/src/package.dart index fa99394..5685679 100644 --- a/lib/src/package.dart +++ b/lib/src/package.dart
@@ -125,28 +125,62 @@ /// Loads the package whose root directory is [packageDir]. /// + /// Will also load the workspace sub-packages of this package (recursively). + /// /// [name] is the expected name of that package (e.g. the name given in the /// dependency), or `null` if the package being loaded is the entrypoint /// package. /// /// `pubspec_overrides.yaml` is only loaded if [withPubspecOverrides] is /// `true`. + /// + /// [loadPubspec] if given will be used to obtain a pubspec from a path. Also + /// for the workspace children. + /// + /// This mechanism can be used to avoid loading pubspecs twice. It can also be + /// used to override a pubspec in memory for trying out an alternative + /// resolution. factory Package.load( - String? name, String dir, SourceRegistry sources, { bool withPubspecOverrides = false, + String? expectedName, + Pubspec Function( + String path, { + String? expectedName, + required bool withPubspecOverrides, + })? loadPubspec, }) { - final pubspec = Pubspec.load( + loadPubspec ??= + (path, {expectedName, required withPubspecOverrides}) => Pubspec.load( + path, + sources, + containingDescription: RootDescription(path), + ); + final pubspec = loadPubspec( dir, - sources, - expectedName: name, - allowOverridesFile: withPubspecOverrides, - containingDescription: RootDescription(dir), + withPubspecOverrides: withPubspecOverrides, + expectedName: expectedName, ); final workspacePackages = pubspec.workspace - .map((e) => Package.load(null, p.join(dir, e), sources)) + .map( + (e) => Package.load( + p.join(dir, e), + sources, + loadPubspec: loadPubspec, + withPubspecOverrides: withPubspecOverrides, + ), + ) .toList(); + for (final package in workspacePackages) { + if (package.pubspec.resolution != Resolution.workspace) { + fail(''' +${package.pubspecPath} is inluded in the workspace from ${p.join(dir, 'pubspec.yaml')}, but does not have `resolution: workspace`. + +See $workspacesDocUrl for more information. +'''); + } + } return Package(pubspec, dir, workspacePackages); }
diff --git a/lib/src/solver/report.dart b/lib/src/solver/report.dart index da82329..6154d32 100644 --- a/lib/src/solver/report.dart +++ b/lib/src/solver/report.dart
@@ -153,7 +153,7 @@ final dir = _location; if (dir != null) { if (dir != '.') { - suffix = ' in $dir'; + suffix = ' in `$dir`'; } }
diff --git a/lib/src/source/git.dart b/lib/src/source/git.dart index 99bf59b..7f863aa 100644 --- a/lib/src/source/git.dart +++ b/lib/src/source/git.dart
@@ -443,7 +443,7 @@ var packageDir = p.join(revisionCachePath, relative); try { - return Package.load(null, packageDir, cache.sources); + return Package.load(packageDir, cache.sources); } catch (error, stackTrace) { log.error('Failed to load package', error, stackTrace); var name = p.basename(revisionCachePath).split('-').first;
diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart index 7a2c9de..b51baa9 100644 --- a/lib/src/source/hosted.dart +++ b/lib/src/source/hosted.dart
@@ -1277,7 +1277,7 @@ var packages = <Package>[]; for (var entry in listDir(serverDir)) { try { - packages.add(Package.load(null, entry, cache.sources)); + packages.add(Package.load(entry, cache.sources)); } catch (error, stackTrace) { log.error('Failed to load package', error, stackTrace); final id = _idForBasename( @@ -1384,7 +1384,7 @@ .where(_looksLikePackageDir) .map((entry) { try { - return Package.load(null, entry, cache.sources); + return Package.load(entry, cache.sources); } catch (error, stackTrace) { log.fine('Failed to load package from $entry:\n' '$error\n'
diff --git a/lib/src/system_cache.dart b/lib/src/system_cache.dart index 222c375..e20a60b 100644 --- a/lib/src/system_cache.dart +++ b/lib/src/system_cache.dart
@@ -111,16 +111,16 @@ /// /// Throws an [ArgumentError] if [id] has an invalid source. Package load(PackageId id) { - return Package.load(id.name, getDirectory(id), sources); + return Package.load(getDirectory(id), sources, expectedName: id.name); } Package loadCached(PackageId id) { final source = id.description.description.source; if (source is CachedSource) { return Package.load( - id.name, source.getDirectoryInCache(id, this), sources, + expectedName: id.name, ); } else { throw ArgumentError('Call only on Cached ids.');
diff --git a/lib/src/utils.dart b/lib/src/utils.dart index cb971ff..3093fce 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart
@@ -104,6 +104,8 @@ 'yield', }; +const workspacesDocUrl = 'https://dart.dev/go/pub-workspaces'; + /// An cryptographically secure instance of [math.Random]. final random = math.Random.secure();
diff --git a/test/add/git/git_test.dart b/test/add/git/git_test.dart index 833dd0f..19938e3 100644 --- a/test/add/git/git_test.dart +++ b/test/add/git/git_test.dart
@@ -47,7 +47,7 @@ await pubAdd( args: ['--directory', appPath, 'foo', '--git-url', 'foo.git'], workingDirectory: d.sandbox, - output: contains('Changed 1 dependency in myapp!'), + output: contains('Changed 1 dependency in `myapp`!'), ); await d.dir(cachePath, [ @@ -228,7 +228,7 @@ 'foo:{"git": {"url":"foo.git", "path":"subdir"}}', ], workingDirectory: d.sandbox, - output: contains('Changed 1 dependency in myapp!'), + output: contains('Changed 1 dependency in `myapp`!'), ); await d.appDir(
diff --git a/test/add/path/relative_path_test.dart b/test/add/path/relative_path_test.dart index 5a3dcaa..32ae0d6 100644 --- a/test/add/path/relative_path_test.dart +++ b/test/add/path/relative_path_test.dart
@@ -59,7 +59,7 @@ await pubAdd( args: ['--directory', appPath, 'foo', '--path', 'foo'], workingDirectory: d.sandbox, - output: contains('Changed 1 dependency in myapp!'), + output: contains('Changed 1 dependency in `myapp`!'), ); await d.appPackageConfigFile([ @@ -180,7 +180,7 @@ 'bar:{"path":"bar"}', ], workingDirectory: d.sandbox, - output: contains('Changed 2 dependencies in myapp!'), + output: contains('Changed 2 dependencies in `myapp`!'), ); await d.appPackageConfigFile([
diff --git a/test/ascii_tree_test.dart b/test/ascii_tree_test.dart index d0a921b..b406f72 100644 --- a/test/ascii_tree_test.dart +++ b/test/ascii_tree_test.dart
@@ -61,11 +61,9 @@ file('path.dart', bytes(100)), ]), ]).create(); - var files = Package.load( - null, - path(appPath), - (name) => throw UnimplementedError(), - ).listFiles(); + var files = + Package.load(path(appPath), (name) => throw UnimplementedError()) + .listFiles(); ctx.expectNextSection( tree.fromFiles(files, baseDir: path(appPath), showFileSizes: true), );
diff --git a/test/descriptor.dart b/test/descriptor.dart index b0e68c2..8a8f783 100644 --- a/test/descriptor.dart +++ b/test/descriptor.dart
@@ -111,14 +111,25 @@ String version, { Map<String, Object?>? deps, Map<String, Object?>? devDeps, + String? resolution, String? sdk, Map<String, Object?>? extras, + bool resolutionWorkspace = false, }) { var map = packageMap(name, version, deps, devDeps); + if (resolutionWorkspace && sdk == null) { + sdk = '3.7.0'; + } if (sdk != null) { map['environment'] = {'sdk': sdk}; } - return pubspec({...map, ...extras ?? {}}); + return pubspec( + { + ...map, + if (resolutionWorkspace) 'resolution': 'workspace', + ...extras ?? {}, + }, + ); } /// Describes a file named `pubspec_overrides.yaml` by default, with the given
diff --git a/test/get/enforce_lockfile_test.dart b/test/get/enforce_lockfile_test.dart index 9cafa8e..75c3ee1 100644 --- a/test/get/enforce_lockfile_test.dart +++ b/test/get/enforce_lockfile_test.dart
@@ -84,14 +84,14 @@ args: ['--enforce-lockfile', '--example'], output: allOf( contains('Got dependencies!'), - contains('Resolving dependencies in $example...'), + contains('Resolving dependencies in `$example`...'), ), error: allOf( contains( - 'Unable to satisfy `$examplePubspec` using `$examplePubspecLock` in $example.', + 'Unable to satisfy `$examplePubspec` using `$examplePubspecLock` in `$example`.', ), contains( - 'To update `$examplePubspecLock` run `dart pub get` in $example without\n' + 'To update `$examplePubspecLock` run `dart pub get` in `$example` without\n' '`--enforce-lockfile`.'), ), exitCode: DATA,
diff --git a/test/get/gets_in_example_folder_test.dart b/test/get/gets_in_example_folder_test.dart index 11288d0..8e990da 100644 --- a/test/get/gets_in_example_folder_test.dart +++ b/test/get/gets_in_example_folder_test.dart
@@ -47,16 +47,16 @@ Resolving dependencies... Downloading packages... Got dependencies! -Resolving dependencies in $dotExample... +Resolving dependencies in `$dotExample`... Downloading packages... -Got dependencies in $dotExample.''' +Got dependencies in `$dotExample`.''' : ''' Resolving dependencies... Downloading packages... No dependencies changed. -Resolving dependencies in $dotExample... +Resolving dependencies in `$dotExample`... Downloading packages... -Got dependencies in $dotExample.''', +Got dependencies in `$dotExample`.''', ); expect(lockFile.existsSync(), true); expect(exampleLockFile.existsSync(), true);
diff --git a/test/get/path/relative_path_test.dart b/test/get/path/relative_path_test.dart index 67e9749..4b379f9 100644 --- a/test/get/path/relative_path_test.dart +++ b/test/get/path/relative_path_test.dart
@@ -92,7 +92,7 @@ await pubGet( args: ['--directory', appPath], workingDirectory: d.sandbox, - output: contains('Changed 2 dependencies in myapp!'), + output: contains('Changed 2 dependencies in `myapp`!'), ); await d.appPackageConfigFile([
diff --git a/test/global/activate/installs_dependencies_for_path_test.dart b/test/global/activate/installs_dependencies_for_path_test.dart index ff85808..7c9f11e 100644 --- a/test/global/activate/installs_dependencies_for_path_test.dart +++ b/test/global/activate/installs_dependencies_for_path_test.dart
@@ -19,7 +19,7 @@ ]).create(); var pub = await startPub(args: ['global', 'activate', '-spath', '../foo']); - expect(pub.stdout, emitsThrough('Resolving dependencies in ../foo...')); + expect(pub.stdout, emitsThrough('Resolving dependencies in `../foo`...')); expect(pub.stdout, emitsThrough(startsWith('Activated foo 0.0.0 at path'))); await pub.shouldExit();
diff --git a/test/global/run/missing_path_package_test.dart b/test/global/run/missing_path_package_test.dart index dc98583..dce42da 100644 --- a/test/global/run/missing_path_package_test.dart +++ b/test/global/run/missing_path_package_test.dart
@@ -3,7 +3,6 @@ // 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' as exit_codes; import 'package:pub/src/io.dart'; import 'package:test/test.dart'; @@ -22,11 +21,10 @@ deleteEntry(p.join(d.sandbox, 'foo')); var pub = await pubRun(global: true, args: ['foo']); - var path = canonicalize(p.join(d.sandbox, 'foo')); expect( pub.stderr, - emits('Could not find a file named "pubspec.yaml" in "$path".'), + emits('The directory `${d.path('foo')}` does not exist.'), ); - await pub.shouldExit(exit_codes.NO_INPUT); + await pub.shouldExit(1); }); }
diff --git a/test/pub_get_and_upgrade_test.dart b/test/pub_get_and_upgrade_test.dart index 5000cac..61dfa45 100644 --- a/test/pub_get_and_upgrade_test.dart +++ b/test/pub_get_and_upgrade_test.dart
@@ -17,8 +17,9 @@ await pubCommand( command, - error: RegExp(r'Could not find a file named "pubspec.yaml" ' - r'in "[^\n]*"\.'), + error: contains( + 'Found no `pubspec.yaml` file in `${d.path(appPath)}` or parent directories', + ), exitCode: exit_codes.NO_INPUT, ); });
diff --git a/test/testdata/goldens/directory_option_test/commands taking a --directory~-C parameter work.txt b/test/testdata/goldens/directory_option_test/commands taking a --directory~-C parameter work.txt index c448092..6a8719e 100644 --- a/test/testdata/goldens/directory_option_test/commands taking a --directory~-C parameter work.txt +++ b/test/testdata/goldens/directory_option_test/commands taking a --directory~-C parameter work.txt
@@ -2,74 +2,74 @@ ## Section 0 $ pub add --directory=myapp foo -Resolving dependencies in myapp... +Resolving dependencies in `myapp`... Downloading packages... + foo 1.0.0 -Changed 1 dependency in myapp! -Resolving dependencies in myapp/example... +Changed 1 dependency in `myapp`! +Resolving dependencies in `myapp/example`... Downloading packages... -Got dependencies in myapp/example. +Got dependencies in `myapp/example`. -------------------------------- END OF OUTPUT --------------------------------- ## Section 1 $ pub -C myapp add bar -Resolving dependencies in myapp... +Resolving dependencies in `myapp`... Downloading packages... + bar 1.2.3 -Changed 1 dependency in myapp! -Resolving dependencies in myapp/example... +Changed 1 dependency in `myapp`! +Resolving dependencies in `myapp/example`... Downloading packages... -Got dependencies in myapp/example. +Got dependencies in `myapp/example`. -------------------------------- END OF OUTPUT --------------------------------- ## Section 2 $ pub -C 'myapp/example' get --directory=myapp bar -Resolving dependencies in myapp... +Resolving dependencies in `myapp`... Downloading packages... -Got dependencies in myapp! -Resolving dependencies in myapp/example... +Got dependencies in `myapp`! +Resolving dependencies in `myapp/example`... Downloading packages... -Got dependencies in myapp/example. +Got dependencies in `myapp/example`. -------------------------------- END OF OUTPUT --------------------------------- ## Section 3 $ pub remove bar -C myapp -Resolving dependencies in myapp... +Resolving dependencies in `myapp`... Downloading packages... These packages are no longer being depended on: - bar 1.2.3 -Changed 1 dependency in myapp! -Resolving dependencies in myapp/example... +Changed 1 dependency in `myapp`! +Resolving dependencies in `myapp/example`... Downloading packages... -Got dependencies in myapp/example. +Got dependencies in `myapp/example`. -------------------------------- END OF OUTPUT --------------------------------- ## Section 4 $ pub get bar -C myapp -Resolving dependencies in myapp... +Resolving dependencies in `myapp`... Downloading packages... -Got dependencies in myapp! -Resolving dependencies in myapp/example... +Got dependencies in `myapp`! +Resolving dependencies in `myapp/example`... Downloading packages... -Got dependencies in myapp/example. +Got dependencies in `myapp/example`. -------------------------------- END OF OUTPUT --------------------------------- ## Section 5 $ pub get bar -C 'myapp/example' -Resolving dependencies in myapp/example... +Resolving dependencies in `myapp/example`... Downloading packages... -Got dependencies in myapp/example! +Got dependencies in `myapp/example`! -------------------------------- END OF OUTPUT --------------------------------- ## Section 6 $ pub get bar -C 'myapp/example2' -Resolving dependencies in myapp/example2... +Resolving dependencies in `myapp/example2`... [STDERR] Error on line 1, column 9 of myapp/pubspec.yaml: "name" field doesn't match expected name "myapp". [STDERR] ╷ [STDERR] 1 │ {"name":"test_pkg","version":"1.0.0","homepage":"https://pub.dev","description":"A package, I guess.","environment":{"sdk":">=3.1.2 <=3.2.0"}, dependencies: { foo: ^1.0.0}} @@ -81,38 +81,38 @@ ## Section 7 $ pub get bar -C 'myapp/broken_dir' -[STDERR] Could not find a file named "pubspec.yaml" in "$SANDBOX/myapp/broken_dir". -[EXIT CODE] 66 +[STDERR] The directory `myapp/broken_dir` does not exist. +[EXIT CODE] 1 -------------------------------- END OF OUTPUT --------------------------------- ## Section 8 $ pub downgrade -C myapp -Resolving dependencies in myapp... +Resolving dependencies in `myapp`... Downloading packages... -No dependencies changed in myapp. -Resolving dependencies in myapp/example... +No dependencies changed in `myapp`. +Resolving dependencies in `myapp/example`... Downloading packages... -Got dependencies in myapp/example. +Got dependencies in `myapp/example`. -------------------------------- END OF OUTPUT --------------------------------- ## Section 9 $ pub upgrade bar -C myapp -Resolving dependencies in myapp... +Resolving dependencies in `myapp`... Downloading packages... -No dependencies changed in myapp. -Resolving dependencies in myapp/example... +No dependencies changed in `myapp`. +Resolving dependencies in `myapp/example`... Downloading packages... -Got dependencies in myapp/example. +Got dependencies in `myapp/example`. -------------------------------- END OF OUTPUT --------------------------------- ## Section 10 $ pub run -C myapp 'bin/app.dart' -Resolving dependencies in myapp... +Resolving dependencies in `myapp`... Downloading packages... -Got dependencies in myapp. +Got dependencies in `myapp`. Building package executable... Built test_pkg:app. Hi @@ -121,9 +121,9 @@ ## Section 11 $ pub publish -C myapp --dry-run -Resolving dependencies in myapp... +Resolving dependencies in `myapp`... Downloading packages... -Got dependencies in myapp! +Got dependencies in `myapp`! Publishing test_pkg 1.0.0 to http://localhost:$PORT: ├── CHANGELOG.md (<1 KB) ├── LICENSE (<1 KB) @@ -158,9 +158,9 @@ ## Section 13 $ pub deps -C myapp -Resolving dependencies in myapp... +Resolving dependencies in `myapp`... Downloading packages... -Got dependencies in myapp. +Got dependencies in `myapp`. Dart SDK 3.1.2+3 test_pkg 1.0.0 └── foo 1.0.0
diff --git a/test/testdata/goldens/outdated/outdated_test/no pubspec.txt b/test/testdata/goldens/outdated/outdated_test/no pubspec.txt index ba8858a..c8def95 100644 --- a/test/testdata/goldens/outdated/outdated_test/no pubspec.txt +++ b/test/testdata/goldens/outdated/outdated_test/no pubspec.txt
@@ -2,6 +2,6 @@ ## Section 0 $ pub outdated -[STDERR] Could not find a file named "pubspec.yaml" in "$SANDBOX/myapp". +[STDERR] Found no `pubspec.yaml` file in `$SANDBOX/myapp` or parent directories [EXIT CODE] 66
diff --git a/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt b/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt index d53310e..b5ea61a 100644 --- a/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt +++ b/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt
@@ -9,19 +9,19 @@ Changed 1 constraint in pubspec.yaml: bar: ^1.0.0 -> ^2.0.0 -Resolving dependencies in ./example... +Resolving dependencies in `./example`... Downloading packages... -Got dependencies in ./example. +Got dependencies in `./example`. [STDERR] Running `upgrade --major-versions` only in `.`. Run `dart pub upgrade --major-versions --directory example/` separately. -------------------------------- END OF OUTPUT --------------------------------- ## Section 1 $ pub upgrade --major-versions --directory example -Resolving dependencies in example... +Resolving dependencies in `example`... Downloading packages... > foo 2.0.0 (was 1.0.0) -Changed 1 dependency in example! +Changed 1 dependency in `example`! Changed 1 constraint in pubspec.yaml: foo: ^1.0.0 -> ^2.0.0
diff --git a/test/unpack_test.dart b/test/unpack_test.dart index cdf1f6e..ebf83ce 100644 --- a/test/unpack_test.dart +++ b/test/unpack_test.dart
@@ -72,7 +72,7 @@ output: allOf( contains(''' Downloading foo 1.2.3 to `.${s}foo-1.2.3`... -Resolving dependencies in .${s}foo-1.2.3... +Resolving dependencies in `.${s}foo-1.2.3`... '''), contains('To explore type: cd .${s}foo-1.2.3'), contains( @@ -97,7 +97,7 @@ output: allOf( contains(''' Downloading foo 1.2.3-pre to `../foo-1.2.3-pre`... -Resolving dependencies in ../foo-1.2.3-pre... +Resolving dependencies in `../foo-1.2.3-pre`... '''), contains('To explore type: cd ../foo-1.2.3-pre'), ),
diff --git a/test/workspace_test.dart b/test/workspace_test.dart index 60204fa..d603f5d 100644 --- a/test/workspace_test.dart +++ b/test/workspace_test.dart
@@ -26,7 +26,12 @@ ), dir('pkgs', [ dir('a', [ - libPubspec('a', '1.1.1', devDeps: {'dev_dep': '^1.0.0'}), + libPubspec( + 'a', + '1.1.1', + devDeps: {'dev_dep': '^1.0.0'}, + resolutionWorkspace: true, + ), ]), ]), ]).create(); @@ -62,7 +67,12 @@ ), dir('pkgs', [ dir('a', [ - libPubspec('a', '1.1.1', deps: {'b': '^2.0.0'}), + libPubspec( + 'a', + '1.1.1', + deps: {'b': '^2.0.0'}, + resolutionWorkspace: true, + ), ]), dir('b', [ libPubspec( @@ -71,6 +81,7 @@ deps: { 'myapp': {'git': 'somewhere'}, }, + resolutionWorkspace: true, ), ]), ]), @@ -109,7 +120,7 @@ extras: { 'workspace': ['example'], }, - sdk: '^3.7.0', + resolutionWorkspace: true, ), dir('example', [ libPubspec( @@ -118,6 +129,7 @@ deps: { 'a': {'path': '..'}, }, + resolutionWorkspace: true, ), ]), ]), @@ -151,7 +163,12 @@ ), dir('pkgs', [ dir('a', [ - libPubspec('a', '1.1.1', deps: {'myapp': '^0.2.3'}), + libPubspec( + 'a', + '1.1.1', + deps: {'myapp': '^0.2.3'}, + resolutionWorkspace: true, + ), ]), ]), ]).create(); @@ -181,10 +198,65 @@ deps: { 'myapp': {'posted': 'https://abc'}, }, + resolutionWorkspace: true, ), ]), ]), ]).create(); await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}); }); + + test('Can resolve from any directory inside the workspace', () async { + await dir(appPath, [ + libPubspec( + 'myapp', + '1.2.3', + extras: { + 'workspace': ['pkgs/a'], + }, + sdk: '^3.7.0', + ), + dir('pkgs', [ + dir('a', [ + libPubspec( + 'a', + '1.1.1', + deps: { + 'myapp': {'posted': 'https://abc'}, + }, + resolutionWorkspace: true, + ), + ]), + ]), + ]).create(); + await pubGet( + environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, + workingDirectory: p.join(sandbox, appPath, 'pkgs'), + output: contains('Resolving dependencies in `..`...'), + ); + final s = p.separator; + await pubGet( + environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, + workingDirectory: p.join(sandbox, appPath, 'pkgs', 'a'), + output: contains('Resolving dependencies in `..$s..`...'), + ); + + await pubGet( + args: ['-C$appPath/pkgs'], + environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, + workingDirectory: sandbox, + output: contains('Resolving dependencies in `$appPath`...'), + ); + + await pubGet( + args: ['-C..'], + environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, + workingDirectory: p.join( + sandbox, + appPath, + 'pkgs', + ), + output: contains('Resolving dependencies in `..`...'), + ); + }); }